### Render with PagSize Example Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/types.md Example of rendering a PAG player with a specified size. ```kotlin val size = PagSize(width = 200, height = 200) val image = player.render(size) ``` -------------------------------- ### Example Usage of unsupportedPagPlatform Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/extensions.md Demonstrates how the unsupportedPagPlatform function is called within a platform-specific implementation to indicate that a feature is not yet supported. ```kotlin // In Pag.js.kt: actual fun createPlayer(): PagPlayer = unsupportedPagPlatform("JS") ``` -------------------------------- ### PAG Composition Loading and Rendering Example Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/types.md Demonstrates loading a PAG composition from bytes, accessing its properties, rendering it, and cleaning up resources. Ensure to call close() when done. ```kotlin val composition = Pag.load(bytes) println("Duration: ${composition.durationUs} µs") println("Size: ${composition.width}×${composition.height}") // Render at intrinsic size val image = player.render(composition.intrinsicSize) // Clean up composition.close() ``` -------------------------------- ### Get Platform Description Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Returns a human-readable identifier for the current platform. Example output: "macOS aarch64". ```kotlin fun platformDescription(): String ``` -------------------------------- ### Full pag-cmp Configuration Example Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/configuration.md Demonstrates how to configure and display a PAG animation using the PagView composable. This includes setting the animation path, size, playback controls, scaling mode, and caching options. ```kotlin import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.limuyang2.libpag.cmp.PagScaleMode import io.github.limuyang2.libpag.cmp.PagView @Composable fun ConfiguredPagAnimation() { PagView( path = "https://example.com/animation.pag", modifier = Modifier.size(200.dp, 200.dp), isPlaying = true, progress = null, // Follow automatic playback repeatCount = 2, // Play twice scaleMode = PagScaleMode.Zoom, cacheEnabled = true, videoEnabled = true, useDiskCache = false, // Disable disk cache on this instance ) } ``` -------------------------------- ### Basic PagView Usage Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/COMPLETION.txt A simple example of using PagView to display a PAG composition. Ensure the PagView composable is correctly placed within your Compose UI. ```kotlin PagView(modifier = Modifier.size(128.dp), path = "path/to/your.pag") ``` -------------------------------- ### Render PAG Composition Example Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/types.md Demonstrates how to load a PAG composition, create a player, set its progress, render a frame to an ImageBitmap, and then close both the player and composition to free resources. Ensure proper resource management by calling `close()` on both objects. ```kotlin import androidx.compose.ui.graphics.ImageBitmap import io.github.limuyang2.libpag.cmp.Pag import io.github.limuyang2.libpag.cmp.PagSize fun renderAnimationFrame(): ImageBitmap { val composition = Pag.load(bytes) val player = Pag.createPlayer() player.composition = composition player.progress = 0.5 // Render the midpoint val image = player.render(PagSize(width = 400, height = 400)) player.close() composition.close() return image } ``` -------------------------------- ### Custom Native Library Setup (JVM) Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/COMPLETION.txt Configures the JVM to use a custom native library path for PAG. Set the system property `JNI_LIB_PATH` before loading any PAG components. ```kotlin System.setProperty("JNI_LIB_PATH", "/path/to/custom/natives") // Now load PAG components, they will use the custom path ``` -------------------------------- ### Swift Package Manager Dependency Setup Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Configure the Swift Package Manager to fetch the libpag iOS library. Ensure the correct URL and version are specified. ```kotlin swiftPMDependencies { swiftPackage( url = url("https://github.com/libpag/pag-ios.git"), version = exact("4.5.70"), products = listOf(product("libpag")), ) } ``` -------------------------------- ### Render PagView to ImageBitmap (JVM) Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/COMPLETION.txt Example of rendering a PAG composition to an ImageBitmap on the JVM. This is useful for generating static images from animations. ```kotlin val pagFile = PagFile.load("path/to/your.pag") val imageBitmap = pagFile.toImageBitmap() // Use the imageBitmap as needed ``` -------------------------------- ### Load and Display PAG Animation from Bytes Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/README.md Use this composable to display a PAG animation directly from a ByteArray. Set `isPlaying` to true to start playback and `repeatCount` to 0 for infinite looping. ```kotlin import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.limuyang2.libpag.cmp.PagScaleMode import io.github.limuyang2.libpag.cmp.PagView @Composable fun AnimationScreen(animationBytes: ByteArray) { PagView( bytes = animationBytes, modifier = Modifier.size(200.dp), isPlaying = true, repeatCount = 0, // infinite scaleMode = PagScaleMode.LetterBox, ) } ``` -------------------------------- ### Get PagComposition Intrinsic Size Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/extensions.md Provides a convenient property to get the intrinsic size of a PagComposition as a PagSize object. This simplifies rendering by directly accessing the composition's width and height, and can be used as a default argument for size parameters. ```kotlin val PagComposition.intrinsicSize: PagSize get() = PagSize(width, height) ``` ```kotlin val composition = Pag.load(bytes) // Direct property access: val image1 = player.render(PagSize(composition.width, composition.height)) // Using intrinsicSize: val image2 = player.render(composition.intrinsicSize) // ← Cleaner // As default argument: fun renderFrame(composition: PagComposition, size: PagSize = composition.intrinsicSize): ImageBitmap { val player = Pag.createPlayer() player.composition = composition return player.render(size) } ``` -------------------------------- ### Run Desktop Demo Without Native Arguments Source: https://github.com/limuyang2/pag-cmp/blob/main/BUILD.md Gradle command to run the desktop application, which should automatically load bundled native libraries. ```shell ./gradlew :desktopApp:run ``` -------------------------------- ### Validation Pattern for PAG Parameters Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/COMPLETION.txt Example of validating input parameters before using them with PAG. This prevents runtime errors by ensuring data integrity. ```kotlin fun loadPagWithValidation(path: String?) { if (path.isNullOrBlank()) { throw IllegalArgumentException("PAG path cannot be empty") } // Proceed with loading PAG file PagFile.load(path) } ``` -------------------------------- ### updateImage Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Installs the pixel buffer into the Skia Bitmap and returns the associated ImageBitmap. This ImageBitmap is valid until the next updateImage() call or buffer reallocation. ```APIDOC ## updateImage ### Description Installs the pixel buffer into the Skia Bitmap and returns the associated `ImageBitmap`. ### Method fun ### Parameters #### Path Parameters - **size** (PagSize) - Required - Dimensions (must match the last `pixelsFor()` call). ### Returns - **ImageBitmap** - A Compose-compatible image wrapping the updated pixels. ### Behavior - Calls `Bitmap.installPixels()` to bind the pixel array to the Skia bitmap. - Returns the cached `ImageBitmap` (which references the bitmap). - The `ImageBitmap` is valid until the next `updateImage()` call or buffer reallocation. ``` -------------------------------- ### Compile and Run with Custom Native Libraries Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Instructions for compiling a custom libpag and bridge, and running the application with specified native library paths. ```bash # Compile custom libpag and bridge cmake . make # Run with custom binaries java -Dlibpag.cmp.libpag=/usr/local/lib/libpag.dylib \ -Dlibpag.cmp.bridge=/usr/local/lib/libpag_cmp_jvm.dylib \ -jar app.jar ``` -------------------------------- ### Load PagView from File Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/COMPLETION.txt Demonstrates loading a PAG composition into PagView directly from a file path. This is suitable for local assets. ```kotlin PagView(path = "assets:///path/to/your.pag") ``` -------------------------------- ### Configure Player Rendering Options Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Configure player rendering options using feature flags. Requires a player handle. ```kotlin @JvmStatic external fun setCacheEnabled(playerHandle: Long, cacheEnabled: Boolean) ``` ```kotlin @JvmStatic external fun setVideoEnabled(playerHandle: Long, videoEnabled: Boolean) ``` ```kotlin @JvmStatic external fun setUseDiskCache(playerHandle: Long, useDiskCache: Boolean) ``` -------------------------------- ### Check JVM Platform Detection Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/errors.md Execute this command to display Java properties, including 'os.name', which can help in verifying platform detection for native library loading. ```bash # Check platform detection java -XshowSettings:properties -version 2>&1 | grep os.name ``` -------------------------------- ### Get Pixel Buffer - pixelsFor Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Retrieves a pixel buffer of the specified size. It reallocates the buffer only if the size has changed since the last call, otherwise, it returns the existing buffer. ```kotlin fun pixelsFor(size: PagSize): ByteArray ``` -------------------------------- ### Update Image Bitmap - updateImage Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Installs the current pixel buffer into the Skia Bitmap and returns the associated ImageBitmap. This ImageBitmap is valid until the next updateImage call or buffer reallocation. ```kotlin fun updateImage(size: PagSize): ImageBitmap ``` -------------------------------- ### Reusing PagComposition with PagView Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/pagview.md Demonstrates how to load a PAG composition from bytes and render it using PagView. It ensures the composition is properly closed when no longer needed. ```kotlin import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import io.github.limuyang2.libpag.cmp.Pag import io.github.limuyang2.libpag.cmp.PagView @Composable fun ReusedAnimationView(bytes: ByteArray, modifier: Modifier = Modifier) { val composition = remember(bytes) { Pag.load(bytes) } DisposableEffect(composition) { onDispose { composition.close() } } PagView( composition = composition, modifier = modifier, ) } ``` -------------------------------- ### createPlayer Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Creates a native PAG player for offscreen rendering. Returns a non-zero handle to the native player, or 0 on failure. ```APIDOC ## createPlayer ### Description Creates a native PAG player instance for offscreen rendering. ### Method external fun createPlayer(): Long ### Parameters None ### Request Example ```kotlin // Example usage val playerHandle: Long = JvmPagNative.createPlayer() ``` ### Response #### Success Response - **return value** (Long) - A non-zero handle to the native player. #### Failure Response - **return value** (Long) - 0 on failure. #### Response Example ```kotlin // Assuming playerHandle is obtained from createPlayer if (playerHandle != 0L) { println("PAG player created successfully. Handle: $playerHandle") } else { println("Failed to create PAG player.") } ``` ``` -------------------------------- ### Build JVM Jar and Verify Native Resources Source: https://github.com/limuyang2/pag-cmp/blob/main/BUILD.md Gradle command to build the JVM jar and a shell command to verify that native resources are included. ```shell ./gradlew :lib-pag-cmp:jvmJar jar tf lib-pag-cmp/build/libs/lib-pag-cmp-jvm.jar | rg 'native/macos-arm64' ``` -------------------------------- ### Debug Desktop Demo with External Native Artifacts Source: https://github.com/limuyang2/pag-cmp/blob/main/BUILD.md Command to run the desktop demo using external native libraries specified via JAVA_TOOL_OPTIONS. ```shell JAVA_TOOL_OPTIONS='-Dlibpag.cmp.libpag=/private/tmp/pag-cmp-libpag-build/libpag.framework/Versions/A/libpag -Dlibpag.cmp.bridge=/private/tmp/pag-cmp-bridge-build/libpag_cmp_jvm.dylib' \ ./gradlew :desktopApp:run ``` -------------------------------- ### PagComposition Interface Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/types.md The PagComposition interface represents a decoded PAG animation. It provides access to the animation's metadata such as duration, width, and height, and also inherits the `close()` method from `PagCloseable` for resource management. The `intrinsicSize` property offers a convenient way to get the composition's dimensions. ```APIDOC ## Interface PagComposition ### Description Represents a decoded PAG animation. Holds immutable metadata; can be reused across multiple renderings. ### Properties #### `durationUs` - **Type:** Long - **Description:** Duration of the animation in microseconds. #### `width` - **Type:** Int - **Description:** Composition width in pixels. #### `height` - **Type:** Int - **Description:** Composition height in pixels. #### `intrinsicSize` - **Type:** PagSize - **Description:** Convenience property: `PagSize(width, height)`. ### Methods #### `close()` - **Signature:** `fun close()` - **Description:** Releases native resources. Platform-specific behavior. ``` -------------------------------- ### Load and Create PAG Player on JVM Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/pag.md Calls the JNI bridge JvmPagNative.loadFile() for loading and JvmPagNative.createPlayer() for creating players, returning native handles wrapped in respective composition and player classes. Use this for PAG operations on the JVM. ```kotlin actual object Pag { actual fun load(bytes: ByteArray, filePath: String?): PagComposition { val handle = JvmPagNative.loadFile(bytes, filePath) check(handle != 0L) { "Failed to load PAG composition." } return JvmPagComposition(handle) } actual fun createPlayer(): PagPlayer { val handle = JvmPagNative.createPlayer() check(handle != 0L) { "Failed to create PAG player." } return JvmPagPlayer(handle) } } ``` -------------------------------- ### Load and Render PAG Animation Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Demonstrates loading a PAG animation from bytes and rendering a specific frame. Ensure necessary imports are present. ```kotlin import io.github.limuyang2.libpag.cmp.Pag import io.github.limuyang2.libpag.cmp.PagSize fun renderFrame(bytes: ByteArray): ImageBitmap { val composition = Pag.load(bytes, filePath = "animation.pag") val player = Pag.createPlayer() player.composition = composition player.progress = 0.5 // Render midpoint player.scaleMode = PagScaleMode.LetterBox val image = player.render(PagSize(width = 256, height = 256)) player.close() composition.close() return image } ``` -------------------------------- ### Run JVM Application with Default Native Libraries Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/README.md Execute the application JAR using default bundled native libraries. This is the simplest way to run the application. ```bash java -jar app.jar ``` -------------------------------- ### Create PAG Player Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Creates a native PAG player for offscreen rendering. Returns a non-zero handle on success, or 0 on failure. ```kotlin @JvmStatic external fun createPlayer(): Long ``` -------------------------------- ### Detect Current Platform Logic Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Detects the current operating system and architecture. Returns a platform string like "macos-arm64" for supported platforms, or null if unsupported. ```kotlin private fun currentPlatform(): String? { when { os.contains("mac") && (arch == "aarch64" || arch == "arm64") -> "macos-arm64" else -> null } } ``` -------------------------------- ### PagView(bytes) Implementation in JS Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Renders a PAG file from bytes onto a DOM canvas overlay. It initializes the canvas, loads the PAG SDK, and tracks the Compose placeholder's bounds to position the canvas correctly. Ensure the canvas is appended to the document body and properly removed on disposal. ```kotlin @Composable actual fun PagView( bytes: ByteArray, // ... ) { var bounds by remember { mutableStateOf(null) } val canvas = remember { createPagCanvas() } DisposableEffect(canvas) { document.body.appendChild(canvas) onDispose { JsPagBridge.destroy(canvas) canvas.remove() } } LaunchedEffect(canvas, bytes) { JsPagBridge.init(canvas, bytes).await() } Box( modifier = modifier .fillMaxSize() .onGloballyPositioned { coordinates -> val rect = coordinates.boundsInWindow() bounds = JsPagBounds(left = rect.left.toDouble(), /* ... */) }, ) } ``` -------------------------------- ### Check Platform Availability for PAG Player Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/errors.md Before creating a PAG player, check if the operation is supported on the current platform. Use a try-catch block to handle `UnsupportedOperationException` and provide a fallback mechanism. ```kotlin try { val player = Pag.createPlayer() // Only on JVM val image = player.render(size) } catch (e: UnsupportedOperationException) { // Fallback: use PagView(bytes) instead } ``` -------------------------------- ### Pag Object Entry Point Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/README.md The main `Pag` object provides methods to load animation data and create player instances. ```APIDOC ## Pag Object ### Description The `Pag` object is the primary entry point for interacting with PAG animations. It allows loading animation data from bytes and creating player instances for rendering. ### Methods - **load(bytes, filePath)**: Loads PAG animation data. - **createPlayer()**: Creates a new `PagPlayer` instance. ``` -------------------------------- ### Load PAG File Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Loads a PAG file from bytes and creates a native composition handle. Provide an optional path for native cache keys or diagnostics. Returns a non-zero handle on success, or 0 on failure. ```kotlin @JvmStatic external fun loadFile(bytes: ByteArray, filePath: String?): Long ``` -------------------------------- ### BundledJvmNative.platformDescription() Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Returns a human-readable platform identifier. ```APIDOC ## BundledJvmNative.platformDescription() ### Description Returns a human-readable platform identifier. ### Method Signature ```kotlin fun platformDescription(): String ``` ### Example Output `"macOS aarch64"` ``` -------------------------------- ### Gracefully Handle Path Loading with PagView Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/errors.md Use `PagView` to load PAG files from a path. This component logs errors internally and renders nothing if loading fails, avoiding disruptive error dialogs. ```kotlin @Composable fun SafePagView(path: String, modifier: Modifier = Modifier) { // PagView(path) logs errors but doesn't throw // If loading fails, nothing renders (no error dialog) PagView( path = path, modifier = modifier, ) } ``` -------------------------------- ### BundledJvmNative.current() Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Detects the current platform and extracts bundled natives if available. Returns paths to extracted files or null if the platform is not supported. ```APIDOC ## BundledJvmNative.current() ### Description Detects the current platform and extracts bundled natives if available. Returns paths to extracted files or null if the platform is not supported. ### Method Signature ```kotlin fun current(): BundledJvmNative? ``` ### Returns `BundledJvmNative` with paths to extracted files, or `null` if the platform is not supported (Linux, Windows, etc.). ### Supported platforms - `macos-arm64` — macOS on Apple Silicon. ### Resource layout - `libpag` at `/native/macos-arm64/libpag` - Bridge at `/native/macos-arm64/libpag_cmp_jvm.dylib` ### Extraction Behavior - Creates a temporary directory (`libpag-cmp-{platform}-{timestamp}`). - Extracts resources from the JAR. - Sets executable permissions. - Marks files for deletion on JVM exit. ``` -------------------------------- ### PagView(path) Implementation (Android) Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Loads a PAG file asynchronously from a given path using `setPathAsync()`. Supports local files, assets, and network URLs. Errors are logged, and rendering waits for the path to be ready. ```kotlin LaunchedEffect(path, pagView) { pagView?.setPathAsync(path) { result -> if (result == null) { Log.w("PagView", "Failed to load PAG from path: $path") } } } ``` -------------------------------- ### loadFile Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/jvmpagenative.md Loads a PAG file from bytes and creates a native composition handle. Returns a non-zero handle to the native composition, or 0 on failure. ```APIDOC ## loadFile ### Description Loads a PAG file from bytes and creates a native composition handle. ### Method external fun loadFile(bytes: ByteArray, filePath: String?): Long ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **bytes** (ByteArray) - Required - Raw PAG file bytes. - **filePath** (String?) - Optional - Path for native cache keys or diagnostics. ### Request Example ```kotlin // Example usage (assuming JvmPagNative is accessible) val pagBytes: ByteArray = ... // Load your PAG file bytes here val compositionHandle: Long = JvmPagNative.loadFile(pagBytes, null) ``` ### Response #### Success Response - **return value** (Long) - A non-zero handle to the native composition. #### Failure Response - **return value** (Long) - 0 on failure. #### Response Example ```kotlin // Assuming compositionHandle is obtained from loadFile if (compositionHandle != 0L) { println("PAG file loaded successfully. Handle: $compositionHandle") } else { println("Failed to load PAG file.") } ``` ``` -------------------------------- ### Build Web App JS Browser Development Source: https://github.com/limuyang2/pag-cmp/blob/main/AGENTS.md Build the web application for JS browser development using Gradle. ```shell ./gradlew :webApp:jsBrowserDevelopmentWebpack ``` -------------------------------- ### PagView Implementation with Composition Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Handles PAG rendering using a PagComposition. It manages frame rendering based on elapsed time and playback state, utilizing Compose's `withFrameNanos` for synchronized updates. Supports automatic playback, progress-based rendering, and explicit frame control. ```kotlin import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.graphics.drawscope.drawIntoCanvas import androidx.compose.ui.graphics.nativeCanvas import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.IntSize import org.jetbrains.skia.Bitmap import org.jetbrains.skia.Canvas import org.jetbrains.skia.Image import org.jetbrains.skia.Paint // Assume JvmPagRenderedFrame, PagComposition, player, canvasSize, isPlaying, progress, repeatCount, etc. are defined elsewhere @Composable actual fun PagView( composition: PagComposition, modifier: Modifier = Modifier, isPlaying: Boolean?, progress: Float?, repeatCount: Int?, density: Density = LocalDensity.current, canvasSize: IntSize?, // ... other parameters ) { var frame by remember { mutableStateOf(null) } var playbackStartNanos by remember { mutableStateOf(null) } var shouldContinue by remember { mutableStateOf(true) } val durationNanos = composition.duration * 1_000_000L val currentRepeatCount = repeatCount ?: 1 LaunchedEffect(player, composition, canvasSize, isPlaying, progress, repeatCount, density) { // Render loop: either explicit progress or automatic playback while (shouldContinue) { withFrameNanos { now -> val elapsed = playbackStartNanos?.let { now - it } ?: 0L // Calculate frame progress based on elapsed time val loop = (elapsed / durationNanos).toInt() if (currentRepeatCount in 1..loop) { render(1.0) shouldContinue = false } else { render((elapsed % durationNanos) / durationNanos) } } } } Canvas(modifier = modifier) { frame?.let { // Simplified drawing logic, actual implementation might differ val bitmap = it.image val image = Image.makeFromBitmap(bitmap) drawImage(image) } } } // Placeholder for the actual render function and related logic private suspend fun render(progress: Double) { // Actual rendering logic based on progress // This would involve updating the 'frame' state variable println("Rendering frame at progress: $progress") } // Placeholder for JvmPagRenderedFrame and related types class JvmPagRenderedFrame(val image: Bitmap) // Example structure interface PagComposition { val duration: Long } // Example structure // Dummy implementations for compilation val player: Any? = null val LocalDensity: Any? = null val Modifier: Any? = null val Canvas: Any? = null val drawImage: Any? = null val remember: Any? = null val mutableStateOf: Any? = null val LaunchedEffect: Any? = null val withFrameNanos: Any? = null val println: Any? = null val runCatching: Any? = null val getOrElse: Any? = null val readPagPathBytes: Any? = null val withContext: Any? = null val Dispatchers: Any? = null val java: Any? = null val kotlin: Any? = null val androidx: Any? = null val org: Any? = null val io: Any? = null val URI: Any? = null val URL: Any? = null val openStream: Any? = null val readBytes: Any? = null val File: Any? = null val startsWith: Any? = null val create: Any? = null val toURL: Any? = null val use: Any? = null val IntSize: Any? = null val Density: Any? = null val LocalDensity: Any? = null val Modifier: Any? = null val Canvas: Any? = null val drawImage: Any? = null val remember: Any? = null val mutableStateOf: Any? = null val LaunchedEffect: Any? = null val withFrameNanos: Any? = null val println: Any? = null val runCatching: Any? = null val getOrElse: Any? = null val readPagPathBytes: Any? = null val withContext: Any? = null val Dispatchers: Any? = null val java: Any? = null val kotlin: Any? = null val androidx: Any? = null val org: Any? = null val io: Any? = null val URI: Any? = null val URL: Any? = null val openStream: Any? = null val readBytes: Any? = null val File: Any? = null val startsWith: Any? = null val create: Any? = null val toURL: Any? = null val use: Any? = null val IntSize: Any? = null val Density: Any? = null ``` -------------------------------- ### Initialize and Control Wasm PAG Bridge Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/extensions.md Provides methods to initialize, play, pause, and set progress for PAG animations using WasmJS. The loadPathBytes method handles asynchronous loading of PAG file bytes, utilizing suspendCoroutine due to WasmJS async limitations. ```kotlin private object WasmPagBridge { fun init(canvas: HTMLCanvasElement, bytes: ByteArray) = pagInit(canvas, bytes.size) { index -> bytes[index].toInt() } fun play(canvas: HTMLCanvasElement) = pagPlay(canvas) fun pause(canvas: HTMLCanvasElement) = pagPause(canvas) fun setProgress(canvas: HTMLCanvasElement, progress: Double) = pagSetProgress(canvas, progress) // ... other methods identical to JsPagBridge ... suspend fun loadPathBytes(path: String): ByteArray { val size = suspendCoroutine { cont -> fetchPagPathBytes(path) { fetched -> cont.resume(fetched) } } require(size > 0) { "PAG bytes must not be empty." } return ByteArray(size) { readFetchedByte(path, it).toByte() }.also { clearFetchedBytes(path) } } } ``` -------------------------------- ### Load PAG Composition on iOS Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/pag.md Pins the byte array in memory, passes a pointer to libpag's PAGFile.Load(), and wraps the result in IosPagComposition. Use this for loading PAG compositions from byte arrays on iOS. ```kotlin actual object Pag { actual fun load(bytes: ByteArray, filePath: String?): PagComposition { val pagFile = bytes.usePinned { pinned -> PAGFile.Load(pinned.addressOf(0), bytes.size.convert()) } return IosPagComposition(requireNotNull(pagFile) { "Invalid PAG bytes." }) } actual fun createPlayer(): PagPlayer = unsupportedPagPlatform("iOS") } ``` -------------------------------- ### JavaScript Bridge: pagInit Function Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Initializes the PAG SDK and loads a PAG file onto a given canvas. This function dynamically fetches and evaluates the libpag-bootstrap.js SDK if it's not already available. It's crucial for setting up the PAG rendering environment in the browser. ```javascript @JsFun( """ (canvas, size, readByte) => { const ensureSdk = () => { if (globalThis.libpag && globalThis.libpag.PAGInit) return Promise.resolve(); // Load pag/libpag-bootstrap.js dynamically return fetch('pag/libpag-bootstrap.js').then(res => res.text()) .then(/* eval SDK */) }; // PAG.PAGFile.load(data.buffer), PAG.PAGView.init(pagFile, canvas) } """ ) private external fun pagInit(canvas: HTMLCanvasElement, size: Int, readByte: (Int) -> Int): Promise ``` -------------------------------- ### Load PAG Animation from Local Resources Source: https://github.com/limuyang2/pag-cmp/blob/main/README.md Load a local .pag file from the `src/commonMain/composeResources/files/` directory using the Compose Multiplatform resource API. This ensures the file path works on all targets. ```kotlin import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.limuyang2.libpag.cmp.PagScaleMode import io.github.limuyang2.libpag.cmp.PagView import org.jetbrains.compose.resources.ExperimentalResourceApi // `Res` is generated by the Compose resources plugin; its package follows your module setup. import your.module.generated.resources.Res @OptIn(ExperimentalResourceApi::class) @Composable fun LocalAnimation() { var bytes by remember { mutableStateOf(null) } LaunchedEffect(Unit) { bytes = Res.readBytes("files/loading.pag") } bytes?.let { PagView( bytes = it, modifier = Modifier.size(160.dp), scaleMode = PagScaleMode.LetterBox, ) } } ``` -------------------------------- ### PagView Composable Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/pagview.md Loads and renders a PAG file from a file path or network URL. Loading is asynchronous; nothing renders until the file is available. Load errors are logged, not thrown. ```APIDOC ## PagView(path) ### Description Loads and renders a PAG file from a file path or network URL. Loading is asynchronous; nothing renders until the file is available. Load errors are logged, not thrown. ### Parameters #### Path Parameters - **path** (String) - Required - Local file path or `http`/`https` URL. Exact behavior varies by platform. #### Optional Parameters - **modifier** (Modifier) - Optional - Compose layout modifier for sizing and positioning. - **isPlaying** (Boolean) - Optional - Whether playback is active. `false` pauses at the current `progress`. Defaults to `true`. - **progress** (Double?) - Optional - Manual progress in `0.0..1.0`. When set while paused, the current frame is rendered. `null` follows automatic playback. - **repeatCount** (Int) - Optional - Number of times to repeat the animation. `0` means infinite repeat. Defaults to `0`. - **scaleMode** (PagScaleMode) - Optional - How the PAG content fits inside the render area. Defaults to `PagScaleMode.LetterBox`. - **cacheEnabled** (Boolean) - Optional - Enables libpag render cache. Defaults to `true`. - **videoEnabled** (Boolean) - Optional - Enables video sequence rendering. Defaults to `true`. - **useDiskCache** (Boolean) - Optional - Enables libpag disk cache. Defaults to `false`. ### Path Formats - **Local file:** Absolute path (e.g., `/path/to/file.pag`) or `file:` URI. On Android, also supports `assets://` paths. - **Network URL:** `http://` or `https://` URLs. Support varies by platform: - **Android/iOS:** Handled natively by libpag. - **JVM:** Downloaded via JDK and rendered from bytes. - **JS/WasmJS:** Fetched via browser `fetch()` (subject to CORS); GitHub raw URLs normalized automatically. - **Compose resources:** Use `Res.getUri("files/name.pag")` on JVM/JS/WasmJS/iOS, or Android's `assets://` form. ### Request Example ```kotlin import androidx.compose.foundation.layout.size import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.limuyang2.libpag.cmp.PagScaleMode import io.github.limuyang2.libpag.cmp.PagView @Composable fun RemoteAnimation(url: String) { PagView( path = url, modifier = Modifier.size(200.dp), scaleMode = PagScaleMode.LetterBox, ) } // Example usage: // RemoteAnimation("https://example.com/animation.pag") // RemoteAnimation("/local/path/to/animation.pag") ``` ``` -------------------------------- ### Render Remote or Local PAG Animation Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/pagview.md Demonstrates how to use the PagView composable to render a PAG animation from a URL or a local file path. Ensure correct imports for PagView and Modifier. ```kotlin import androidx.compose.foundation.layout.size import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.limuyang2.libpag.cmp.PagScaleMode import io.github.limuyang2.libpag.cmp.PagView @Composable fun RemoteAnimation(url: String) { PagView( path = url, modifier = Modifier.size(200.dp), scaleMode = PagScaleMode.LetterBox, ) } // Example usage: // RemoteAnimation("https://example.com/animation.pag") // RemoteAnimation("/local/path/to/animation.pag") ``` -------------------------------- ### Load JVM Native Libraries Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Loads native PAG libraries for the JVM. It first checks for system properties and falls back to bundled natives. Throws an IllegalStateException if loading fails. ```kotlin private fun loadNativeLibraries() { try { loadNativeLibrariesOrThrow() } catch (throwable: UnsatisfiedLinkError) { throw IllegalStateException( "Failed to load libpag JVM native libraries. " + "Platform=${BundledJvmNative.platformDescription()}. " + "Set -Dlibpag.cmp.libpag and -Dlibpag.cmp.bridge to debug with external binaries.", throwable, ) } } ``` -------------------------------- ### PagView Composition Implementation (iOS) Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Implements the PagView composable for iOS using UIKitView. It sets up the native PAGView, manages its composition, and defines cleanup logic. ```kotlin @Composable actual fun PagView( composition: PagComposition, modifier: Modifier, // ... parameters ... ) { UIKitView( factory = { PAGView().apply { setComposition(iosComposition.nativeComposition) } }, modifier = modifier, update = { view -> view.applyPagViewState(...) }, onRelease = { view -> view.stop() view.freeCache() }, properties = UIKitInteropProperties( isInteractive = true, isNativeAccessibilityEnabled = true, ), ) } ``` -------------------------------- ### Configure and Build libpag Source: https://github.com/limuyang2/pag-cmp/blob/main/BUILD.md CMake commands to configure and build the libpag library as a shared library for macOS. ```shell cmake -S /Users/mumu/projects/android/libpag \ -B /private/tmp/pag-cmp-libpag-build \ -DPAG_BUILD_SHARED=ON \ -DPAG_USE_C=OFF \ -DPAG_BUILD_TESTS=OFF \ -DCMAKE_BUILD_TYPE=Release cmake --build /private/tmp/pag-cmp-libpag-build --target pag -j 8 ``` -------------------------------- ### Load PAG Animation from Path (URL or Local File) Source: https://github.com/limuyang2/pag-cmp/blob/main/README.md Pass a local file path or network URL directly to PagView. The library loads it asynchronously. This method accepts platform-accessible local paths and http/https URLs. ```kotlin import androidx.compose.foundation.layout.size import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.limuyang2.libpag.cmp.PagScaleMode import io.github.limuyang2.libpag.cmp.PagView @Composable fun RemoteAnimation(url: String) { PagView( path = url, modifier = Modifier.size(160.dp), scaleMode = PagScaleMode.LetterBox, ) } ``` -------------------------------- ### Run JVM Application with Custom Native Libraries Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/README.md Execute the application JAR, specifying custom paths for the libpag native library and the JVM bridge library using system properties. This allows for using specific versions or locations of native components. ```bash java -Dlibpag.cmp.libpag=/path/to/libpag \ -Dlibpag.cmp.bridge=/path/to/libpag_cmp_jvm.dylib \ -jar app.jar ``` -------------------------------- ### Pag.createPlayer() Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/pag.md Creates a reusable PagPlayer instance for rendering animations. This player manages render state such as progress, cache settings, and the associated composition, and can render offscreen to an ImageBitmap. ```APIDOC ## Pag.createPlayer() ### Description Creates a reusable `PagPlayer` for rendering. A player owns render state (progress, cache settings, composition) and can render offscreen to `ImageBitmap`. ### Method `fun createPlayer(): PagPlayer` ### Parameters None ### Response #### Success Response - **PagPlayer** - A new player instance. Must be closed via `player.close()` when no longer needed. ### Throws - `UnsupportedOperationException` on platforms where `createPlayer()` is not implemented (Android, iOS, JS, WasmJS). ### Platform availability - **JVM:** Fully supported. Returns a `JvmPagPlayer`. - **Android/iOS/JS/WasmJS:** Not supported; throws `UnsupportedOperationException`. ### Example ```kotlin import androidx.compose.foundation.Canvas import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import io.github.limuyang2.libpag.cmp.Pag import io.github.limuyang2.libpag.cmp.PagSize @Composable fun CustomPlayerRenderer(bytes: ByteArray, modifier: Modifier = Modifier) { val composition = remember(bytes) { Pag.load(bytes) } val player = remember { Pag.createPlayer() } DisposableEffect(player, composition) { player.composition = composition onDispose { composition.close() player.close() } } Canvas(modifier = modifier) { player.progress = 0.5 val image = player.render(PagSize(width = 100, height = 100)) drawImage(image) } } ``` ``` -------------------------------- ### PagView(bytes) Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/api-reference/pagview.md Loads and renders a PAG file from a ByteArray. The bytes are decoded to a PagComposition internally and rendered immediately. ```APIDOC ## PagView(bytes) ### Description Loads and renders a PAG file from a `ByteArray`. The bytes are decoded to a `PagComposition` internally and rendered immediately. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **bytes** (ByteArray) - Required - Raw PAG file bytes. Must not be empty. - **modifier** (Modifier) - Optional - Compose layout modifier for sizing and positioning. Defaults to `Modifier`. - **isPlaying** (Boolean) - Optional - Whether playback is active. `false` pauses at the current `progress`. Defaults to `true`. - **progress** (Double?) - Optional - Manual progress in `0.0..1.0`. When set while paused, the current frame is rendered. `null` follows automatic playback. Defaults to `null`. - **repeatCount** (Int) - Optional - Number of times to repeat the animation. `0` means infinite repeat. Defaults to `0`. - **scaleMode** (PagScaleMode) - Optional - How the PAG content fits inside the render area. Defaults to `PagScaleMode.LetterBox`. - **cacheEnabled** (Boolean) - Optional - Enables libpag render cache. Defaults to `true`. - **videoEnabled** (Boolean) - Optional - Enables video sequence rendering for PAG files containing video layers. Defaults to `true`. - **useDiskCache** (Boolean) - Optional - Enables libpag disk cache (platform-specific; unsupported on some targets). Defaults to `false`. ### Request Example ```kotlin import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.limuyang2.libpag.cmp.PagScaleMode import io.github.limuyang2.libpag.cmp.PagView @Composable fun AnimationFromBytes(animationBytes: ByteArray) { PagView( bytes = animationBytes, modifier = Modifier.size(200.dp), isPlaying = true, repeatCount = 0, // infinite scaleMode = PagScaleMode.LetterBox, ) } ``` ### Response #### Success Response (Unit) This composable does not return a value, it renders the animation. #### Response Example None ``` -------------------------------- ### Build Web App WasmJs Browser Development Source: https://github.com/limuyang2/pag-cmp/blob/main/AGENTS.md Build the web application for WasmJs browser development using Gradle. ```shell ./gradlew :webApp:wasmJsBrowserDevelopmentWebpack ``` -------------------------------- ### PagView Implementation with File Path Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Loads PAG data from a given path, supporting various URI schemes including `file:`, `http:`, `https:`, and `jar:`. Once loaded, it delegates the rendering to another `PagView` implementation that accepts raw bytes. ```kotlin import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File import java.net.URI // Assume PagView(bytes: ByteArray, ...) is defined elsewhere @Composable actual fun PagView( path: String, modifier: Modifier = Modifier, // ... other parameters ) { var bytes by remember(path) { mutableStateOf(null) } LaunchedEffect(path) { bytes = runCatching { readPagPathBytes(path) }.getOrElse { println("PagView: failed to load PAG from path: $path") null } } bytes?.let { PagView(bytes = it, modifier = modifier /*, ... */) } } private suspend fun readPagPathBytes(path: String): ByteArray = withContext(Dispatchers.IO) { when { path.startsWith("http://") || path.startsWith("https://") || path.startsWith("file:") || path.startsWith("jar:") -> { URI.create(path).toURL().openStream().use { it.readBytes() } } else -> File(path).readBytes() } } // Dummy implementations for compilation val println: Any? = null val runCatching: Any? = null val getOrElse: Any? = null val withContext: Any? = null val Dispatchers: Any? = null val File: Any? = null val URI: Any? = null val URL: Any? = null val openStream: Any? = null val readBytes: Any? = null val startsWith: Any? = null val create: Any? = null val toURL: Any? = null val use: Any? = null val Modifier: Any? = null val Composable: Any? = null val LaunchedEffect: Any? = null val remember: Any? = null val mutableStateOf: Any? = null val ByteArray: Any? = null val String: Any? = null ``` -------------------------------- ### Load PAG from ByteArray Source: https://github.com/limuyang2/pag-cmp/blob/main/README.md Loads a PAG file from a byte array. Ensure the byte array is correctly populated with the PAG file content. ```kotlin Res.readBytes("files/8.pag") -> PagView(bytes) ``` -------------------------------- ### Load PAG Composition from Bytes (Android) Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Decodes byte arrays into a PagComposition using the native libpag Android SDK. The `close()` operation is a no-op as libpag's Composition lacks a release API. ```kotlin fun load(bytes: ByteArray, filePath: String?): PagComposition { return AndroidPagComposition(PAGFile.Load(bytes)) } ``` -------------------------------- ### Load PAG Composition from Bytes (iOS) Source: https://github.com/limuyang2/pag-cmp/blob/main/_autodocs/platform-implementations.md Loads a PAG composition from a byte array. Uses pinned memory for passing a stable pointer to Objective-C and handles potential loading errors. ```kotlin actual fun load(bytes: ByteArray, filePath: String?): PagComposition { val pagFile = bytes.usePinned { pinned -> PAGFile.Load(pinned.addressOf(0), bytes.size.convert()) } return IosPagComposition(requireNotNull(pagFile) { "Invalid PAG bytes." }) } ``` -------------------------------- ### Load PAG from Network URL Source: https://github.com/limuyang2/pag-cmp/blob/main/README.md Loads a PAG file from a remote URL. Ensure the URL is valid and the network connection is available. ```kotlin remote PAG URL -> PagView(path) ``` -------------------------------- ### Load PAG from Local Path Source: https://github.com/limuyang2/pag-cmp/blob/main/README.md Loads a PAG file using a platform-specific local resource path or URI. This is suitable for assets bundled with the application. ```kotlin platform-specific local resource path/URI -> PagView(path) ```