--- title: "Animation" description: "Add motion to designs with support for keyframes, timeline editing, and programmatic animation control." platform: android url: "https://img.ly/docs/cesdk/android/animation-ce900c/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Animation](https://img.ly/docs/cesdk/android/animation-ce900c/) --- --- ## Related Pages - [Overview](https://img.ly/docs/cesdk/android/animation/overview-6a2ef2/) - Add motion to designs with support for keyframes, timeline editing, and programmatic animation control. - [Supported Animation Types](https://img.ly/docs/cesdk/android/animation/types-4e5f41/) - Explore the types of animations supported by CE.SDK, including object, text, and transition effects. - [Create Animations](https://img.ly/docs/cesdk/android/animation/create-15cf50/) - Build animations manually or with presets to animate objects, text, and scenes within your design. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create Animations" description: "Build animations manually or with presets to animate objects, text, and scenes within your design." platform: android url: "https://img.ly/docs/cesdk/android/animation/create-15cf50/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Animation](https://img.ly/docs/cesdk/android/animation-ce900c/) > [Create Animations](https://img.ly/docs/cesdk/android/animation/create-15cf50/) --- --- ## Related Pages - [Base Animations](https://img.ly/docs/cesdk/android/animation/create/base-0fc5c4/) - Apply movement, scaling, rotation, or opacity changes to elements using timeline-based keyframes. - [Text Animations](https://img.ly/docs/cesdk/android/animation/create/text-d6f4aa/) - Animate text elements with effects like fade, typewriter, and bounce for dynamic visual presentation. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Base Animations" description: "Apply movement, scaling, rotation, or opacity changes to elements using timeline-based keyframes." platform: android url: "https://img.ly/docs/cesdk/android/animation/create/base-0fc5c4/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Animation](https://img.ly/docs/cesdk/android/animation-ce900c/) > [Create Animations](https://img.ly/docs/cesdk/android/animation/create-15cf50/) > [Base Animations](https://img.ly/docs/cesdk/android/animation/create/base-0fc5c4/) --- ```kotlin file=@cesdk_android_examples/engine-guides-using-animations/UsingAnimations.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.AnimationType import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode fun usingAnimations( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 100F) engine.block.setPositionY(block, value = 50F) engine.block.setWidth(block, value = 300F) engine.block.setHeight(block, value = 300F) engine.block.appendChild(parent = page, child = block) val fill = engine.block.createFill(FillType.Image) engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) engine.block.setFill(block, fill = fill) if (!engine.block.supportsAnimation(block)) { engine.stop() return@launch } val slideInAnimation = engine.block.createAnimation(AnimationType.Slide) val breathingLoopAnimation = engine.block.createAnimation(AnimationType.BreathingLoop) val fadeOutAnimation = engine.block.createAnimation(AnimationType.Fade) engine.block.setInAnimation(block, slideInAnimation) engine.block.setLoopAnimation(block, breathingLoopAnimation) engine.block.setOutAnimation(block, fadeOutAnimation) val animation = engine.block.getLoopAnimation(block) val animationType = engine.block.getType(animation) val squeezeLoopAnimation = engine.block.createAnimation(AnimationType.SqueezeLoop) engine.block.destroy(engine.block.getLoopAnimation(block)) engine.block.setLoopAnimation(block, squeezeLoopAnimation) // The following line would also destroy all currently attached animations // engine.block.destroy(block) val allAnimationProperties = engine.block.findAllProperties(slideInAnimation) engine.block.setFloat(slideInAnimation, "animation/slide/direction", 0.5F * Math.PI.toFloat()) engine.block.setDuration(slideInAnimation, 0.6) engine.block.setEnum(slideInAnimation, "animationEasing", "EaseOut") println("Available easing options: ${engine.block.getEnumValues("animationEasing")}") val text = engine.block.create(DesignBlockType.Text) val textAnimation = engine.block.createAnimation(AnimationType.Baseline) engine.block.setInAnimation(text, textAnimation) engine.block.appendChild(page, text) engine.block.setPositionX(text, 100F) engine.block.setPositionY(text, 100F) engine.block.setWidthMode(text, SizeMode.AUTO) engine.block.setHeightMode(text, SizeMode.AUTO) engine.block.replaceText(text, "You can animate text\nline by line,\nword by word,\nor character by character\nwith CE.SDK") engine.block.setEnum(textAnimation, "textAnimationWritingStyle", "Word") engine.block.setDuration(textAnimation, 2.0) engine.block.setEnum(textAnimation, "animationEasing", "EaseOut") val text2 = engine.block.create(DesignBlockType.Text) val textAnimation2 = engine.block.createAnimation(AnimationType.Pan) engine.block.setInAnimation(text2, textAnimation2) engine.block.appendChild(page, text2) engine.block.setPositionX(text2, 100F) engine.block.setPositionY(text2, 500F) engine.block.setWidth(text2, 500F) engine.block.setHeightMode(text2, SizeMode.AUTO) engine.block.replaceText(text2, "You can use the textAnimationOverlap property to control the overlap between text animation segments.") engine.block.setFloat(textAnimation2, "textAnimationOverlap", 0.4F) engine.block.setDuration(textAnimation2, 1.0) engine.block.setEnum(textAnimation2, "animationEasing", "EaseOut") engine.stop() } ``` CreativeEditor SDK supports many different types of configurable animations for animating the appearance of design blocks in video scenes. Similarly to blocks, each animation object has a numeric id which can be used to query and [modify its properties](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/). ## Accessing Animation APIs In order to query whether a block supports animations, you should call the `fun supportsAnimation(block: DesignBlock): Boolean` API. ```kotlin highlight-supportsAnimation if (!engine.block.supportsAnimation(block)) { engine.stop() return@launch } ``` ## Animation Categories There are three different categories of animations: *In*, *Out* and *Loop* animations. ### In Animations *In* animations animate a block for a specified duration after the block first appears in the scene. For example, if a block has a time offset of 4s in the scene and it has an *In* animation with a duration of 1s, then the appearance of the block will be animated between 4s and 5s with the *In* animation. ### Out Animations *Out* animations animate a block for a specified duration before the block disappears from the scene. For example, if a block has a time offset of 4s in the scene and a duration of 5s and it has an *Out* animation with a duration of 1s, then the appearance of the block will be animated between 8s and 9s with the *Out* animation. ### Loop Animations *Loop* animations animate a block for the total duration that the block is visible in the scene. *Loop* animations also run simultaneously with *In* and *Out* animations, if those are present. ## Creating Animations In order to create a new animation, we must call the `fun createAnimation(type: AnimationType): DesignBlock` API. All `AnimationType` implementations below are nested in `AnimationType` sealed class. We currently support the following *In* and *Out* animation types: - `Slide - "//ly.img.ubq/animation/slide"` - `Pan - "//ly.img.ubq/animation/pan"` - `Fade - "//ly.img.ubq/animation/fade"` - `Blur - "//ly.img.ubq/animation/blur"` - `Grow - "//ly.img.ubq/animation/grow"` - `Zoom - "//ly.img.ubq/animation/zoom"` - `Pop - "//ly.img.ubq/animation/pop"` - `Wipe - "//ly.img.ubq/animation/wipe"` - `Baseline - "//ly.img.ubq/animation/baseline"` - `CropZoom - "//ly.img.ubq/animation/crop_zoom"` - `Spin - "//ly.img.ubq/animation/spin"` - `KenBurns - "//ly.img.ubq/animation/ken_burns"` - `TypewriterText - "//ly.img.ubq/animation/typewriter_text"` // text-ony - `BlockSwipeText - "//ly.img.ubq/animation/block_swipe_text"` // text-ony - `SpreadText - "//ly.img.ubq/animation/spread_text"` // text-only - `MergeText - "//ly.img.ubq/animation/merge_text"` // text-only and the following *Loop* animation types: - `SpinLoop - "//ly.img.ubq/animation/spin_loop"` - `FadeLoop - "//ly.img.ubq/animation/fade_loop"` - `BlurLoop - "//ly.img.ubq/animation/blur_loop"` - `PulsatingLoop - "//ly.img.ubq/animation/pulsating_loop"` - `BreathingLoop - "//ly.img.ubq/animation/breathing_loop"` - `JumpLoop - "//ly.img.ubq/animation/jump_loop"` - `SqueezeLoop - "//ly.img.ubq/animation/squeeze_loop"` - `SwayLoop - "//ly.img.ubq/animation/sway_loop"` ```kotlin highlight-createAnimation val slideInAnimation = engine.block.createAnimation(AnimationType.Slide) val breathingLoopAnimation = engine.block.createAnimation(AnimationType.BreathingLoop) val fadeOutAnimation = engine.block.createAnimation(AnimationType.Fade) ``` ## Assigning Animations In order to assign an *In* animation to the block, call the `fun setInAnimation(block: DesignBlock, animation: DesignBlock)` API. ```kotlin highlight-setInAnimation engine.block.setInAnimation(block, slideInAnimation) ``` In order to assign a *Loop* animation to the block, call the `fun setLoopAnimation(block: DesignBlock, animation: DesignBlock)` API. ```kotlin highlight-setLoopAnimation engine.block.setLoopAnimation(block, breathingLoopAnimation) ``` In order to assign an *Out* animation to the block, call the `fun setOutAnimation(block: DesignBlock, animation: DesignBlock)` API. ```kotlin highlight-setOutAnimation engine.block.setOutAnimation(block, fadeOutAnimation) ``` To query the current animation ids of a design block, call the `fun getInAnimation(block: DesignBlock): DesignBlock`, `fun getLoopAnimation(block: DesignBlock): DesignBlock` or `fun getInAnimation(block: DesignBlock): DesignBlock` API. You can now pass the returned animation `DesignBlock` into other APIs in order to query more information about the animation, e.g. its type via the `fun getType(block: DesignBlock): String` API. In case the design block does not have animation, query will return an invalid design block. Make sure to check for `fun isValid(block: DesignBlock): Boolean` before running any API's on the animation design block. ```kotlin highlight-getAnimation val animation = engine.block.getLoopAnimation(block) val animationType = engine.block.getType(animation) ``` When replacing the animation of a design block, remember to destroy the previous animation object if you don't intend to use it any further. Animation objects that are not attached to a design block will never be automatically destroyed. Destroying a design block will also destroy all of its attached animations. ```kotlin highlight-replaceAnimation val squeezeLoopAnimation = engine.block.createAnimation(AnimationType.SqueezeLoop) engine.block.destroy(engine.block.getLoopAnimation(block)) engine.block.setLoopAnimation(block, squeezeLoopAnimation) // The following line would also destroy all currently attached animations // engine.block.destroy(block) ``` ## Animation Properties Just like design blocks, animations with different types have different properties that you can query and modify via the API. Use `fun findAllProperties(block: DesignBlock): List` in order to get a list of all properties of a given animation. For the slide animation in this example, the call would return `["name", "animation/slide/direction", "animationEasing", "includedInExport", "playback/duration", "type", "uuid"]`. Please refer to the [API docs](https://img.ly/docs/cesdk/android/animation/types-4e5f41/) for a complete list of all available properties for each type of animation. ```kotlin highlight-getProperties val allAnimationProperties = engine.block.findAllProperties(slideInAnimation) ``` Once we know the property keys of an animation, we can use the same APIs as for design blocks in order to modify those properties. For example, we can use `fun setFloat(block: DesignBlock, property: String, value: Float)` in order to change the direction of the slide animation to make our block slide in from the top. ```kotlin highlight-modifyProperties engine.block.setFloat(slideInAnimation, "animation/slide/direction", 0.5F * Math.PI.toFloat()) ``` All animations have a duration. For *In* and *Out* animations, the duration defines the total length of the animation as described above. For *Loop* animations, the duration defines the length of each loop cycle. We can use the `fun setDuration(block: DesignBlock, duration: Double)` API in order to change the animation duration. Note that changing the duration of an *In* animation will automatically adjust the duration of the *Out* animation (and vice versa) in order to avoid overlaps between the two animations. ```kotlin highlight-changeDuration engine.block.setDuration(slideInAnimation, 0.6) ``` Some animations allow you to configure their easing behavior by choosing from a list of common easing curves. The easing controls the acceleration throughout the animation. We can use the `fun setEnum(block: DesignBlock, property: String, value: String)` API in order to change the easing curve. Call `engine.block.getEnumValues("animationEasing")` in order to get a list of currently supported easing options. In this example, we set the easing to `EaseOut` so that the animation starts fast and then slows down towards the end. An `EaseIn` easing would start slow and then speed up, while `EaseInOut` starts slow, speeds up towards the middle of the animation and then slows down towards the end again. ```kotlin highlight-changeEasing engine.block.setEnum(slideInAnimation, "animationEasing", "EaseOut") println("Available easing options: ${engine.block.getEnumValues("animationEasing")}") ``` ## Full Code Here's the full code: ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.AnimationType import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode fun usingAnimations( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 100F) engine.block.setPositionY(block, value = 50F) engine.block.setWidth(block, value = 300F) engine.block.setHeight(block, value = 300F) engine.block.appendChild(parent = page, child = block) val fill = engine.block.createFill(FillType.Image) engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) engine.block.setFill(block, fill = fill) if (!engine.block.supportsAnimation(block)) { engine.stop() return@launch } val slideInAnimation = engine.block.createAnimation(AnimationType.Slide) val breathingLoopAnimation = engine.block.createAnimation(AnimationType.BreathingLoop) val fadeOutAnimation = engine.block.createAnimation(AnimationType.Fade) engine.block.setInAnimation(block, slideInAnimation) engine.block.setLoopAnimation(block, breathingLoopAnimation) engine.block.setOutAnimation(block, fadeOutAnimation) val animation = engine.block.getLoopAnimation(block) val animationType = engine.block.getType(animation) val squeezeLoopAnimation = engine.block.createAnimation(AnimationType.SqueezeLoop) engine.block.destroy(engine.block.getLoopAnimation(block)) engine.block.setLoopAnimation(block, squeezeLoopAnimation) // The following line would also destroy all currently attached animations // engine.block.destroy(block) val allAnimationProperties = engine.block.findAllProperties(slideInAnimation) engine.block.setFloat(slideInAnimation, "animation/slide/direction", 0.5F * Math.PI.toFloat()) engine.block.setDuration(slideInAnimation, 0.6) engine.block.setEnum(slideInAnimation, "animationEasing", "EaseOut") println("Available easing options: ${engine.block.getEnumValues("animationEasing")}") engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Text Animations" description: "Animate text elements with effects like fade, typewriter, and bounce for dynamic visual presentation." platform: android url: "https://img.ly/docs/cesdk/android/animation/create/text-d6f4aa/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Animation](https://img.ly/docs/cesdk/android/animation-ce900c/) > [Create Animations](https://img.ly/docs/cesdk/android/animation/create-15cf50/) > [Text Animations](https://img.ly/docs/cesdk/android/animation/create/text-d6f4aa/) --- ```kotlin file=@cesdk_android_examples/engine-guides-using-animations/UsingAnimations.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.AnimationType import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode fun usingAnimations( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 100F) engine.block.setPositionY(block, value = 50F) engine.block.setWidth(block, value = 300F) engine.block.setHeight(block, value = 300F) engine.block.appendChild(parent = page, child = block) val fill = engine.block.createFill(FillType.Image) engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) engine.block.setFill(block, fill = fill) if (!engine.block.supportsAnimation(block)) { engine.stop() return@launch } val slideInAnimation = engine.block.createAnimation(AnimationType.Slide) val breathingLoopAnimation = engine.block.createAnimation(AnimationType.BreathingLoop) val fadeOutAnimation = engine.block.createAnimation(AnimationType.Fade) engine.block.setInAnimation(block, slideInAnimation) engine.block.setLoopAnimation(block, breathingLoopAnimation) engine.block.setOutAnimation(block, fadeOutAnimation) val animation = engine.block.getLoopAnimation(block) val animationType = engine.block.getType(animation) val squeezeLoopAnimation = engine.block.createAnimation(AnimationType.SqueezeLoop) engine.block.destroy(engine.block.getLoopAnimation(block)) engine.block.setLoopAnimation(block, squeezeLoopAnimation) // The following line would also destroy all currently attached animations // engine.block.destroy(block) val allAnimationProperties = engine.block.findAllProperties(slideInAnimation) engine.block.setFloat(slideInAnimation, "animation/slide/direction", 0.5F * Math.PI.toFloat()) engine.block.setDuration(slideInAnimation, 0.6) engine.block.setEnum(slideInAnimation, "animationEasing", "EaseOut") println("Available easing options: ${engine.block.getEnumValues("animationEasing")}") val text = engine.block.create(DesignBlockType.Text) val textAnimation = engine.block.createAnimation(AnimationType.Baseline) engine.block.setInAnimation(text, textAnimation) engine.block.appendChild(page, text) engine.block.setPositionX(text, 100F) engine.block.setPositionY(text, 100F) engine.block.setWidthMode(text, SizeMode.AUTO) engine.block.setHeightMode(text, SizeMode.AUTO) engine.block.replaceText(text, "You can animate text\nline by line,\nword by word,\nor character by character\nwith CE.SDK") engine.block.setEnum(textAnimation, "textAnimationWritingStyle", "Word") engine.block.setDuration(textAnimation, 2.0) engine.block.setEnum(textAnimation, "animationEasing", "EaseOut") val text2 = engine.block.create(DesignBlockType.Text) val textAnimation2 = engine.block.createAnimation(AnimationType.Pan) engine.block.setInAnimation(text2, textAnimation2) engine.block.appendChild(page, text2) engine.block.setPositionX(text2, 100F) engine.block.setPositionY(text2, 500F) engine.block.setWidth(text2, 500F) engine.block.setHeightMode(text2, SizeMode.AUTO) engine.block.replaceText(text2, "You can use the textAnimationOverlap property to control the overlap between text animation segments.") engine.block.setFloat(textAnimation2, "textAnimationOverlap", 0.4F) engine.block.setDuration(textAnimation2, 1.0) engine.block.setEnum(textAnimation2, "animationEasing", "EaseOut") engine.stop() } ``` When applied to text blocks, some animations allow you to control whether the animation should be applied to the entire text at once, line by line, word by word or character by character. We can use the `fun setEnum(block: DesignBlock, property: String, value: String)` API in order to change the text writing style. Call `engine.block.getEnumValues("textAnimationWritingStyle")` in order to get a list of currently supported text writing style options. The default writing style is `Line`. In this example, we set the easing to `Word` so that the text animates in one word at a time. ```kotlin highlight-textAnimationWritingStyle val text = engine.block.create(DesignBlockType.Text) val textAnimation = engine.block.createAnimation(AnimationType.Baseline) engine.block.setInAnimation(text, textAnimation) engine.block.appendChild(page, text) engine.block.setPositionX(text, 100F) engine.block.setPositionY(text, 100F) engine.block.setWidthMode(text, SizeMode.AUTO) engine.block.setHeightMode(text, SizeMode.AUTO) engine.block.replaceText(text, "You can animate text\nline by line,\nword by word,\nor character by character\nwith CE.SDK") engine.block.setEnum(textAnimation, "textAnimationWritingStyle", "Word") engine.block.setDuration(textAnimation, 2.0) engine.block.setEnum(textAnimation, "animationEasing", "EaseOut") ``` Together with the writing style, you can also configure the overlap between the individual segments of a text animation using the `textAnimationOverlap` property. With an overlap value of `0`, the next segment only starts its animation once the previous segment's animation has finished. With an overlap value of `1`, all segments animate at the same time. ```kotlin highlight-textAnimationOverlap val text2 = engine.block.create(DesignBlockType.Text) val textAnimation2 = engine.block.createAnimation(AnimationType.Pan) engine.block.setInAnimation(text2, textAnimation2) engine.block.appendChild(page, text2) engine.block.setPositionX(text2, 100F) engine.block.setPositionY(text2, 500F) engine.block.setWidth(text2, 500F) engine.block.setHeightMode(text2, SizeMode.AUTO) engine.block.replaceText(text2, "You can use the textAnimationOverlap property to control the overlap between text animation segments.") engine.block.setFloat(textAnimation2, "textAnimationOverlap", 0.4F) engine.block.setDuration(textAnimation2, 1.0) engine.block.setEnum(textAnimation2, "animationEasing", "EaseOut") ``` ## Full Code Here's the full code: ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.AnimationType import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode fun usingAnimations( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val text = engine.block.create(DesignBlockType.Text) val textAnimation = engine.block.createAnimation(AnimationType.Baseline) engine.block.setInAnimation(text, textAnimation) engine.block.appendChild(page, text) engine.block.setPositionX(text, 100F) engine.block.setPositionY(text, 100F) engine.block.setWidthMode(text, SizeMode.AUTO) engine.block.setHeightMode(text, SizeMode.AUTO) engine.block.replaceText(text, "You can animate text\nline by line,\nword by word,\nor character by character\nwith CE.SDK") engine.block.setEnum(textAnimation, "textAnimationWritingStyle", "Word") engine.block.setDuration(textAnimation, 2.0) engine.block.setEnum(textAnimation, "animationEasing", "EaseOut") val text2 = engine.block.create(DesignBlockType.Text) val textAnimation2 = engine.block.createAnimation(AnimationType.Pan) engine.block.setInAnimation(text2, textAnimation2) engine.block.appendChild(page, text2) engine.block.setPositionX(text2, 100F) engine.block.setPositionY(text2, 500F) engine.block.setWidth(text2, 500F) engine.block.setHeightMode(text2, SizeMode.AUTO) engine.block.replaceText(text2, "You can use the textAnimationOverlap property to control the overlap between text animation segments.") engine.block.setFloat(textAnimation2, "textAnimationOverlap", 0.4F) engine.block.setDuration(textAnimation2, 1.0) engine.block.setEnum(textAnimation2, "animationEasing", "EaseOut") engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Overview" description: "Add motion to designs with support for keyframes, timeline editing, and programmatic animation control." platform: android url: "https://img.ly/docs/cesdk/android/animation/overview-6a2ef2/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Animation](https://img.ly/docs/cesdk/android/animation-ce900c/) > [Overview](https://img.ly/docs/cesdk/android/animation/overview-6a2ef2/) --- Animations in CreativeEditor SDK (CE.SDK) bring your designs to life by adding motion to images, text, and design elements. Whether you're creating a dynamic social media post, a video ad, or an engaging product demo, animations help capture attention and communicate ideas more effectively. With CE.SDK, you can create and edit animations either through the built-in UI timeline or programmatically using the CreativeEngine API. Animated designs can be exported as MP4 videos, allowing you to deliver polished, motion-rich content entirely client-side. [Explore Demos](https://img.ly/showcases/cesdk?tags=android) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Supported Animation Types" description: "Explore the types of animations supported by CE.SDK, including object, text, and transition effects." platform: android url: "https://img.ly/docs/cesdk/android/animation/types-4e5f41/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Animation](https://img.ly/docs/cesdk/android/animation-ce900c/) > [Supported Animation Types](https://img.ly/docs/cesdk/android/animation/types-4e5f41/) --- ## Animation Categories There are three different categories of animations: *In*, *Out* and *Loop* animations. ### In Animations *In* animations animate a block for a specified duration after the block first appears in the scene. For example, if a block has a time offset of 4s in the scene and it has an *In* animation with a duration of 1s, then the appearance of the block will be animated between 4s and 5s with the *In* animation. ### Out Animations *Out* animations animate a block for a specified duration before the block disappears from the scene. For example, if a block has a time offset of 4s in the scene and a duration of 5s and it has an *Out* animation with a duration of 1s, then the appearance of the block will be animated between 8s and 9s with the *Out* animation. ### Loop Animations *Loop* animations animate a block for the total duration that the block is visible in the scene. *Loop* animations also run simultaneously with *In* and *Out* animations, if those are present. ## Animation Presets We currently support the following *In* and *Out* animation presets: - `'//ly.img.ubq/animation/slide'` - `'//ly.img.ubq/animation/pan'` - `'//ly.img.ubq/animation/fade'` - `'//ly.img.ubq/animation/blur'` - `'//ly.img.ubq/animation/grow'` - `'//ly.img.ubq/animation/zoom'` - `'//ly.img.ubq/animation/pop'` - `'//ly.img.ubq/animation/wipe'` - `'//ly.img.ubq/animation/baseline'` - `'//ly.img.ubq/animation/crop_zoom'` - `'//ly.img.ubq/animation/spin'` - `'//ly.img.ubq/animation/ken_burns'` - `'//ly.img.ubq/animation/typewriter_text'` (text-only) - `'//ly.img.ubq/animation/block_swipe_text'` (text-only) - `'//ly.img.ubq/animation/merge_text'` (text-only) - `'//ly.img.ubq/animation/spread_text'` (text-only) and the following *Loop* animation types: - `'//ly.img.ubq/animation/spin_loop'` - `'//ly.img.ubq/animation/fade_loop'` - `'//ly.img.ubq/animation/blur_loop'` - `'//ly.img.ubq/animation/pulsating_loop'` - `'//ly.img.ubq/animation/breathing_loop'` - `'//ly.img.ubq/animation/jump_loop'` - `'//ly.img.ubq/animation/squeeze_loop'` - `'//ly.img.ubq/animation/sway_loop'` ## Animation Type Properties ## Baseline Type A text animation that slides text in along its baseline. This section describes the properties available for the **Baseline Type** (`//ly.img.ubq/animation/baseline`) block type. | Property | Type | Default | Description | | ------------------------------ | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/baseline/direction` | `Enum` | `"Up"` | The direction of the wipe animation., Possible values: `"Up"`, `"Right"`, `"Down"`, `"Left"` | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Block Swipe Text Type A text animation that reveals text with a colored block swiping across. This section describes the properties available for the **Block Swipe Text Type** (`//ly.img.ubq/animation/block_swipe_text`) block type. | Property | Type | Default | Description | | ----------------------------------------- | -------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `animation/block_swipe_text/blockColor` | `Color` | `{"r":0,"g":0,"b":0,"a":1}` | The overlay block color. | | `animation/block_swipe_text/direction` | `Enum` | `"Right"` | The direction of the block swipe animation., Possible values: `"Up"`, `"Right"`, `"Down"`, `"Left"` | | `animation/block_swipe_text/useTextColor` | `Bool` | `true` | Whether the overlay block should use the text color. | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Blur Type An animation that applies a blur effect over time. This section describes the properties available for the **Blur Type** (`//ly.img.ubq/animation/blur`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/blur/fade` | `Bool` | `true` | Whether an opacity fade animation should be applied during the blur animation. | | `animation/blur/intensity` | `Float` | `1` | The maximum intensity of the blur. | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Blur Loop Type A looping animation that continuously applies a blur effect. This section describes the properties available for the **Blur Loop Type** (`//ly.img.ubq/animation/blur_loop`) block type. | Property | Type | Default | Description | | ------------------------------- | -------- | ------- | --------------------------------------------------------------- | | `animation/blur_loop/intensity` | `Float` | `1` | The maximum blur intensity of this effect. | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Breathing Loop Type A looping animation with a slow, breathing-like scale effect. This section describes the properties available for the **Breathing Loop Type** (`//ly.img.ubq/animation/breathing_loop`) block type. | Property | Type | Default | Description | | ------------------------------------ | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `animation/breathing_loop/intensity` | `Float` | `0` | Controls the intensity of the scaling. A value of 0 results in a maximum scale of 1.25. A value of 1 results in a maximum scale of 2.5. | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Crop Zoom Type An animation that zooms the content within the block's frame. This section describes the properties available for the **Crop Zoom Type** (`//ly.img.ubq/animation/crop_zoom`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/crop_zoom/fade` | `Bool` | `true` | Whether an opacity fade animation should be applied during the crop zoom animation. | | `animation/crop_zoom/scale` | `Float` | `1.25` | The maximum crop scale value. | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Fade Type An animation that fades the block in or out. This section describes the properties available for the **Fade Type** (`//ly.img.ubq/animation/fade`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Fade Loop Type A looping animation that continuously fades the block in and out. This section describes the properties available for the **Fade Loop Type** (`//ly.img.ubq/animation/fade_loop`) block type. | Property | Type | Default | Description | | ------------------- | -------- | ------- | --------------------------------------------------------------- | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Grow Type An animation that scales the block up from a point. This section describes the properties available for the **Grow Type** (`//ly.img.ubq/animation/grow`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/grow/direction` | `Enum` | `"All"` | The direction from which the grow animation originates. Can be horizontal only, vertical only, from the center (all), or from any of the four corners (top-left, top-right, bottom-left, bottom-right)., Possible values: `"Horizontal"`, `"Vertical"`, `"All"` | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Jump Loop Type A looping animation with a jumping motion. This section describes the properties available for the **Jump Loop Type** (`//ly.img.ubq/animation/jump_loop`) block type. | Property | Type | Default | Description | | ------------------------------- | -------- | ------- | -------------------------------------------------------------------------------------------- | | `animation/jump_loop/direction` | `Enum` | `"Up"` | The direction of the jump animation., Possible values: `"Up"`, `"Right"`, `"Down"`, `"Left"` | | `animation/jump_loop/intensity` | `Float` | `0.5` | Controls how far the block should move as a percentage of its width or height. | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Ken Burns Type An animation that simulates the Ken Burns effect by panning and zooming on content. This section describes the properties available for the **Ken Burns Type** (`//ly.img.ubq/animation/ken_burns`) block type. | Property | Type | Default | Description | | ----------------------------------------- | -------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/ken_burns/direction` | `Enum` | `"Right"` | The direction of the pan travel., Possible values: `"Up"`, `"Right"`, `"Down"`, `"Left"` | | `animation/ken_burns/fade` | `Bool` | `false` | Whether an opacity fade animation should be applied during the animation. | | `animation/ken_burns/travelDistanceRatio` | `Float` | `1` | The movement distance relative to the length of the crop. | | `animation/ken_burns/zoomIntensity` | `Float` | `0.5` | The factor by which to zoom in or out. | | `animationEasing` | `Enum` | `"EaseOutQuint"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `2.4` | The duration in seconds for which this block should be visible. | ## Merge Text Type A text animation where lines of text merge from opposite directions. This section describes the properties available for the **Merge Text Type** (`//ly.img.ubq/animation/merge_text`) block type. | Property | Type | Default | Description | | -------------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/merge_text/direction` | `Enum` | `"Left"` | The in-animation direction of the first line of text., Possible values: `"Right"`, `"Left"` | | `animation/merge_text/intensity` | `Float` | `0.5` | The intensity of the pan. | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Pan Type An animation that pans the block across the view. This section describes the properties available for the **Pan Type** (`//ly.img.ubq/animation/pan`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/pan/direction` | `Float` | `0` | The movement direction of the animation in radians. | | `animation/pan/distance` | `Float` | `0.1` | The movement distance relative to the longer side of the page. | | `animation/pan/fade` | `Bool` | `true` | Whether an opacity fade animation should be applied during the pan animation. | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Pop Type An animation that quickly scales the block up and down. This section describes the properties available for the **Pop Type** (`//ly.img.ubq/animation/pop`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Pulsating Loop Type A looping animation with a pulsating scale effect. This section describes the properties available for the **Pulsating Loop Type** (`//ly.img.ubq/animation/pulsating_loop`) block type. | Property | Type | Default | Description | | ------------------------------------ | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `animation/pulsating_loop/intensity` | `Float` | `0` | Controls the intensity of the pulsating effect. A value of 0 results in a maximum scale of 1.25. A value of 1 results in a maximum scale of 2.5. | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | ## Slide Type An animation that slides the block into or out of view. This section describes the properties available for the **Slide Type** (`//ly.img.ubq/animation/slide`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/slide/direction` | `Float` | `0` | The movement direction angle of the slide animation in radians. | | `animation/slide/fade` | `Bool` | `false` | Whether an opacity fade animation should be applied during the slide animation. | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Spin Type An animation that rotates the block. This section describes the properties available for the **Spin Type** (`//ly.img.ubq/animation/spin`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/spin/direction` | `Enum` | `"Clockwise"` | The direction of the spin animation., Possible values: `"Clockwise"`, `"CounterClockwise"` | | `animation/spin/fade` | `Bool` | `true` | Whether an opacity fade animation should be applied during the spin animation. | | `animation/spin/intensity` | `Float` | `1` | How far the animation should spin the block. 1.0 is a full rotation (360°). | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Spin Loop Type A looping animation that continuously rotates the block. This section describes the properties available for the **Spin Loop Type** (`//ly.img.ubq/animation/spin_loop`) block type. | Property | Type | Default | Description | | ------------------------------- | -------- | ------------- | ------------------------------------------------------------------------------------------ | | `animation/spin_loop/direction` | `Enum` | `"Clockwise"` | The direction of the spin animation., Possible values: `"Clockwise"`, `"CounterClockwise"` | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Spread Text Type A text animation where letters spread apart or come together. This section describes the properties available for the **Spread Text Type** (`//ly.img.ubq/animation/spread_text`) block type. | Property | Type | Default | Description | | --------------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/spread_text/fade` | `Bool` | `true` | Whether the text should fade in / out during the spread animation. | | `animation/spread_text/intensity` | `Float` | `0.5` | The intensity of the spread. | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | ## Squeeze Loop Type A looping animation with a squeezing effect. This section describes the properties available for the **Squeeze Loop Type** (`//ly.img.ubq/animation/squeeze_loop`) block type. | Property | Type | Default | Description | | ------------------- | -------- | ------- | --------------------------------------------------------------- | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Sway Loop Type A looping animation with a swaying rotational motion. This section describes the properties available for the **Sway Loop Type** (`//ly.img.ubq/animation/sway_loop`) block type. | Property | Type | Default | Description | | ------------------------------- | -------- | ------- | ----------------------------------------------------------------------------------- | | `animation/sway_loop/intensity` | `Float` | `1` | The intensity of the animation. Defines the maximum sway angle between 15° and 45°. | | `playback/duration` | `Double` | `1.2` | The duration in seconds for which this block should be visible. | ## Typewriter Text Type A text animation that reveals text as if it's being typed. This section describes the properties available for the **Typewriter Text Type** (`//ly.img.ubq/animation/typewriter_text`) block type. | Property | Type | Default | Description | | ---------------------------------------- | -------- | ------------- | ------------------------------------------------------------------------------------------------------------- | | `animation/typewriter_text/writingStyle` | `Enum` | `"Character"` | Whether the text should appear one character or one word at a time., Possible values: `"Character"`, `"Word"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | ## Wipe Type An animation that reveals or hides the block with a wipe transition. This section describes the properties available for the **Wipe Type** (`//ly.img.ubq/animation/wipe`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/wipe/direction` | `Enum` | `"Right"` | The direction of the wipe animation., Possible values: `"Up"`, `"Right"`, `"Down"`, `"Left"` | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | ## Zoom Type An animation that scales the entire block. This section describes the properties available for the **Zoom Type** (`//ly.img.ubq/animation/zoom`) block type. | Property | Type | Default | Description | | --------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `animation/zoom/fade` | `Bool` | `true` | Whether an opacity fade animation should be applied during the zoom animation. | | `animationEasing` | `Enum` | `"Linear"` | The easing function to apply to the animation., Possible values: `"Linear"`, `"EaseIn"`, `"EaseOut"`, `"EaseInOut"`, `"EaseInQuart"`, `"EaseOutQuart"`, `"EaseInOutQuart"`, `"EaseInQuint"`, `"EaseOutQuint"`, `"EaseInOutQuint"`, `"EaseInBack"`, `"EaseOutBack"`, `"EaseInOutBack"`, `"EaseInSpring"`, `"EaseOutSpring"`, `"EaseInOutSpring"` | | `playback/duration` | `Double` | `0.6` | The duration in seconds for which this block should be visible. | | `textAnimationOverlap` | `Float` | `0.35` | The overlap factor for text animations. | | `textAnimationWritingStyle` | `Enum` | `"Line"` | The writing style for text animations (e.g., by character, by word)., Possible values: `"Block"`, `"Line"`, `"Character"`, `"Word"` | --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "API Reference" description: "Find out how to use the API of the CESDK." platform: android url: "https://img.ly/docs/cesdk/android/api-reference/overview-8f24e1/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [API Reference](https://img.ly/docs/cesdk/android/api-reference/overview-8f24e1/) --- For Android, the following packages are available: - [ly.img:engine](`$\{props.platform.slug}/api-reference/engine/ly.img[58]engine/ly.img.engine`) - [ly.img:engine-camera](`$\{props.platform.slug}/api-reference/engine-camera/ly.img[58]engine-camera/ly.img.engine.camera`) - [ly.img:editor](`$\{props.platform.slug}/api-reference/editor/ly.img[58]editor/ly.img.editor`) - [ly.img:editor-core](`$\{props.platform.slug}/api-reference/editor-core`) - [ly.img:camera](`$\{props.platform.slug}/api-reference/camera/ly.img[58]camera/ly.img.camera`) - [ly.img:camera-core](`$\{props.platform.slug}/api-reference/camera-core/ly.img[58]camera-core/ly.img.camera.core`) --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Automate Workflows" description: "Automate repetitive editing tasks using CE.SDK’s headless APIs to generate assets at scale." platform: android url: "https://img.ly/docs/cesdk/android/automation-715209/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Automate Workflows](https://img.ly/docs/cesdk/android/automation-715209/) --- --- ## Related Pages - [Overview](https://img.ly/docs/cesdk/android/automation/overview-34d971/) - Automate repetitive editing tasks using CE.SDK’s headless APIs to generate assets at scale. - [Batch Processing](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/) - Documentation for Batch Processing - [Auto-Resize Blocks (Fill Parent & Percent Sizing) in Android (Kotlin)](https://img.ly/docs/cesdk/android/automation/auto-resize-4c2d58/) - Make blocks automatically fill their parent or resize proportionally using percent-based sizing in Android. Learn when to use absolute vs. percent sizing, and how to build predictable, responsive layouts for automation. - [Multiple Image Generation](https://img.ly/docs/cesdk/android/automation/multi-image-generation-2a0de4/) - Create many image variants from structured data by interpolating content into reusable design templates. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Auto-Resize Blocks (Fill Parent & Percent Sizing) in Android (Kotlin)" description: "Make blocks automatically fill their parent or resize proportionally using percent-based sizing in Android. Learn when to use absolute vs. percent sizing, and how to build predictable, responsive layouts for automation." platform: android url: "https://img.ly/docs/cesdk/android/automation/auto-resize-4c2d58/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Automate Workflows](https://img.ly/docs/cesdk/android/automation-715209/) > [Auto-Resize](https://img.ly/docs/cesdk/android/automation/auto-resize-4c2d58/) --- Sometimes you don't want to hard-code widths and heights. You want elements that *just fit*. You need a background that always covers the page, an overlay that scales with its container, or a column that takes up exactly half the parent. CE.SDK supports this through **size modes** and **percent-based sizing**, plus a one-liner convenience API that makes a block **fill its parent**. You set size modes per axis and use `0.0…1.0` values to express percentages; you can then query **computed dimensions** once layout stabilizes. ### Why It Matters for Automation When you generate designs in bulk, you can't manually correct layout differences for each image. **Percentage-based sizing** and `fillParent()` guarantee that every inserted asset or background automatically scales to the right dimensions, regardless of its original size or aspect ratio. This ensures **reliable layouts** and **predictable exports** in automated pipelines. ## What You'll Learn - Make a block **fill its parent** in one line. This is perfect for backgrounds and overlays. - Use **percent size modes** for responsive, predictable layouts. - Read **computed** width and height after layout to verify results. - Switch between **absolute** and **percent** sizing modes at runtime. - Build a **responsive background** that always fits the page. ## When You'll Use It - Full-bleed **background images** that cover the page. - **Responsive overlays** and watermarks that track the parent's size. - **Adaptive layouts** across different screen sizes and orientations. - **Automation workflows** that replace assets of different sizes without breaking layout consistency. ## Fill the Parent (One-Liner) The simplest way to auto-resize is to ask the engine to make a block fill its parent: ```kotlin import ly.img.engine.Engine // Make 'block' fill its parent container (resizes & positions). engine.block.fillParent(block) ``` CE.SDK resizes and positions the block, resets crop values if applicable, and switches content fill mode to `.cover` if needed to avoid invalid crops. **Good for:** Page backgrounds, edge-to-edge color panels, full-page masks. ## Percent-Based Sizing (Responsive Layouts) For finer control, switch size modes for width and height to `SizeMode.PERCENT`, then assign values from `0.0 ... 1.0`: ```kotlin import ly.img.engine.Engine import ly.img.engine.SizeMode // 100% width & height (fill parent) engine.block.setWidthMode(block, mode = SizeMode.PERCENT) engine.block.setHeightMode(block, mode = SizeMode.PERCENT) engine.block.setWidth(block, value = 1.0F) engine.block.setHeight(block, value = 1.0F) ``` In percent mode, `1.0` means 100% of the parent. Use: - `SizeMode.ABSOLUTE` for fixed-size elements. - `SizeMode.AUTO` when the content determines its own size. ## Partial Fill Examples ```kotlin import ly.img.engine.Engine import ly.img.engine.SizeMode // 50% width, 100% height (e.g., a left column) engine.block.setWidthMode(block, mode = SizeMode.PERCENT) engine.block.setHeightMode(block, mode = SizeMode.PERCENT) engine.block.setWidth(block, value = 0.5F) engine.block.setHeight(block, value = 1.0F) ``` Great for split layouts, sidebars, or variable-width panels. ## Reading Computed Dimensions After the engine completes a layout pass, you can read **computed** dimensions with the standard accessors: ```kotlin import ly.img.engine.Engine val width = engine.block.getWidth(block) val height = engine.block.getHeight(block) ``` These values are available **after** layout updates. If you query immediately after changes, you might see stale values. Use coroutines to yield or defer the read before querying. > **Note:** In Kotlin coroutines, using `yield()` or deferring the read to the next frame often suffices for demos. ## Switching Between Absolute & Percent You can toggle sizing modes at runtime to move between fixed and responsive layouts: ```kotlin import ly.img.engine.Engine import ly.img.engine.SizeMode // Fixed (absolute) sizing engine.block.setWidthMode(block, mode = SizeMode.ABSOLUTE) engine.block.setHeightMode(block, mode = SizeMode.ABSOLUTE) engine.block.setWidth(block, value = 400.0F) engine.block.setHeight(block, value = 300.0F) // Back to responsive sizing engine.block.setWidthMode(block, mode = SizeMode.PERCENT) engine.block.setHeightMode(block, mode = SizeMode.PERCENT) engine.block.setWidth(block, value = 0.75F) // 75% width engine.block.setHeight(block, value = 1.0F) // 100% height ``` Use **absolute** for fixed-size exports or print layouts, and **percent** for responsive layouts or template-based automation. ## Practical Example: Responsive Background Here's a common pattern. Create a background that always fills the page: ```kotlin import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode // 1) Create a graphic block and set its fill val bg = engine.block.create(DesignBlockType.Graphic) val shape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(bg, shape = shape) val solidColor = engine.block.createFill(FillType.Color) val rgbaGreen = Color.fromRGBA(r = 0.5F, g = 1.0F, b = 0.5F, a = 1.0F) engine.block.setColor(solidColor, property = "fill/color/value", color = rgbaGreen) engine.block.setFill(bg, fill = solidColor) // 2) Append to the page and send behind other content engine.block.appendChild(parent = page, child = bg) engine.block.sendToBack(bg) // 3) Either the one-liner: engine.block.fillParent(bg) // 4) Or, percent-based equivalent: engine.block.setWidthMode(bg, mode = SizeMode.PERCENT) engine.block.setHeightMode(bg, mode = SizeMode.PERCENT) engine.block.setWidth(bg, value = 1.0F) engine.block.setHeight(bg, value = 1.0F) ``` The percent-based alternative mirrors the behavior of `fillParent` if your content and crop are already valid. The `fillParent` method guarantees coverage and sets a safe fill mode automatically. ## Automation Example: Batch Image Replacement This automation scenario generates name tags: - Each tag has a dedicated container that: - Is called hero-frame. - Displays the person's photo. - The code prepares hero-frame **once** so it always fills its parent. - It replaces the image fill inside hero-frame during batch generation. - Layout stays stable regardless of the photo's size and aspect. ### Prepare Once ```kotlin import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType // One‑time setup when building the template/scene val heroFrame = engine.block.create(DesignBlockType.Graphic) val rect = engine.block.createShape(ShapeType.Rect) engine.block.setShape(heroFrame, shape = rect) // Give it a name for easy lookup in later steps / debugging engine.block.setString(heroFrame, property = "name", value = "hero-frame") // Attach an image fill now (can be a placeholder) val heroFrameImageFill = engine.block.createFill(FillType.Image) engine.block.setFill(heroFrame, fill = heroFrameImageFill) // Place it in the nametag layout once (e.g., inside a card group or page) engine.block.appendChild(parent = faceGroup, child = heroFrame) // Make the container auto‑resize to its parent so the photo always fits engine.block.fillParent(heroFrame) ``` ### Update During the Batch At a later time, when the batch runs, it: - Gets a reference to the `heroFrame` block - Calls a function to update the image fill during each pass. ```kotlin import ly.img.engine.Engine import ly.img.engine.FillType fun replaceHeroPhotoURL(engine: Engine, hero: Int, url: String) { try { // Try to get the current fill and swap the image on the same object val fill = engine.block.getFill(hero) engine.block.setString( fill, property = "fill/image/imageFileURI", value = url ) } catch (e: Exception) { // If no fill exists yet, create and attach one println("No fill found on hero-frame; creating new fill.") try { val newFill = engine.block.createFill(FillType.Image) engine.block.setString( newFill, property = "fill/image/imageFileURI", value = url ) engine.block.setFill(hero, fill = newFill) } catch (e: Exception) { println("Failed to attach new fill: ${e.message}") } } } ``` Because `heroFrame` used `fillParent`, every image conforms to its parent's size, ensuring layouts remain consistent. This approach is ideal for: - Product catalogs - User-generated templates - Any workflow where image dimensions vary widely. ![Example of batch image replacement for nametags](assets/resize-example-ios-160-0.png) In the preceding diagram, three input images are of **different sizes**. The engine **crops and resizes** them to fill the placeholder during the batch. ## Complete Example Here's a complete example demonstrating auto-resize in a template-based workflow: ```kotlin import android.content.Context import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode fun autoResizeExample( context: Context, license: String, userId: String ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.autoresize") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) try { // Create scene and page val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1080F) engine.block.setHeight(page, value = 1920F) // Create a full-bleed background using fillParent val background = engine.block.create(DesignBlockType.Graphic) val bgShape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(background, shape = bgShape) val bgFill = engine.block.createFill(FillType.Color) val bgColor = Color.fromRGBA(r = 0.2F, g = 0.4F, b = 0.8F, a = 1.0F) engine.block.setColor(bgFill, property = "fill/color/value", color = bgColor) engine.block.setFill(background, fill = bgFill) engine.block.appendChild(parent = page, child = background) engine.block.sendToBack(background) engine.block.fillParent(background) // Create a 50% width left column using percent sizing val leftColumn = engine.block.create(DesignBlockType.Graphic) val leftShape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(leftColumn, shape = leftShape) val leftFill = engine.block.createFill(FillType.Color) val leftColor = Color.fromRGBA(r = 1.0F, g = 0.8F, b = 0.2F, a = 0.8F) engine.block.setColor(leftFill, property = "fill/color/value", color = leftColor) engine.block.setFill(leftColumn, fill = leftFill) engine.block.appendChild(parent = page, child = leftColumn) // Set to 50% width, 100% height engine.block.setWidthMode(leftColumn, mode = SizeMode.PERCENT) engine.block.setHeightMode(leftColumn, mode = SizeMode.PERCENT) engine.block.setWidth(leftColumn, value = 0.5F) engine.block.setHeight(leftColumn, value = 1.0F) engine.block.setPositionX(leftColumn, value = 0F) engine.block.setPositionY(leftColumn, value = 0F) // Create a 50% width right column val rightColumn = engine.block.create(DesignBlockType.Graphic) val rightShape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(rightColumn, shape = rightShape) val rightFill = engine.block.createFill(FillType.Color) val rightColor = Color.fromRGBA(r = 0.2F, g = 1.0F, b = 0.4F, a = 0.8F) engine.block.setColor(rightFill, property = "fill/color/value", color = rightColor) engine.block.setFill(rightColumn, fill = rightFill) engine.block.appendChild(parent = page, child = rightColumn) // Set to 50% width, 100% height, positioned on the right engine.block.setWidthMode(rightColumn, mode = SizeMode.PERCENT) engine.block.setHeightMode(rightColumn, mode = SizeMode.PERCENT) engine.block.setWidth(rightColumn, value = 0.5F) engine.block.setHeight(rightColumn, value = 1.0F) engine.block.setPositionX(rightColumn, value = 540F) // 50% of 1080 engine.block.setPositionY(rightColumn, value = 0F) // Read computed dimensions val leftWidth = engine.block.getWidth(leftColumn) val leftHeight = engine.block.getHeight(leftColumn) val rightWidth = engine.block.getWidth(rightColumn) val rightHeight = engine.block.getHeight(rightColumn) println("Left column: ${leftWidth}x${leftHeight}") println("Right column: ${rightWidth}x${rightHeight}") // Switch left column to absolute sizing engine.block.setWidthMode(leftColumn, mode = SizeMode.ABSOLUTE) engine.block.setHeightMode(leftColumn, mode = SizeMode.ABSOLUTE) engine.block.setWidth(leftColumn, value = 400F) engine.block.setHeight(leftColumn, value = 800F) println("Left column after switching to absolute: ${engine.block.getWidth(leftColumn)}x${engine.block.getHeight(leftColumn)}") } finally { // Note: Don't stop the engine here if you want to keep using it // engine.stop() } } ``` ## Platform Notes & Limitations - **Computed dimensions are asynchronous.** Read them after a layout cycle. If you set percent sizing and immediately call `getWidth`, you may get the previous value. - **Groups vs. children.** Percent sizing relates a child to *its direct parent*. Ensure your block is appended where you expect before reading dimensions. - **Fills and crops.** `fillParent` may reset crop values and/or set fill mode to cover to avoid invalid states. ## Troubleshooting | Symptom | Likely Cause | Fix | |---|---|---| | Block doesn't resize with parent | Width/height modes aren't set to `SizeMode.PERCENT` | Set `setWidthMode(SizeMode.PERCENT)` / `setHeightMode(SizeMode.PERCENT)` and assign `0…1` values. | | Computed width/height are `0` or stale | Reading before layout settled | Defer reads; yield before `getWidth/Height`. | | Only one axis resizes | Only one axis set to `SizeMode.PERCENT` | Set both axes to `SizeMode.PERCENT` (or use `fillParent`). | | Unexpected crop after fill | `fillParent` adjusted crop/fill mode | Use percent sizing manually if you need to preserve a crop. | | Child ignores parent size | Wrong parent in hierarchy | Verify `appendChild` target and recheck computed dimensions after update. | ## Next Steps You can use auto-resize as you create compositions and make responsive designs. Here are some other guides to explore to deepen your understanding. - [Resize blocks (manual)](https://img.ly/docs/cesdk/android/edit-image/transform/resize-407242/) — control dimensions interactively. - [Batch Processing](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/) — automate design generation at scale. - [Multi-Image Generation](https://img.ly/docs/cesdk/android/automation/multi-image-generation-2a0de4/) — generate multiple variants from templates. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Batch Processing" description: "Documentation for Batch Processing" platform: android url: "https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Automate Workflows](https://img.ly/docs/cesdk/android/automation-715209/) > [Batch Processing](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/) --- Batch processing lets your app automatically generate scores of assets from a single design template. For example, you might create 100 personalized posters or social posts from a JSON file of names and photos, without opening the editor for each one. CE.SDK's headless engine makes this possible entirely in Kotlin. This guide shows you how to do that in Kotlin for Android. You'll learn how to load a saved design, substitute text and images, and export each variation as an asset file. The same techniques apply to more complex outputs like PDFs or videos. ## What You'll Learn - How to start CE.SDK's **headless engine** without a UI editor. - How to **load a template** from an archive or URL and attach it to a new scene. - How to **replace variables and images** for each record in your data. - How to **export** each generated design as a common format like PNG, JPEG or PDF. ## When You'll Use This Headless batch generation is ideal for tasks that need automation, not user interaction. Use it to mass-produce: - Branded materials - Social media graphics - Dynamic thumbnails - Personalized certificates - Product cards at scale Because you're not displaying the editor UI, it works well for background processing and server-side workflows. ## Headless Engine At the center of CE.SDK is the `Engine`, a lightweight rendering system you can use without the prebuilt editors. It can run in the background, respond to coroutines, and render scenes directly to image data. ```kotlin import ly.img.engine.Engine import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch fun startHeadlessEngine(license: String, userId: String) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.batch") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) } ``` For automation, you'll typically create one `Engine` instance for the full batch run. - **On mobile**, a single-engine, sequential approach is safest. - **On more powerful hardware or servers**, you can explore modest parallelism, as each instance of `Engine` is independent. ## Loading Templates The template defines the design you'll use for all generated images. You can: 1. Create a template in the CE.SDK editor. 2. Save it as an archive or scene file. 3. Bundle that file with your app in the assets folder or host it at a URL for the batch to use. ```kotlin import android.net.Uri val templateUri = Uri.parse("file:///android_asset/templates/badge_template.scene") ``` **Archives** are self-contained ZIP files that include: - Your layout - Text - All linked assets They're ideal for predictable batch exports. You can also save templates as scene JSON files, but in those cases, the URI of every asset must resolve correctly at runtime. Once loaded, always validate the structure before using it. ```kotlin import android.net.Uri import ly.img.engine.DesignBlock import ly.img.engine.Engine suspend fun loadTemplate(engine: Engine, uri: Uri): DesignBlock { val scene = engine.scene.load(sceneUri = uri) return scene } ``` This ensures that missing or corrupt templates don't interrupt your batch. `engine.scene.load()` loads the template and returns the scene root block, which you can then render, modify, and export. ## Supplying Data from JSON Every batch needs a list of records. Each record holds the values to apply to the template. A common pattern is: 1. Store them as a JSON array. 2. Decode them during the batch. A record might have these properties: ```kotlin import kotlinx.serialization.Serializable @Serializable data class Record( val id: String, val variables: Map, val outputFileName: String, val images: Map? = null // optional blockName → image URI ) ``` Then decode any JSON using kotlinx.serialization or Gson: ```kotlin import android.content.Context import kotlinx.serialization.json.Json import kotlinx.serialization.decodeFromString import java.io.IOException fun loadRecords(context: Context): List { return try { val jsonString = context.assets.open("records.json") .bufferedReader() .use { it.readText() } Json.decodeFromString>(jsonString) } catch (e: IOException) { emptyList() } } ``` Example `records.json`: ```json [ { "id": "001", "variables": { "name": "Ruth", "tagline": "Ship great apps" }, "outputFileName": "badge-ruth" }, { "id": "002", "variables": { "name": "Chris", "tagline": "Move fast, polish later" }, "outputFileName": "badge-chris" } ] ``` In a production environment, you'll load data from an API or database instead of bundled assets. If your dataset is large, consider streaming it in chunks instead of loading everything at once. ## Templates and Variables Templates often include placeholders, or variables, that you can update with real data at runtime. In CE.SDK (Android), template variables follow a key/value pattern and are **always stored as strings**. Your app can convert them into types like numbers or colors when needed. For text blocks, CE.SDK automatically matches placeholders in the template with variable names. Displaying `\{\{username\}\}` as the text in a text box becomes the variable `username` you can replace with a person's name before exporting. ```kotlin import ly.img.engine.Engine // All variables are set via (key:String, value:String) engine.variable.set(key = "name", value = "Chris") // text engine.variable.set(key = "price", value = "9.99") // number encoded as string engine.variable.set(key = "brandColor", value = "#FFD60A") // color as hex string engine.variable.set(key = "isFeatured", value = "true") // boolean as "true" / "false" engine.variable.set(key = "imageURL", value = "https://example.com/image.jpg") // URL as string ``` Discover the available variable keys at runtime to validate a template using: ```kotlin val keys = engine.variable.findAll() // assert or log missing keys before a long batch run ``` ## Applying Data to the Template Once the engine loads the template, you can fill in variables. These correspond to the placeholders you set in your CE.SDK scene, like `\{\{name\}\}` or `\{\{tagline\}\}`. ```kotlin import ly.img.engine.Engine fun applyVariables(engine: Engine, values: Map) { for ((key, value) in values) { engine.variable.set(key = key, value = value) } } ``` You can also swap out placeholder images at runtime. The simplest method is to find the block by its name and update its image fill. ```kotlin import ly.img.engine.Engine fun replaceNamedImage(engine: Engine, blockName: String, imageUri: String) { val matches = engine.block.findByName(blockName) if (matches.isNotEmpty()) { val imageBlock = matches.first() val fill = engine.block.getFill(imageBlock) engine.block.setString(fill, property = "fill/image/imageFileURI", value = imageUri) engine.block.setFill(imageBlock, fill = fill) engine.block.setKind(imageBlock, kind = "image") } } ``` This snippet looks up a block named `productImage` and replaces its image fill with the URI of the new image. > **Note:** Using block names keeps your automation readable and less fragile than referencing IDs. ## Create Thumbnails You can generate previews by exporting a scaled version of each result: ```kotlin import android.content.Context import kotlinx.coroutines.withContext import kotlinx.coroutines.Dispatchers import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import java.io.File suspend fun exportThumbnail( engine: Engine, context: Context, fileName: String, scale: Float = 0.25f ): File { val scene = requireNotNull(engine.scene.get()) { "No scene loaded" } val width = engine.block.getFrameWidth(scene) * scale val height = engine.block.getFrameHeight(scene) * scale val options = ExportOptions( jpegQuality = 0.7f, targetWidth = width, targetHeight = height ) val exportData = engine.block.export(scene, mimeType = MimeType.JPEG, options = options) val outputDir = context.filesDir val thumbFile = File(outputDir, "thumb_$fileName.jpg") withContext(Dispatchers.IO) { thumbFile.outputStream().channel.use { channel -> channel.write(exportData) } } return thumbFile } ``` ## Exporting to Multiple Formats Exports can target different output types. Just switch the mime type you pass: ```kotlin import ly.img.engine.ExportOptions import ly.img.engine.MimeType val pngData = engine.block.export(scene, mimeType = MimeType.PNG, options = ExportOptions(targetHeight = 1080f)) val pdfData = engine.block.export(scene, mimeType = MimeType.PDF) ``` |Format|MimeType|Typical Use| |---|---|---| |PNG|`MimeType.PNG`|Lossless images with transparency| |JPEG|`MimeType.JPEG`|Photos and smaller files| |PDF|`MimeType.PDF`|Printable designs| |MP4|`MimeType.MP4`|Animated or timed templates| Use an `ExportOptions` instance to tune output quality, size and other properties of the export. You can get the details in the [Export](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) guides. If you need multiple formats at once, run several export calls back-to-back using the same engine and scene. ## Managing Memory and Resources Each export involves GPU textures, image buffers, and temporary files. To keep your app responsive: - Reuse a single engine for sequential jobs. - Clean up temporary directories between batches. - Call `engine.stop()` when completely done to free resources. ## Performance Tuning Checklist - Use JPEG quality 0.8–0.9 to balance file size and speed. - Keep templates simple. Avoid unnecessary effects or large images. - Chunk data into smaller groups for large datasets. - Limit concurrency to 2–3 parallel tasks if attempting parallel processing. - Profile on the lowest device you support. ## Error Handling and Retries Batch jobs can fail for network hiccups or invalid data. Use Kotlin's try/catch blocks to retry a few times before giving up. ```kotlin import kotlinx.coroutines.delay suspend fun processRecordWithRetry(record: Record, maxAttempts: Int = 3) { var attempts = 0 while (attempts < maxAttempts) { try { exportRecord(record) break } catch (e: Exception) { attempts++ if (attempts >= maxAttempts) { throw e } delay((attempts * 500L)) // exponential backoff } } } ``` You can also log each attempt for easier debugging. ## Logging and Monitoring Progress Adding logging helps track how long each export takes: ```kotlin import android.util.Log const val TAG = "BatchProcessing" Log.i(TAG, "Starting batch processing for ${records.size} records") records.forEachIndexed { index, record -> val startTime = System.currentTimeMillis() try { processRecord(record) val duration = System.currentTimeMillis() - startTime Log.i(TAG, "Exported ${record.outputFileName} in ${duration}ms [${index + 1}/${records.size}]") } catch (e: Exception) { Log.e(TAG, "Failed to export ${record.outputFileName}", e) } } ``` Wrap your entire run in timestamps to measure throughput and display progress in your UI. ## Batch Workflow Batch processing isn't limited to mobile apps. The same logic can run on backends or web services using CE.SDK for Web or Node. If your workload scales beyond device limits, consider: 1. Migrating automation to a server workflow. 2. Sending results back to the app. An example batch process, below, calls `processRecord()` for each record in the dataset. The record is processed by: 1. Loading the template 2. Setting variables 3. Replacing images 4. Exporting the result ```kotlin import android.content.Context import android.net.Uri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.DesignBlock import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import java.io.File suspend fun processRecord( engine: Engine, context: Context, record: Record, templateUri: Uri ): File { // Load the template val scene = engine.scene.load(sceneUri = templateUri) // Apply variables applyVariables(engine, record.variables) // Replace images if specified record.images?.forEach { (blockName, imageUri) -> replaceNamedImage(engine, blockName, imageUri) } // Export the result val exportData = engine.block.export( scene, mimeType = MimeType.JPEG, options = ExportOptions(jpegQuality = 0.9f) ) // Save to file val outputDir = context.filesDir val outputFile = File(outputDir, "${record.outputFileName}.jpg") withContext(Dispatchers.IO) { outputFile.outputStream().channel.use { channel -> channel.write(exportData) } } return outputFile } suspend fun runBatch( context: Context, license: String, userId: String, records: List ) { val engine = Engine.getInstance(id = "ly.img.engine.batch") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val templateUri = Uri.parse("file:///android_asset/templates/badge_template.scene") for (record in records) { try { processRecord(engine, context, record, templateUri) } catch (e: Exception) { Log.e("Batch", "Failed to process ${record.id}", e) } } engine.stop() } ``` Use modest parallelism for faster processing on capable devices: ```kotlin import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope suspend fun runBatchParallel( context: Context, license: String, userId: String, records: List, maxConcurrent: Int = 3 ) = coroutineScope { val templateUri = Uri.parse("file:///android_asset/templates/badge_template.scene") records.chunked(maxConcurrent).forEach { chunk -> chunk.map { record -> async(Dispatchers.Main) { // Create a separate engine instance for each parallel task val engine = Engine.getInstance(id = "ly.img.engine.batch.${record.id}") try { engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) processRecord(engine, context, record, templateUri) } finally { engine.stop() } } }.awaitAll() } } ``` ## Troubleshooting **❌ Your exports appear blank**: - Verify that the scene loaded successfully with `engine.scene.get()`. - Check that all asset URIs are reachable (network or local). - Ensure the page has content before exporting. **❌ Text variables don't update**: - Confirm variable names match the template's tokens exactly (case-sensitive). - Use `engine.variable.findAll()` to see what variables exist in the template. - Verify that `engine.variable.set()` is called with the correct key. **❌ Your image placeholder doesn't update**: - Ensure you're setting the image URI on an image fill. - Verify that the fill is applied to the target block with `engine.block.setFill()`. - Check that the URI is valid and reachable (add INTERNET permission for remote URLs). - Confirm the block's kind is set to `"image"` after applying the new fill. **❌ The batch job becomes sluggish**: - Performance issues are rare in sequential runs, but if you attempt parallel exports: - Limit concurrency to a few simultaneous tasks (2-3 on mobile). - Ensure each engine instance is properly stopped after use. - Monitor memory usage and reduce batch size if needed. **❌ Network errors when loading remote templates or images**: - Add `` to AndroidManifest.xml. - Verify URLs are using HTTPS. - Test URLs in a browser to confirm they're accessible. ## Next Steps Continue learning about automation and export workflows with these related guides: - Use Templates to [generate content](https://img.ly/docs/cesdk/android/use-templates/generate-334e15/). - [Text Variables](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/text-variables-7ecb50/) & [Placeholders](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/placeholders-d9ba8a/) for dynamic content. - [Export assets](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) in different formats. - Generate [multiple assets](https://img.ly/docs/cesdk/android/automation/multi-image-generation-2a0de4/) from a single record. - Create [Preview Thumbnails](https://img.ly/docs/cesdk/android/export-save-publish/create-thumbnail-749be1/). These guides expand on how to prepare templates, manage variable data, and optimize export pipelines for larger-scale automation. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Multiple Image Generation" description: "Create many image variants from structured data by interpolating content into reusable design templates." platform: android url: "https://img.ly/docs/cesdk/android/automation/multi-image-generation-2a0de4/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Automate Workflows](https://img.ly/docs/cesdk/android/automation-715209/) > [Multiple Image Generation](https://img.ly/docs/cesdk/android/automation/multi-image-generation-2a0de4/) --- Generate image variants, such as square, portrait, or landscape layouts, from a single data record using the CreativeEditor SDK's Engine API. This pattern lets you populate templates programmatically with text, images, and colors to create consistent, on-brand designs across all formats. ## What You'll Learn - Load multiple templates into CE.SDK and populate them with structured data. - Replace text and image placeholders dynamically using variables and named blocks. - Apply consistent brand color themes across scenes. - Export each variant as PNG, JPEG, or PDF. - Build efficient workflows for generating multiple format variations. ## When to Use It Use multi-image generation when a single record (like a restaurant listing or product) needs to produce multiple layout variants. For larger datasets with many records generating many images, refer to the [Batch Processing](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/) guide. ## Core Concepts **Templates and Instances**: Templates define reusable layout and placeholders. An instance is a populated version with specific data. Use `engine.scene.saveToString()` to serialize a template and `engine.scene.load(scene =)` to load it for processing. **Variables for Dynamic Text**: Define variables in your templates for fields like `RestaurantName` or `Rating`. Set them at runtime with `engine.variable.set(key =, value =)`. Use `engine.variable.findAll()` to verify available variable names. **Named Blocks for Image Replacement**: Name your image placeholders (for example, `RestaurantImage`, `Logo`). Retrieve them with `engine.block.findByName()`, access the fill with `getFill()`, then update its source URI using `setString(..., property = "fill/image/imageFileURI")`. Always reset the crop after replacing an image fill for proper framing. **Brand and Conditional Styling**: Use predictable block naming for elements such as star ratings. Apply color changes programmatically with `setColor` to visualize rating or brand status. **Sequential Template Processing**: Process each variant one at a time to reduce memory pressure and simplify export tracking. ## Prerequisites - CE.SDK for Android integrated through Gradle. - A valid license key. - Templates saved as `.scene` files in assets or available via URLs. - Template variables and named blocks prepared for population. ## Initialize the Engine ```kotlin import ly.img.engine.Engine import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch fun makeEngine(license: String, userId: String) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.multiimage") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) engine.addDefaultAssetSources() } ``` ## Define Your Data Model Your data model can use proper typing for variables. When you insert values into the templates, you will often need to convert them to strings. ```kotlin import java.util.UUID data class Restaurant( val id: UUID = UUID.randomUUID(), val name: String, val rating: Double, val reviewCount: Int, val imageURL: String, val logoURL: String, val brandPrimary: String, val brandSecondary: String ) ``` This model provides a data record for the example code below. ## Populate Templates and Export Variants Use one template per format such as: - square - portrait - landscape Populate the templates sequentially. ```kotlin import android.content.Context import android.net.Uri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.MimeType import java.io.File suspend fun generateVariants( engine: Engine, context: Context, restaurant: Restaurant ): List { val templates = listOf( "restaurant_square.scene", "restaurant_portrait.scene", "restaurant_landscape.scene" ) val results = mutableListOf() for (template in templates) { val templateUri = Uri.parse("file:///android_asset/templates/$template") val scene = engine.scene.load(sceneUri = templateUri) // Set text variables engine.variable.set(key = "RestaurantName", value = restaurant.name) engine.variable.set(key = "Rating", value = String.format("%.1f ★", restaurant.rating)) engine.variable.set(key = "ReviewCount", value = "${restaurant.reviewCount}") // Replace images replaceImage(engine, name = "RestaurantImage", uri = restaurant.imageURL) replaceImage(engine, name = "Logo", uri = restaurant.logoURL) // Apply brand theme applyBrandTheme( engine = engine, primary = parseColor(restaurant.brandPrimary), secondary = parseColor(restaurant.brandSecondary) ) // Export variant val output = exportJPEG(engine, context, outputName(restaurant, template)) results.add(output) } return results } fun outputName(restaurant: Restaurant, template: String): String { val format = template.substringAfter("restaurant_").substringBefore(".scene") return "${restaurant.name.replace(" ", "_")}_$format" } ``` **Helper Functions**: The preceding code example uses some helper functions. These aren't part of the CE.SDK. Possible implementations of the functions follow. ```kotlin import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.MimeType import java.io.File fun replaceImage(engine: Engine, name: String, uri: String) { val matches = engine.block.findByName(name) if (matches.isNotEmpty()) { val block = matches.first() val fill = engine.block.getFill(block) engine.block.setString(fill, property = "fill/image/imageFileURI", value = uri) engine.block.resetCrop(block) } } fun applyBrandTheme(engine: Engine, primary: Color, secondary: Color) { val allBlocks = engine.block.findAll() for (block in allBlocks) { when (engine.block.getType(block)) { "//ly.img.ubq/text" -> { engine.block.setTextColor(block, color = primary) } "//ly.img.ubq/graphic" -> { runCatching { val fill = engine.block.getFill(block) engine.block.setColor(fill, property = "fill/color/value", color = secondary) } } } } } suspend fun exportJPEG(engine: Engine, context: Context, name: String): File { val page = engine.block.findByType(DesignBlockType.Page).firstOrNull() ?: throw IllegalStateException("No page found") val data = engine.block.export(page, mimeType = MimeType.JPEG) val dir = context.filesDir val file = File(dir, "$name.jpg") withContext(Dispatchers.IO) { file.outputStream().channel.use { channel -> channel.write(data) } } return file } ``` ## Color Utility Add this helper to convert hex strings into CE.SDK `Color` values. ```kotlin import ly.img.engine.Color fun parseColor(hex: String): Color { var hexString = hex.trim().removePrefix("#") // Add alpha if missing if (hexString.length == 6) { hexString += "FF" } val hexValue = hexString.toLongOrNull(16) ?: 0L val r = ((hexValue and 0xFF000000) shr 24).toFloat() / 255f val g = ((hexValue and 0x00FF0000) shr 16).toFloat() / 255f val b = ((hexValue and 0x0000FF00) shr 8).toFloat() / 255f val a = (hexValue and 0x000000FF).toFloat() / 255f return Color(r = r, g = g, b = b, a = a) } ``` ## Preview the Generated Variants Use Jetpack Compose to display and share generated images. ```kotlin import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import coil.compose.rememberAsyncImagePainter import java.io.File @Composable fun VariantsGrid(files: List) { var selectedFile by remember { mutableStateOf(null) } LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 160.dp), contentPadding = PaddingValues(12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { items(files) { file -> Card( modifier = Modifier .aspectRatio(1f) .shadow(elevation = 2.dp, shape = RoundedCornerShape(10.dp)) .clickable { selectedFile = file }, shape = RoundedCornerShape(10.dp) ) { Image( painter = rememberAsyncImagePainter(file), contentDescription = file.name, contentScale = ContentScale.Crop, modifier = Modifier.fillMaxSize() ) } } } // Handle share dialog for selectedFile if needed } ``` ## Advanced Use Cases **Conditional Content**: Show or hide elements based on data values—for example, color stars according to the rating. ```kotlin import ly.img.engine.Color import ly.img.engine.Engine fun colorStars(engine: Engine, rating: Int, baseName: String = "Rating") { for (index in 1..5) { val starBlocks = engine.block.findByName("$baseName$index") if (starBlocks.isEmpty()) continue val star = starBlocks.first() runCatching { val fill = engine.block.getFill(star) val color = if (index <= rating) { parseColor("#FFD60A") } else { parseColor("#CCCCCC") } engine.block.setColor(fill, property = "fill/color/value", color = color) } } } ``` **Custom Assets**: Add your own logos or fonts by registering a custom asset source. See the [Custom Asset Sources](https://img.ly/docs/cesdk/android/import-media/asset-library-65d6c4/) guide for setup examples. **Adopter Mode Editing**: Allow users to open the generated design in the editor UI for minor edits. Serialize the populated scene with `engine.scene.saveToString()` and load it into the Design Editor configured for [restricted content](https://img.ly/docs/cesdk/android/create-templates/lock-131489/) editing. ## Troubleshooting **❌ Variables not updating**: - Verify variable names in both template and code using `engine.variable.findAll()`. - Variable names are case-sensitive. - Ensure `engine.variable.set()` is called with the correct key. **❌ Images missing**: - Confirm local path or remote URL points to a valid image. - For remote images, add `` to AndroidManifest.xml. - Verify CORS settings for remote images. **❌ Colors incorrect**: - Check block type before applying color with `engine.block.getType()`. - Ensure color values are in range 0-1 (not 0-255). - Use `runCatching` to handle blocks that don't support fills. **❌ Memory spikes**: - Process templates sequentially, not in parallel. - Call `engine.stop()` when completely done. - Clean up temporary files after export. **❌ Export size unexpected**: - Confirm consistent page dimensions across templates. - Verify `engine.block.getFrameWidth()` and `engine.block.getFrameHeight()` values. - Check template design settings. **Debugging Tips**: - Print variable names using `engine.variable.findAll()` - Log block names with `engine.block.getName(id)` - Test with one minimal template before expanding - Use Android Logcat to track processing flow ## Next Steps Multi-image generation is one way to automate your workflow. Some other ways the CE.SDK can automate are in these guides: - [Batch Processing](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/) lets you process many data records at once. - Adapt layouts across aspect ratios using [auto resize](https://img.ly/docs/cesdk/android/automation/auto-resize-4c2d58/). - Explore [export formats](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) and settings. - Add branded fonts, logos, and graphics by creating [custom asset sources](https://img.ly/docs/cesdk/android/import-media/overview-84bb23/). --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Overview" description: "Automate repetitive editing tasks using CE.SDK’s headless APIs to generate assets at scale." platform: android url: "https://img.ly/docs/cesdk/android/automation/overview-34d971/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Automate Workflows](https://img.ly/docs/cesdk/android/automation-715209/) > [Overview](https://img.ly/docs/cesdk/android/automation/overview-34d971/) --- Workflow automation with CreativeEditor SDK (CE.SDK) enables you to programmatically generate, manipulate, and export creative assets—at scale. Whether you're creating thousands of localized ads, preparing platform-specific variants of a campaign, or populating print-ready templates with dynamic data, CE.SDK provides a flexible foundation for automation. You can run automation entirely on the client, integrate it with your backend, or build hybrid “human-in-the-loop” workflows where users interact with partially automated scenes before export. The automation engine supports static pipelines, making it suitable for a wide range of publishing, e-commerce, and marketing applications. Video support will follow soon. [Explore Demos](https://img.ly/showcases/cesdk?tags=android) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) ### Output Formats --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Bundle Size" description: "Understand CE.SDK’s engine and editor bundle sizes and how they affect your mobile app’s download footprint." platform: android url: "https://img.ly/docs/cesdk/android/bundle-size-df9210/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Compatibility & Security](https://img.ly/docs/cesdk/android/compatibility-fef719/) > [Bundle Size](https://img.ly/docs/cesdk/android/bundle-size-df9210/) --- ## Engine Download Size When included in your app, the download size of the engine is different depending on the architecture: - arm64-v8a ~ 14.9MB - armeabi-v7a ~ 13.7MB - x86\_64 ~ 14.9MB - x86 ~ 14.9MB This means that the download size from Play Store will be increased by the amount mentioned above. ## Mobile Editor Download Size In order to use the mobile editor, you either have to use the gradle dependency, or directly copy the solutions from our [repository](https://github.com/imgly/cesdk-android-examples). No matter which approach you choose, you can expect the download size of the mobile editor to be around the size of the engine plus a few additional megabytes. The precise size may depend on the bundled assets (scene files, images, stickers), however, with the default resources you can expect it to be around **3 MB** plus the size of the engine. Note that this does not include the size of the Jetpack Compose library. Also note that this is measured without R8 optimizations. Enabling it in your project will shrink it further. For more information on R8, follow this [link](https://developer.android.com/build/shrink-code). ## Including as a Dynamic Feature If you want to include the engine or the mobile editor via dependency as a dynamic feature, create an android module, add the dependency of the engine/mobile editor to that module and make that module dynamic. Here is the [link](https://developer.android.com/guide/playcore/feature-delivery) on how to create a dynamic module and load it. If you want to include the mobile editor by copying our repository, you do not need to create an extra module. Simply declare the `:editor` module as dynamic when you copy that module to your project. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Colors" description: "Manage color usage in your designs, from applying brand palettes to handling print and screen formats." platform: android url: "https://img.ly/docs/cesdk/android/colors-a9b79c/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) --- --- ## Related Pages - [Overview](https://img.ly/docs/cesdk/android/colors/overview-16a177/) - Manage color usage in your designs, from applying brand palettes to handling print and screen formats. - [Basics](https://img.ly/docs/cesdk/android/colors/basics-307115/) - Learn how color is handled in CE.SDK and how to apply, modify, and manage it across elements. - [For Print](https://img.ly/docs/cesdk/android/colors/for-print-59bc05/) - Use print-ready color models and settings for professional-quality, production-ready exports. - [For Screen](https://img.ly/docs/cesdk/android/colors/for-screen-1911f8/) - Documentation for For Screen - [Apply Colors](https://img.ly/docs/cesdk/android/colors/apply-2211e3/) - Apply solid colors to shapes, backgrounds, and other design elements. - [Create a Color Palette](https://img.ly/docs/cesdk/android/colors/create-color-palette-7012e0/) - Build reusable color palettes to maintain consistency and streamline user choices. - [Color Conversion](https://img.ly/docs/cesdk/android/colors/conversion-bcd82b/) - Convert between RGB, CMYK, and other color formats based on your project’s output requirements. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Apply Colors" description: "Apply solid colors to shapes, backgrounds, and other design elements." platform: android url: "https://img.ly/docs/cesdk/android/colors/apply-2211e3/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [Apply Color](https://img.ly/docs/cesdk/android/colors/apply-2211e3/) --- ```kotlin file=@cesdk_android_examples/engine-guides-colors/Colors.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.ColorSpace import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun colors( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 350F) engine.block.setPositionY(block, value = 400F) engine.block.setWidth(block, value = 100F) engine.block.setHeight(block, value = 100F) val fill = engine.block.createFill(FillType.Color) engine.block.setFill(block, fill = fill) val rgbaBlue = Color.fromRGBA(r = 0F, g = 0F, b = 1F, a = 1F) val cmykRed = Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F, tint = 1F) val cmykPartialRed = Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F, tint = 0.5F) engine.editor.setSpotColor( name = "Pink-Flamingo", Color.fromRGBA(r = 0.988F, g = 0.455F, b = 0.992F), ) engine.editor.setSpotColor(name = "Yellow", Color.fromCMYK(c = 0F, m = 0F, y = 1F, k = 0F)) val spotPinkFlamingo = Color.fromSpotColor( name = "Pink-Flamingo", tint = 1F, externalReference = "Crayola", ) val spotPartialYellow = Color.fromSpotColor(name = "Yellow", tint = 0.3F) engine.block.setColor(fill, property = "fill/color/value", value = rgbaBlue) engine.block.setColor(fill, property = "fill/color/value", value = cmykRed) engine.block.setColor(block, property = "stroke/color", value = cmykPartialRed) engine.block.setColor(fill, property = "fill/color/value", value = spotPinkFlamingo) engine.block.setColor(block, property = "dropShadow/color", value = spotPartialYellow) val cmykBlueConverted = engine.editor.convertColorToColorSpace( rgbaBlue, colorSpace = ColorSpace.CMYK, ) val rgbaPinkFlamingoConverted = engine.editor.convertColorToColorSpace( spotPinkFlamingo, colorSpace = ColorSpace.SRGB, ) engine.editor.findAllSpotColors() // ["Crayola-Pink-Flamingo", "Yellow"] engine.editor.setSpotColor("Yellow", Color.fromCMYK(c = 0.2F, m = 0F, y = 1F, k = 0F)) engine.editor.removeSpotColor("Yellow") engine.stop() } ``` ## Setup the scene We first create a new scene with a graphic block that has color fill. ```kotlin highlight-setup val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 350F) engine.block.setPositionY(block, value = 400F) engine.block.setWidth(block, value = 100F) engine.block.setHeight(block, value = 100F) val fill = engine.block.createFill(FillType.Color) engine.block.setFill(block, fill = fill) ``` ## Create colors Here we instantiate a few colors with RGB and CMYK color spaces. We also define two spot colors, one with an RGB approximation and another with a CMYK approximation. Note that a spot colors can have both color space approximations. ```kotlin highlight-create-colors val rgbaBlue = Color.fromRGBA(r = 0F, g = 0F, b = 1F, a = 1F) val cmykRed = Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F, tint = 1F) val cmykPartialRed = Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F, tint = 0.5F) engine.editor.setSpotColor( name = "Pink-Flamingo", Color.fromRGBA(r = 0.988F, g = 0.455F, b = 0.992F), ) engine.editor.setSpotColor(name = "Yellow", Color.fromCMYK(c = 0F, m = 0F, y = 1F, k = 0F)) val spotPinkFlamingo = Color.fromSpotColor( name = "Pink-Flamingo", tint = 1F, externalReference = "Crayola", ) val spotPartialYellow = Color.fromSpotColor(name = "Yellow", tint = 0.3F) ``` ## Applying colors to a block We can use the defined colors to modify certain properties of a fill or properties of a shape. Here we apply it to `'fill/color/value'`, `'stroke/color'` and `'dropShadow/color'`. ```kotlin highlight-apply-colors engine.block.setColor(fill, property = "fill/color/value", value = rgbaBlue) engine.block.setColor(fill, property = "fill/color/value", value = cmykRed) engine.block.setColor(block, property = "stroke/color", value = cmykPartialRed) engine.block.setColor(fill, property = "fill/color/value", value = spotPinkFlamingo) engine.block.setColor(block, property = "dropShadow/color", value = spotPartialYellow) ``` ## Converting colors Using the utility function `convertColorToColorSpace` we create a new color in the CMYK color space by converting the `rgbaBlue` color to the CMYK color space. We also create a new color in the RGB color space by converting the `spotPinkFlamingo` color to the RGB color space. ```kotlin highlight-convert-color val cmykBlueConverted = engine.editor.convertColorToColorSpace( rgbaBlue, colorSpace = ColorSpace.CMYK, ) val rgbaPinkFlamingoConverted = engine.editor.convertColorToColorSpace( spotPinkFlamingo, colorSpace = ColorSpace.SRGB, ) ``` ## Listing spot colors This function returns the list of currently defined spot colors. ```kotlin highlight-find-spot engine.editor.findAllSpotColors() // ["Crayola-Pink-Flamingo", "Yellow"] ``` ## Redefine a spot color We can re-define the RGB and CMYK approximations of an already defined spot color. Doing so will change the rendered color of the blocks. We change it for the CMYK approximation of `'Yellow'` and make it a bit greenish. The properties that have `'Yellow'` as their spot color will change when re-rendered. ```kotlin highlight-change-spot engine.editor.setSpotColor("Yellow", Color.fromCMYK(c = 0.2F, m = 0F, y = 1F, k = 0F)) ``` ## Removing the definition of a spot color We can undefine a spot color. Doing so will make all the properties still referring to that spot color (`'Yellow'` in this case) use the default magenta RGB approximation. ```kotlin highlight-undefine-spot engine.editor.removeSpotColor("Yellow") ``` ## Full Code Here's the full code: ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.ColorSpace import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun colors( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 350F) engine.block.setPositionY(block, value = 400F) engine.block.setWidth(block, value = 100F) engine.block.setHeight(block, value = 100F) val fill = engine.block.createFill(FillType.Color) engine.block.setFill(block, fill = fill) val rgbaBlue = Color.fromRGBA(r = 0F, g = 0F, b = 1F, a = 1F) val cmykRed = Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F, tint = 1F) val cmykPartialRed = Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F, tint = 0.5F) engine.editor.setSpotColor( name = "Pink-Flamingo", Color.fromRGBA(r = 0.988F, g = 0.455F, b = 0.992F), ) engine.editor.setSpotColor(name = "Yellow", Color.fromCMYK(c = 0F, m = 0F, y = 1F, k = 0F)) val spotPinkFlamingo = Color.fromSpotColor( name = "Pink-Flamingo", tint = 1F, externalReference = "Crayola", ) val spotPartialYellow = Color.fromSpotColor(name = "Yellow", tint = 0.3F) engine.block.setColor(fill, property = "fill/color/value", value = rgbaBlue) engine.block.setColor(fill, property = "fill/color/value", value = cmykRed) engine.block.setColor(block, property = "stroke/color", value = cmykPartialRed) engine.block.setColor(fill, property = "fill/color/value", value = spotPinkFlamingo) engine.block.setColor(block, property = "dropShadow/color", value = spotPartialYellow) val cmykBlueConverted = engine.editor.convertColorToColorSpace( rgbaBlue, colorSpace = ColorSpace.CMYK, ) val rgbaPinkFlamingoConverted = engine.editor.convertColorToColorSpace( spotPinkFlamingo, colorSpace = ColorSpace.SRGB, ) engine.editor.findAllSpotColors() // ["Crayola-Pink-Flamingo", "Yellow"] engine.editor.setSpotColor("Yellow", Color.fromCMYK(c = 0.2F, m = 0F, y = 1F, k = 0F)) engine.editor.removeSpotColor("Yellow") engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Basics" description: "Learn how color is handled in CE.SDK and how to apply, modify, and manage it across elements." platform: android url: "https://img.ly/docs/cesdk/android/colors/basics-307115/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [Basics](https://img.ly/docs/cesdk/android/colors/basics-307115/) --- When specifying a color property, you can use one of three color spaces: [RGB](https://en.wikipedia.org/wiki/RGB_color_model), [CMYK](https://en.wikipedia.org/wiki/CMYK_color_model) and [spot color](https://en.wikipedia.org/wiki/Spot_color). > **Note:** During export, only RGB and spot color values will be inserted into the > resulting PDF. Any color specified in CMYK values will be converted to RGB > using the standard conversion. Tint values will be retained in the alpha > channel. The following properties can be set with the function `setColor` and support all three color spaces: - `'backgroundColor/color'` - `'camera/clearColor'` - `'dropShadow/color'` - `'fill/color/value'` - `'stroke/color'` ## RGB RGB is the color space used when rendering a color to a screen. All values of `R`, `G`, `B`must be between `0.0` and `1.0` When using RGB, you typically also specify opacity or alpha as a value between `0.0` and `1.0` and is then referred to as `RGBA`. When a RGB color has an alpha value that is not `1.0`, it will be rendered to screen with corresponding transparency. ## CMYK CMYK is the color space used when color printing. All values of `C`, `M`, `Y`, and `K` must be between `0.0` and `1.0` When using CMYK, you can also specify a tint value between `0.0` and `1.0`. When a CMYK color has a tint value that is not `1.0`, it will be rendered to screen as if transparent over a white background. When rendering to screen, CMYK colors are first converted to RGB using a simple mathematical conversion. Currently, the same conversion happens when exporting a scene to a PDF file. ## Spot Color Spot colors are typically used for special printers or other devices that understand how to use them. Spot colors are defined primarily by their name as that is the information that the device will use to render. For the purpose of rendering a spot color to screen, it must be given either an RGB or CMYK color approximation or both. These approximations adhere to the same restrictions respectively described above. You can specify a tint as a value between `0.0` and `1.0` which will be interpreted as opacity when rendering to screen. It is up to the special printer or device how to interpret the tint value. You can also specify an external reference, a string describing the origin of this spot color. When rendering to screen, the spot color's RGB or CMYK approximation will be used, in that order of preference. When exporting a scene to a PDF file, spot colors will be saved as a [Separation Color Space](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.6.pdf#G9.1850648). Using a spot color is a two step process: 1. you define a spot color with its name and color approximation(s) in the spot color registry. 2. you instantiate a spot color with its name, a tint and an external reference. The spot color registry allows you to: - list the defined spot colors - define a new a spot color with a name and its RGB or CMYK approximation - re-define an existing spot color's RGB or CMYK approximation - retrieve the RGB or CMYK approximation of an already defined spot color - remove a spot color from the list of defined spot colors Multiple blocks and their properties can refer to the same spot color and each can have a different tint and external reference. > **Note:** **Warning** If a block's color property refers to an undefined spot color, the > default color magenta with an RGB approximation of (1, 0, 1) will be used. ## Converting between colors A utility function `convertColorToColorSpace` is provided to create a new color from an existing color and a new color space. RGB and CMYK colors can be converted between each other and is done so using a simple mathematical conversion. Spot colors can be converted to RGB and CMYK simply by using the corresponding approximation. RGB and CMYK colors cannot be converted to spot colors. ## Custom Color Libraries You can configure CE.SDK with custom color libraries. More information is found [here](https://img.ly/docs/cesdk/android/colors/create-color-palette-7012e0/). --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Color Conversion" description: "Convert between RGB, CMYK, and other color formats based on your project’s output requirements." platform: android url: "https://img.ly/docs/cesdk/android/colors/conversion-bcd82b/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [Color Conversion](https://img.ly/docs/cesdk/android/colors/conversion-bcd82b/) --- To ease implementing advanced color interfaces, you may rely on the engine to perform color conversions. Converts a color to the given color space. - `color`: The color to convert. - `colorSpace`: The color space to convert to. - Returns The converted color. ```kotlin // Convert a color val rgbaGreen = Color.fromRGBA(r = 0F, g = 1F, b = 0F, a = 0F) val cmykGreen = engine.editor.convertColorToColorSpace(color = rgbaGreen, colorSpace = ColorSpace.CMYK) ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create a Color Palette" description: "Build reusable color palettes to maintain consistency and streamline user choices." platform: android url: "https://img.ly/docs/cesdk/android/colors/create-color-palette-7012e0/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [Create a Color Palette](https://img.ly/docs/cesdk/android/colors/create-color-palette-7012e0/) --- ```kotlin file=@cesdk_android_examples/editor-guides-configuration-color-palette/ColorPaletteEditorSolution.kt reference-only import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Color import androidx.navigation.NavHostController import ly.img.editor.DesignEditor import ly.img.editor.EditorConfiguration import ly.img.editor.EngineConfiguration import ly.img.editor.rememberForDesign // Add this composable to your NavHost @Composable fun ColorPaletteEditorSolution(navController: NavHostController) { val engineConfiguration = EngineConfiguration.rememberForDesign( license = "", // pass null or empty for evaluation mode with watermark ) val editorConfiguration = EditorConfiguration.rememberForDesign( colorPalette = remember { listOf( Color(0xFF4A67FF), Color(0xFFFFD333), Color(0xFFC41230), Color(0xFF000000), Color(0xFFFFFFFF), ) }, ) DesignEditor( engineConfiguration = engineConfiguration, editorConfiguration = editorConfiguration, ) { // You can set result here navController.popBackStack() } } ``` In this example, we will show you how to make color palette configurations for the mobile editor. The example is based on the `Design Editor`, however, it is exactly the same for all the other [solutions](https://img.ly/docs/cesdk/android/prebuilt-solutions-d0ed07/). ## Configuration Color palette configuration is part of the `EditorConfiguration` class. Use the `EditorConfiguration.getDefault` helper function to make color palette configurations. - `colorPalette` - the color palette used for UI elements that contain predefined color options, e.g., for "Fill Color" or "Stroke Color". ```kotlin highlight-configuration-colorPalette colorPalette = remember { listOf( Color(0xFF4A67FF), Color(0xFFFFD333), Color(0xFFC41230), Color(0xFF000000), Color(0xFFFFFFFF), ) }, ``` ## Full Code Here's the full code: ```kotlin import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Color import androidx.navigation.NavHostController import ly.img.editor.DesignEditor import ly.img.editor.EditorConfiguration import ly.img.editor.EngineConfiguration import ly.img.editor.rememberForDesign // Add this composable to your NavHost @Composable fun ColorPaletteEditorSolution(navController: NavHostController) { val engineConfiguration = EngineConfiguration.rememberForDesign( license = "", ) val editorConfiguration = EditorConfiguration.rememberForDesign( colorPalette = remember { listOf( Color(0xFF4A67FF), Color(0xFFFFD333), Color(0xFFC41230), Color(0xFF000000), Color(0xFFFFFFFF), ) }, ) DesignEditor( engineConfiguration = engineConfiguration, editorConfiguration = editorConfiguration, ) { // You can set result here navController.popBackStack() } } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "For Print" description: "Use print-ready color models and settings for professional-quality, production-ready exports." platform: android url: "https://img.ly/docs/cesdk/android/colors/for-print-59bc05/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [For Print](https://img.ly/docs/cesdk/android/colors/for-print-59bc05/) --- --- ## Related Pages - [Spot Colors](https://img.ly/docs/cesdk/android/colors/for-print/spot-c3a150/) - Learn how to define spot colors and set their color approximation in the CreativeEditor SDK. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Spot Colors" description: "Learn how to define spot colors and set their color approximation in the CreativeEditor SDK." platform: android url: "https://img.ly/docs/cesdk/android/colors/for-print/spot-c3a150/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [For Print](https://img.ly/docs/cesdk/android/colors/for-print-59bc05/) > [Spot Colors](https://img.ly/docs/cesdk/android/colors/for-print/spot-c3a150/) --- ```kotlin reference-only // Create a spot color with an RGB color approximation. engine.editor.setSpotColor("Red", Color.fromRGBA(r = 1F, g = 0F, b = 0F, a = 1F)) // Create a spot color with a CMYK color approximation. // Add a CMYK approximation to the already defined 'Red' spot color. engine.editor.setSpotColor("Yellow", Color.fromCMYK(c = 0F, m = 0F, y = 1F, k = 0F)) engine.editor.setSpotColor("Red", Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F)) // List all defined spot colors. engine.editor.findAllSpotColors() // ['Red', 'Yellow'] // Retrieve the RGB color approximation for a defined color. // The alpha value will always be 1.0. val rgbaSpotRed = engine.editor.getSpotColorRGB("Red") // Retrieve the CMYK color approximation for a defined color. val cmykSpotRed = engine.editor.getSpotColorCMYK("Red") // Retrieving the approximation of an undefined spot color returns magenta. val cmykSpotUnknown = engine.editor.getSpotColorCMYK("Unknown") // Returns CMYK values for magenta. // Removes a spot color from the list of defined spot colors. engine.editor.removeSpotColor("Red") ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/creative-sdk)'s CreativeEngine to manage spot colors in the `editor` API. ## Functions ```kotlin fun findAllSpotColors(): List ``` Queries the names of currently set spot colors previously set with \`setSpotColor\`\`. - Returns the names of set spot colors. ```kotlin fun getSpotColorRGB(name: String): RGBAColor ``` Queries the RGB representation set for a spot color. If the value of the queried spot color has not been set yet, returns the default RGB representation (of magenta). The alpha value is always 1.0. - `name`: the name of a spot color. - Returns the RGB representation of a spot color. ```kotlin fun getSpotColorCMYK(name: String): CMYKColor ``` Queries the CMYK representation set for a spot color. If the value of the queried spot color has not been set yet, returns the default RGB representation (of magenta). - `name`: the name of a spot color. - Returns the CMYK representation of a spot color. ```kotlin fun setSpotColor( name: String, color: RGBAColor, ) ``` Sets the RGB representation of a spot color. Use this function to both create a new spot color or update an existing spot color. Note: The alpha value is ignored. - `name`: the name of a spot color. - `color`: the RGB spot color. ```kotlin fun setSpotColor( name: String, color: CMYKColor, ) ``` Sets the CMYK representation of a spot color. Use this function to both create a new spot color or update an existing spot color. - `name`: the name of a spot color. - `color`: the CMYK spot color. ```kotlin fun removeSpotColor(name: String) ``` Removes a spot color from the list of set spot colors. - `name`: the name of a spot color. ## Full Code Here's the full code: ```kotlin // Create a spot color with an RGB color approximation. engine.editor.setSpotColor("Red", Color.fromRGBA(r = 1F, g = 0F, b = 0F, a = 1F)) // Create a spot color with a CMYK color approximation. // Add a CMYK approximation to the already defined 'Red' spot color. engine.editor.setSpotColor("Yellow", Color.fromCMYK(c = 0F, m = 0F, y = 1F, k = 0F)) engine.editor.setSpotColor("Red", Color.fromCMYK(c = 0F, m = 1F, y = 1F, k = 0F)) // List all defined spot colors. engine.editor.findAllSpotColors() // ['Red', 'Yellow'] // Retrieve the RGB color approximation for a defined color. // The alpha value will always be 1.0. val rgbaSpotRed = engine.editor.getSpotColorRGB("Red") // Retrieve the CMYK color approximation for a defined color. val cmykSpotRed = engine.editor.getSpotColorCMYK("Red") // Retrieving the approximation of an undefined spot color returns magenta. val cmykSpotUnknown = engine.editor.getSpotColorCMYK("Unknown") // Returns CMYK values for magenta. // Removes a spot color from the list of defined spot colors. engine.editor.removeSpotColor("Red") ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "For Screen" description: "Documentation for For Screen" platform: android url: "https://img.ly/docs/cesdk/android/colors/for-screen-1911f8/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [For Screen](https://img.ly/docs/cesdk/android/colors/for-screen-1911f8/) --- --- ## Related Pages - [P3 Colors](https://img.ly/docs/cesdk/android/colors/for-screen/p3-706127/) - Documentation for P3 Colors --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "P3 Colors" description: "Documentation for P3 Colors" platform: android url: "https://img.ly/docs/cesdk/android/colors/for-screen/p3-706127/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [For Screen](https://img.ly/docs/cesdk/android/colors/for-screen-1911f8/) > [P3 Colors](https://img.ly/docs/cesdk/android/colors/for-screen/p3-706127/) --- This guide explains how to check whether the P3 color space is supported on a given device using the `supportsP3()` function and how to handle scenarios where P3 is unavailable. `supportsP3` returns whether the engine supports displaying and working in the P3 color space on the current device. Otherwise, this function throws an error with a description of why the P3 color space is not supported. If supported, the engine can be switched to a P3 color space using the "features/p3WorkingColorSpace" setting. `checkP3Support` throws an error if the engine does not support working in the P3 color space. ```kotlin // Check whether the current device supports working in the P3 color space val p3IsSupported = engine.editor.supportsP3() try { engine.editor.checkP3Support() } catch (ex: Exception) { // P3 is not supported on the current device } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Overview" description: "Manage color usage in your designs, from applying brand palettes to handling print and screen formats." platform: android url: "https://img.ly/docs/cesdk/android/colors/overview-16a177/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) > [Overview](https://img.ly/docs/cesdk/android/colors/overview-16a177/) --- Colors are a fundamental part of design in the CreativeEditor SDK (CE.SDK). Whether you're designing for digital screens or printed materials, consistent color management ensures your creations look the way you intend. CE.SDK offers flexible tools for working with colors through both the user interface and programmatically, making it easy to manage color workflows at any scale. [Explore Demos](https://img.ly/showcases/cesdk?tags=android) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "System Compatibility" description: "Learn how device performance and hardware limits affect CE.SDK editing, rendering, and export capabilities." platform: android url: "https://img.ly/docs/cesdk/android/compatibility-139ef9/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Compatibility & Security](https://img.ly/docs/cesdk/android/compatibility-fef719/) > [System Compatibility](https://img.ly/docs/cesdk/android/compatibility-139ef9/) --- ## Targets On Android, CE.SDK makes use of system-frameworks to benefit from hardware acceleration and platform native performance. The following targets are supported: - Android 7 or later (`minSdk 24`) ## Recommended Hardware Android phones released in the last 5 years, e.g. Asus Zenfone 3, Samsung M31s, or Google Pixel 5. Video capabilities directly depend on the video capabilities of the individual phone. ## Video Playback and exporting is **supported for all codecs** mentioned in the general section. However, mobile devices have stricter limits around the number of parallel encoders and decoders compared to fully fledged desktop machines. This means, that very large scenes with more than 10 videos shown in parallel may fail to play all videos at the same time. ## Export Limitations The export size is limited by the hardware capabilities of the device, e.g., due to the maximum texture size that can be allocated. The maximum possible export size can be queried via API, see [export guide](https://img.ly/docs/cesdk/android/export-save-publish/export/overview-9ed3a8/). --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Compatibility & Security" description: "Learn about CE.SDK's compatibility and security features." platform: android url: "https://img.ly/docs/cesdk/android/compatibility-fef719/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Compatibility & Security](https://img.ly/docs/cesdk/android/compatibility-fef719/) --- CE.SDK provides robust compatibility and security features across platforms. Learn about supported browsers, frameworks, file formats, language support, and how CE.SDK ensures secure operation in your applications. --- ## Related Pages - [Bundle Size](https://img.ly/docs/cesdk/android/bundle-size-df9210/) - Understand CE.SDK’s engine and editor bundle sizes and how they affect your mobile app’s download footprint. - [System Compatibility](https://img.ly/docs/cesdk/android/compatibility-139ef9/) - Learn how device performance and hardware limits affect CE.SDK editing, rendering, and export capabilities. - [File Format Support](https://img.ly/docs/cesdk/android/file-format-support-3c4b2a/) - See which image, video, audio, font, and template formats CE.SDK supports for import and export. - [Security](https://img.ly/docs/cesdk/android/security-777bfd/) - Learn how CE.SDK keeps your data private with client-side processing, secure licensing, and GDPR-compliant practices. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Concepts" description: "Key concepts and principles of CE.SDK" platform: android url: "https://img.ly/docs/cesdk/android/concepts-c9ff51/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) --- Key Concepts and principles of CE.SDK. --- ## Related Pages - [Key Concepts](https://img.ly/docs/cesdk/android/key-concepts-21a270/) - Explore CE.SDK’s key features—manual editing, automation, templates, AI tools, and full UI and API control. - [Key Capabilities](https://img.ly/docs/cesdk/android/key-capabilities-dbb5b1/) - Explore CE.SDK’s key features—manual editing, automation, templates, AI tools, and full UI and API control. - [Editing Workflow](https://img.ly/docs/cesdk/android/concepts/editing-workflow-032d27/) - Control editing access with Creator and Adopter roles, each offering tailored permissions and UI constraints. - [Blocks](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) - Learn how blocks define elements in a scene and how to structure them for rendering in CE.SDK. - [Scenes](https://img.ly/docs/cesdk/android/concepts/scenes-e8596d/) - Scenes act as the root container for blocks and define the full design structure in CE.SDK. - [Editor State](https://img.ly/docs/cesdk/android/concepts/edit-modes-1f5b6c/) - Control how users interact with content by switching between edit modes like transform, crop, and text. - [Events](https://img.ly/docs/cesdk/android/concepts/events-353f97/) - Subscribe to block creation, update, and deletion events to track changes in your CE.SDK scene. - [Buffers](https://img.ly/docs/cesdk/android/concepts/buffers-9c565b/) - Use buffers to store temporary, non-serializable data in CE.SDK via the CreativeEngine API. - [Working With Resources](https://img.ly/docs/cesdk/android/concepts/resources-a58d71/) - Preload all resources for blocks or scenes in CE.SDK to improve performance and avoid runtime delays. - [Undo and History](https://img.ly/docs/cesdk/android/concepts/undo-and-history-99479d/) - Manage undo and redo stacks in CE.SDK using multiple histories, callbacks, and API-based controls. - [Headless](https://img.ly/docs/cesdk/android/concepts/headless-mode-24ab98/) - Use the engine directly, without any prebuilt UI. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Blocks" description: "Learn how blocks define elements in a scene and how to structure them for rendering in CE.SDK." platform: android url: "https://img.ly/docs/cesdk/android/concepts/blocks-90241e/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Blocks](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) --- ## Lifecycle Only blocks that are direct or indirect children of a `page` block are rendered. Scenes without any `page` child may not be properly displayed by the CE.SDK editor. ### Functions ```kotlin fun create(blockType: DesignBlockType): DesignBlock ``` Create a new block. - `blockType`: the type of the block that shall be created. - Returns the created blocks handle. To create a scene, use [](https://img.ly/docs/cesdk/android/open-the-editor-23a1db/) instead. ```kotlin suspend fun saveToString( blocks: List, allowedResourceSchemes: List = listOf("bundle", "file", "http", "https"), ): String ``` Saves the given blocks to a proprietary string. If a resource uri has a scheme that is not in `allowedResourceSchemes`, an exception will be thrown. Note: All given block handles must be valid, otherwise an exception will be thrown. - `blocks`: the blocks to save. - `allowedResourceSchemes`: the list of allowed resource schemes in the scene. - Returns a string representation of the blocks. ```kotlin suspend fun saveToArchive(blocks: List): ByteBuffer ``` Saves the given blocks to an archive. Note: All given block handles must be valid, otherwise this call returns an error. - `blocks`: the blocks to save. - Returns a string representation of the blocks. ```kotlin suspend fun loadFromString(block: String): List ``` Loads existing blocks from the given string. The blocks are not attached by default and won't be visible until attached to a page or the scene. The UUID of the loaded blocks is replaced with a new one. - `block`: a string representing the given blocks. - Returns a list of loaded blocks. ```kotlin suspend fun loadFromURL(url: Uri): List ``` Loads existing blocks from a URL. The URL should point to a blocks file within an unzipped archive directory previously saved with `saveToArchive`. The blocks are not attached by default and won't be visible until attached to a page or the scene. The UUID of the loaded blocks is replaced with a new one. - `url`: the URL to the blocks.blocks file. - Returns a list of loaded blocks. ```kotlin suspend fun loadFromArchive(archiveUri: Uri): List ``` Loads existing blocks from an archive. The blocks are not attached by default and won't be visible until attached to a page or the scene. The UUID of the loaded blocks is replaced with a new one. - `archiveUri`: the uri of the blocks archive file. - Returns a list of loaded blocks. ```kotlin fun getType(block: DesignBlock): String ``` Get the type of the given block, fails if the block is invalid. - `block`: the block to query. - Returns the block type. ```kotlin fun setName( block: DesignBlock, name: String, ) ``` Update a block's name. - `block`: the block to update. - `name`: the name to set. ```kotlin fun getName(block: DesignBlock): String ``` Get a block's name. - `block:`: The block to query. - Returns The block's name. ```kotlin fun duplicate(block: DesignBlock): DesignBlock ``` Duplicates a block including its children. Required scope: "lifecycle/duplicate" If the block is parented to a track that is set always-on-bottom, the duplicate is inserted in the same track immediately after the block. Otherwise, the duplicate is moved up in the hierarchy. - `block`: the block to duplicate. - Returns the handle of the duplicate. ```kotlin fun destroy(block: DesignBlock) ``` Destroys a block. Required scope: "lifecycle/destroy" - `block`: the block to destroy. ```kotlin fun isValid(block: DesignBlock): Boolean ``` Check if a block is valid. A block becomes invalid once it has been destroyed. - `block`: the block to query. - Returns true if the block is valid, false otherwise. ### Full Code In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to modify scenes through the `block` Api. ```kotlin // Create, save and load blocks val block = engine.block.create(DesignBlockType.Graphic) val savedBlocksString = engine.block.saveToString(blocks = listOf(block)) val loadedBlocksString = engine.block.loadFromString(savedBlocksString) val savedBlocksArchive = engine.block.saveToArchive(blocks = listOf(block)) val loadedBlocksArchive = engine.block.loadFromArchive(blocksUri = Uri.parse("https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1_blocks.zip")) // Load blocks from an extracted zip file created with block.saveToArchive val loadedBlocksURL = engine.block.loadFromURL(url = Uri.parse("https://cdn.img.ly/assets/v6/ly.img.text.components/box/blocks.blocks")) // Check a blocks type val blockType = engine.block.getType(block) // Alter a blocks name engine.block.setName(block, name = "someName") val name = engine.block.getName(block) // You may duplicate or destroy blocks val duplicate = engine.block.duplicate(block) engine.block.destroy(duplicate) engine.block.isValid(duplicate) // false ``` ## Properties ### UUID A universally unique identifier (UUID) is assigned to each block upon creation and can be queried. This is stable across save & load and may be used to reference blocks. ```kotlin fun getUUID(block: DesignBlock): String ``` Get a block's unique identifier. - `block:`: The block to query. - Returns The block's UUID. ### Reflection For every block, you can get a list of all its properties by calling `findAllProperties`. Properties specific to a block are prefixed with the block's type followed by a forward slash. There are also common properties shared between blocks which are prefixed by their respective type. A list of all properties can be found in the [Blocks Overview](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/). ```kotlin fun findAllProperties(block: DesignBlock): List ``` Get all available properties of a block. - `block`: the block whose properties should be queried. - Returns a list of the property names. Given a property you can query its type using `getPropertyType`. ```kotlin fun getPropertyType(property: String): PropertyType ``` Get the type of a property given its name. - `property`: the name of the property whose type should be queried. - Returns the property type. To get a list of possible values for an enum property call `getEnumValues(enumProperty: string): string[]`. ```kotlin fun getEnumValues(enumProperty: String): List ``` Get all the possible values of an enum given an enum property. - `enumProperty`: the name of the property whose enum values should be queried. - Returns a list of the enum value names as string. Some properties can only be written to or only be read. To find out what is possible with a property, you can use the `isPropertyReadable` and `isPropertyWritable` methods. ```kotlin fun isPropertyReadable(property: String): Boolean ``` Check if a property with a given name is readable. - `property`: the name of the property whose type should be queried. - Returns whether the property is readable or not. Will return false for unknown properties. ```kotlin fun isPropertyWritable(property: String): Boolean ``` Check if a property with a given name is writeable. - `property`: the name of the property whose type should be queried. - Returns whether the property is writeable or not. Will return false for unknown properties. ### Generic Properties There are dedicated setter and getter functions for each property type. You have to provide a block and the property path. Use `findAllProperties` to get a list of all the available properties a block has. > **Note:** Please make sure you call the setter and getter function matching the type of > the property you want to set or query or else you will get an error. Use > `getType` to figure out the pair of functions you need to use. ```kotlin fun findAllProperties(block: DesignBlock): List ``` Get all available properties of a block. - `block`: the block whose properties should be queried. - Returns a list of the property names. ```kotlin fun setBoolean( block: DesignBlock, property: String, value: Boolean, ) ``` Set a boolean property of the given design block to the given value. - `block`: the block whose property should be set. - `property`: the name of the property to set. - `value`: the value to set. ```kotlin fun getBoolean( block: DesignBlock, property: String, ): Boolean ``` Get the value of a boolean property of the given design block. - `block`: the block whose property should be queried. - `property`: the name of the property to set. - Returns the value of the property. ```kotlin fun setInt( block: DesignBlock, property: String, value: Int, ) ``` Set an int property of the given design block to the given value. - `block`: the block whose property should be set. - `property`: the name of the property to set. - `value`: the value to set. ```kotlin fun getInt( block: DesignBlock, property: String, ): Int ``` Get the value of an int property of the given design block. - `block`: the block whose property should be queried. - `property`: the name of the property to set. - Returns the value of the property. ```kotlin fun setFloat( block: DesignBlock, property: String, value: Float, ) ``` Set a float property of the given design block to the given value. - `block`: the block whose property should be set. - `property`: the name of the property to set. - `value`: the value to set. ```kotlin fun getFloat( block: DesignBlock, property: String, ): Float ``` Get the value of a float property of the given design block. - `block`: the block whose property should be queried. - `property`: the name of the property to set. - Returns the value of the property. ```kotlin fun setDouble( block: DesignBlock, property: String, value: Double, ) ``` Set a double property of the given design block to the given value. - `block`: the block whose property should be set. - `property`: the name of the property to set. - `value`: the value to set. ```kotlin fun getDouble( block: DesignBlock, property: String, ): Double ``` Get the value of a double property of the given design block. - `block`: the block whose property should be queried. - `property`: the name of the property to set. - Returns the value of the property. ```kotlin fun setString( block: DesignBlock, property: String, value: String, ) ``` Set a string property of the given design block to the given value. - `block`: the block whose property should be set. - `property`: the name of the property to set. - `value`: the value to set. ```kotlin fun getString( block: DesignBlock, property: String, ): String ``` Get the value of a string property of the given design block. - `block`: the block whose property should be queried. - `property`: the name of the property to set. - Returns the value of the property. ```kotlin fun setColor( block: DesignBlock, property: String, value: Color, ) ``` Set a color property of the given design block to the given value. - `block`: the block whose property should be set. - `property`: the name of the property to set. - `value`: the value to set. ```kotlin fun getColor( block: DesignBlock, property: String, ): Color ``` Get the value of a color property of the given design block. - `block`: the block whose property should be queried. - `property`: the name of the property to set. - Returns the value of the property. ```kotlin fun setEnum( block: DesignBlock, property: String, value: String, ) ``` Set an enum property of the given design block to the given value. - `block`: the block whose property should be set. - `property`: the name of the property to set. - `value`: the value to set. ```kotlin fun getEnum( block: DesignBlock, property: String, ): String ``` Get the value of an enum property of the given design block. - `block`: the block whose property should be queried. - `property`: the name of the property to get. - Returns the value of the property. ```kotlin fun setGradientColorStops( block: DesignBlock, property: String, colorStops: List, ) ``` Set a gradient color stops property of the given design block. - `block`: the block whose property should be set. - `property`: the name of the property to set. - `colorStops`: the list of color stops. ```kotlin fun getGradientColorStops( block: DesignBlock, property: String, ): List ``` Get the gradient color stops property of the given design block. - `block`: the block whose property should be queried. - `property`: the name of the property to query. - Returns the list of gradient color stops. ```kotlin fun setSourceSet( block: DesignBlock, property: String, sourceSet: List, ) ``` Set the source set of a source set property of the given block. The crop and content fill mode of the associated block will be set to the default values. - `block`: the block whose source set should be set. - `property`: the name of the property to set. - `sourceSet`: the new source set. ```kotlin fun getSourceSet( block: DesignBlock, property: String, ): List ``` Returns the source set of a source set property of the given block. - `block`: the block whose property should be queried. - `property`: the name of the property to get. - Returns the source set of the given block. ```kotlin suspend fun addImageFileUriToSourceSet( block: DesignBlock, property: String, uri: String, ) ``` Add a source to the `sourceSet` property of the given block. If there already exists in source set an image with the same width, that existing image will be replaced. If the source set is or gets empty, the crop and content fill mode of the associated block will be set to the default values. Note: This fetches the resource from the given URI to obtain the image dimensions. It is recommended to use setSourceSet if the dimension is known. - `block`: the block to update. - `property`: the name of the property to modify. - `uri`: the source to add to the source set. ```kotlin suspend fun addVideoFileUriToSourceSet( block: DesignBlock, property: String, uri: String, ) ``` Add a source to the `sourceSet` property of the given block. If there already exists in source set a video with the same width, that existing video will be replaced. If the source set is or gets empty, the crop and content fill mode of the associated block will be set to the default values. Note: This fetches the resource from the given URI to obtain the video dimensions. It is recommended to use setSourceSet if the dimension is known. - `block`: the block to update. - `property`: the name of the property to modify. - `uri`: the source to add to the source set. ### Modifying Properties Here’s the full code snippet for modifying a block’s properties: ```kotlin val uuid = engine.block.getUUID(block) val propertyNamesStar = engine.block .findAllProperties(starShape) // List [ "shape/star/innerDiameter", "shape/star/points", "opacity/value", ... ] val propertyNamesImage = engine.block .findAllProperties(imageFill) // List [ "fill/image/imageFileURI", "fill/image/previewFileURI", "fill/image/externalReference", ... ] val propertyNamesText = engine.block .findAllProperties(text) // List [ "text/text", "text/fontFileUri", "text/externalReference", "text/fontSize", "text/horizontalAlignment", ... ] val pointsType = engine.block.getPropertyType(property = "shape/star/points") // "Int" val alignmentType = engine.block.getPropertyType(property = "text/horizontalAlignment") // "Enum" engine.block.getEnumValues(enumProperty = "text/horizontalAlignment") val readable = engine.block.isPropertyReadable("shape/star/points") val writable = engine.block.isPropertyWritable("shape/star/points") // Generic Properties engine.block.setBoolean(scene, property = "scene/aspectRatioLock", value = false) engine.block.getBoolean(scene, property = "scene/aspectRatioLock") engine.block.setInt(star, property = "shape/star/points", value = points + 2) val points = engine.block.getInt(star, property = "shape/star/points") engine.block.setFloat(star, property = "shape/star/innerDiameter", value = 0.75F) engine.block.getFloat(star, property = "shape/star/innerDiameter") val audio = engine.block.create(DesignBlockType.Audio) engine.block.appendChild(scene, audio) engine.block.setDouble(audio, property = "playback/duration", value = 1.0) engine.block.getDouble(audio, property = "playback/duration") engine.block.setString(text, property = "text/text", value = "*o*") engine.block.setString( imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_4.jpg" ) engine.block.getString(text, property = "text/text") engine.block.getString(imageFill, property = "fill/image/imageFileURI") engine.block.setColor( colorFill, property = "fill/color/value", value = Color.fromString("#FFFFFFFF") ) // White engine.block.getColor(colorFill, property = "fill/color/value") engine.block.setEnum(text, property = "text/horizontalAlignment", value = "Center") engine.block.setEnum(text, property = "text/verticalAlignment", value = "Center") engine.block.getEnum(text, property = "text/horizontalAlignment") engine.block.getEnum(text, property = "text/verticalAlignment") engine.block.setGradientColorStops( block = gradientFill, property = "fill/gradient/colors", colorStops = listOf( GradientColorStop(stop = 0F, color = Color.fromRGBA(r = 0.1F, g = 0.2F, b = 0.3F, a = 0.4F)), GradientColorStop(stop = 1F, color = Color.fromSpotColor("test")) ) ) engine.block.getGradientColorStops(block = gradientFill, property = "fill/gradient/colors") val imageFill = engine.block.createFill(FillType.Image) engine.block.setSourceSet( block = imageFill, property = "fill/image/sourceSet", sourceSet = listOf( Source( uri = Uri.parse("http://img.ly/my-image.png"), width = 800, height = 600 ) ) ) engine.block.getSourceSet(block = imageFill, property = "fill/image/sourceSet") engine.block.addImageFileUriToSourceSet(block = imageFill, property = "fill/image/sourceSet", uri = "https://img.ly/static/ubq_samples/sample_1.jpg"); val videoFill = engine.block.createFill(FillType.Video) engine.block.addVideoFileUriToSourceSet(block = videoFill, property = "fill/video/sourceSet", uri = "https://img.ly/static/example-assets/sourceset/1x.mp4"); ``` ### Kind Property The `kind` of a design block is a custom string that can be assigned to a block in order to categorize it and distinguish it from other blocks that have the same type. The user interface can then customize its appearance based on the kind of the selected blocks. It can also be used for automation use cases in order to process blocks in a different way based on their kind. ```kotlin fun setKind( block: DesignBlock, kind: String, ) ``` Set the kind of the given block, fails if the block is invalid. - `block`: the block to modify. - `kind`: the block kind. ```kotlin fun getKind(block: DesignBlock): String ``` Get the kind of the given block, fails if the block is invalid. - `block`: the block to query. - Returns the block kind. ```kotlin fun findByKind(blockKind: String): List ``` Finds all blocks with the given kind. - `blockKind`: the kind to search for. - Returns a list of block ids. #### Full Code In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to modify a and query the kind property of design blocks through the `block` API. ```kotlin engine.block.setKind(text, kind = "title") val kind = engine.block.getKind(text) val allTitles = engine.block.findByKind(blockKind = "title") ``` ## Selection & Visibility A block can potentially be *invisible* (in the sense that you can't see it), even though `isVisible()` returns true. This could be the case when a block has not been added to a parent, the parent itself is not visible, or the block is obscured by another block on top of it. ### Select blocks and change their visibility ```kotlin fun setSelected( block: DesignBlock, selected: Boolean, ) ``` Update the selection state of a block. Fails for invalid blocks. Note: Previously selected blocks remain selected. Required scope: "editor/select" - `block`: the block to query. - `selected`: whether or not the block should be selected. ```kotlin fun isSelected(block: DesignBlock): Boolean ``` Get the selected state of a block. - `block`: the block to query. - Returns true if the block is selected, false otherwise. ```kotlin fun select(block: DesignBlock) ``` Selects the given block and deselects all other blocks. - `block`: the block to be selected. ```kotlin fun findAllSelected(): List ``` Get all currently selected blocks. - Returns An array of block ids. ```kotlin fun setVisible( block: DesignBlock, visible: Boolean, ) ``` Update a block's visibility. Required scope: "layer/visibility" - `block`: the block to update. - `visible`: whether the block shall be visible. ```kotlin fun isVisible(block: DesignBlock): Boolean ``` Query a block's visibility. - `block`: the block to query. - Returns true if visible, false otherwise. ```kotlin fun setClipped( block: DesignBlock, clipped: Boolean, ) ``` Update a block's clipped state. Required scope: "layer/clipping" - `block`: the block to update. - `clipped`: whether the block should clips its contents to its frame. ```kotlin fun isClipped(block: DesignBlock): Boolean ``` Query a block's clipped state. If `true`, the block should clip - `block`: the block to query. - Returns true if clipped, false otherwise. ```kotlin fun onSelectionChanged(): Flow ``` Subscribe to changes in the current set of selected blocks. - Returns flow of selected block change events. ```kotlin fun onClicked(): Flow ``` Subscribe to block click events. Note: `DesignBlock` is emitted at the end of the engine update if it has been clicked. - Returns flow of block click events. ```kotlin fun isIncludedInExport(block: DesignBlock): Boolean ``` Query if the given block is included on the exported result. - `block`: the block to query if it's included on the exported result. - Returns true, if the block is included on the exported result, false otherwise. ```kotlin fun setIncludedInExport( block: DesignBlock, enabled: Boolean, ) ``` Set if you want the given design block to be included in exported result. - `block`: the block whose exportable state should be set. - `enabled`: if true, the block will be included on the exported result. ### Full Code In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to modify scenes through the `block` API. ```kotlin engine.block.setSelected(block, selected = true) val isSelected = engine.block.isSelected(block) engine.block.select(block) val selectedIds = engine.block.findAllSelected() val isVisible = engine.block.isVisible(block) engine.block.setVisible(block, visible = true) val isClipped = engine.block.isClipped(page) engine.block.setClipped(page, clipped = true) val coroutineScope = CoroutineScope(Dispatchers.Main) engine.block.onSelectionChanged() .onEach { println("Change in the set of selected blocks") } .launchIn(coroutineScope) engine.block.onClicked() .onEach { println("Block $it was clicked") } .launchIn(coroutineScope) val isIncludedInExport = engine.block.isIncludedInExport(block) engine.block.setIncludedInExport(block, enabled = true) ``` ## State Blocks can perform operations that take some time or that can end in bad results. When that happens, blocks put themselves in a pending state or an error state and visual feedback is shown pertaining to the state. When an external operation is done to blocks, for example with a plugin, you may want to manually set the block's state to pending (if that external operation takes time) or to error (if that operation resulted in an error). The possible states of a block are: ``` data object Ready : BlockState data class Pending( @FloatRange(from = 0.0, to = 1.0) val progress: Float ) : BlockState data class Error(val type: Type) : BlockState { enum class Type { AUDIO_DECODING, // Failed to decode the block's audio stream. IMAGE_DECODING, // Failed to decode the block's image stream. FILE_FETCH, // Failed to retrieve the block's remote content. UNKNOWN, // An unknown error occurred. VIDEO_DECODING // Failed to decode the block's video stream. } } ``` When calling `getState`, the returned state reflects the combined state of a block, the block's fill, the block's shape and the block's effects. If any of these blocks is in an `Error` state, the returned state will reflect that error. If none of these blocks is in error state but any is in `Pending` state, the returned state will reflect the aggregate progress of the block's progress. If none of the blocks are in error state or pending state, the returned state is `Ready`. ```kotlin fun getState(block: DesignBlock): BlockState ``` Get the current state of a block. Note If this block is in error state or this block has a `Shape` block, `Fill` block or `Effect` block(s), that is in error state, the returned state will be `BlockState.Error`. Else, if this block is in pending state or this block has a `Shape` block, `Fill` block or `Effect` block(s), that is in pending state, the returned state will be `BlockState.Pending`. Else, the returned state will be `BlockState.Ready`. - `block`: the block whose state should be queried. - Returns the state of the block. ```kotlin fun setState( block: DesignBlock, state: BlockState, ) ``` Set the state of a block. - `block`: the block whose state should be set. - `state`: the new state to set. ```kotlin fun onStateChanged(blocks: List): Flow> ``` Subscribe to changes to the state of a block. Like `getState`, the state of a block is determined by the state of itself and its `Shape`, `Fill` and `Effect` block(s). - `blocks`: a list of blocks to filter events by. If the list is empty, events for every block are sent. - Returns flow of block state change events. ### Full Code In this example, we will show you how to use [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to retrieve's a block's state and to manually set a block in a pending state, an error state or back to a ready state. ```kotlin engine.block.onStateChanged(listOf(block)) .onEach { println("State of blocks $it is updated.") } .launchIn(CoroutineScope(Dispatchers.Main)) val state = engine.block.getState(block) engine.block.setState(block, state = BlockState.Pending(progress = 0.5F)) engine.block.setState(block, state = BlockState.Ready) engine.block.setState(block, state = BlockState.Error(BlockState.Error.Type.IMAGE_DECODING)) ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Buffers" description: "Use buffers to store temporary, non-serializable data in CE.SDK via the CreativeEngine API." platform: android url: "https://img.ly/docs/cesdk/android/concepts/buffers-9c565b/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Buffers](https://img.ly/docs/cesdk/android/concepts/buffers-9c565b/) --- ```kotlin reference-only // Create an audio block and append it to the page val audioBlock = engine.block.create(DesignBlockType.Audio) engine.block.appendChild(parent = page, child = audioBlock) // Create a buffer val audioBuffer = engine.editor.createBuffer() // Reference the audio buffer resource from the audio block engine.block.setUri( block = audioBlock, property = "audio/fileURI", value = audioBuffer ) // Generate 10 seconds of stereo 48 kHz audio data val sampleCount = 10 * 48000 val byteBuffer = ByteBuffer.allocate(2 * 4 * sampleCount) //2 channels, each 4 bytes repeat(sampleCount) { val sample = sin((440 * it * 2 * PI) / 48000).toFloat() byteBuffer.putFloat(sample) byteBuffer.putFloat(sample) } // Assign the audio data to the buffer val data = ByteArray(byteBuffer.capacity()) byteBuffer.position(0) byteBuffer.get(data) engine.editor.setBufferData(uri = audioBuffer, offset = 0, data = data) // We can get subranges of the buffer data val chunk = engine.editor.getBufferData(uri = audioBuffer, offset = 0, length = 4096) // Get current length of the buffer in bytes val length = engine.editor.getBufferLength(uri = audioBuffer) // Reduce the buffer to half its length, leading to 5 seconds worth of audio engine.editor.setBufferLength(uri = audioBuffer, length = data.size / 2) // Free data engine.editor.destroyBuffer(uri = audioBuffer) ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to create buffers through the `editor` API. Buffers can hold arbitrary data. > **Note:** **Limitations**Buffers are intended for temporary data only.* Buffer data is not part of the [scene serialization](https://img.ly/docs/cesdk/android/concepts/scenes-e8596d/) > * Changes to buffers can't be undone using the [history system](https://img.ly/docs/cesdk/android/concepts/undo-and-history-99479d/) ```kotlin fun createBuffer(): Uri ``` Create a resizable buffer that can hold arbitrary data. - Returns a uri to identify the buffer. ```kotlin fun destroyBuffer(uri: Uri) ``` Destroy a buffer and free its resources. - `uri`: the uri of the buffer to destroy. ```kotlin fun setBufferData( uri: Uri, offset: Int, data: ByteBuffer, ) ``` Set the data of a buffer. - `uri`: the uri of the buffer. - `offset`: the offset in bytes at which to start writing. - `data`: the data to write. Note that it has to be a direct `ByteBuffer`, created either via `ByteBuffer.allocateDirect` or via JNI NewDirectByteBuffer API. ```kotlin fun getBufferData( uri: Uri, offset: Int, length: Int, ): ByteBuffer ``` Get the data of a buffer. - `uri`: the uri of the buffer. - `offset`: the offset in bytes at which to start reading. - `length`: the number of bytes to read. - Returns the data read from the buffer or an error. ```kotlin fun setBufferLength( uri: Uri, length: Int, ) ``` Set the length of a buffer. - `uri`: the uri of the buffer. - `length`: the new length of the buffer in bytes. ```kotlin fun getBufferLength(uri: Uri): Int ``` Get the length of a buffer. - `uri`: the uri of the buffer. - Returns the length of the buffer in bytes. ## Full Code Here's the full code: ```kotlin // Create an audio block and append it to the page val audioBlock = engine.block.create(DesignBlockType.Audio) engine.block.appendChild(parent = page, child = audioBlock) // Create a buffer val audioBuffer = engine.editor.createBuffer() // Reference the audio buffer resource from the audio block engine.block.setUri( block = audioBlock, property = "audio/fileURI", value = audioBuffer ) // Generate 10 seconds of stereo 48 kHz audio data val sampleCount = 10 * 48000 val byteBuffer = ByteBuffer.allocate(2 * 4 * sampleCount) //2 channels, each 4 bytes repeat(sampleCount) { val sample = sin((440 * it * 2 * PI) / 48000).toFloat() byteBuffer.putFloat(sample) byteBuffer.putFloat(sample) } // Assign the audio data to the buffer val data = ByteArray(byteBuffer.capacity()) byteBuffer.position(0) byteBuffer.get(data) engine.editor.setBufferData(uri = audioBuffer, offset = 0, data = data) // We can get subranges of the buffer data val chunk = engine.editor.getBufferData(uri = audioBuffer, offset = 0, length = 4096) // Get current length of the buffer in bytes val length = engine.editor.getBufferLength(uri = audioBuffer) // Reduce the buffer to half its length, leading to 5 seconds worth of audio engine.editor.setBufferLength(uri = audioBuffer, length = data.size / 2) // Free data engine.editor.destroyBuffer(uri = audioBuffer) ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Editor State" description: "Control how users interact with content by switching between edit modes like transform, crop, and text." platform: android url: "https://img.ly/docs/cesdk/android/concepts/edit-modes-1f5b6c/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Editor State](https://img.ly/docs/cesdk/android/concepts/edit-modes-1f5b6c/) --- ```kotlin reference-only engine.editor.onStateChanged() .onEach { println("Editor history has changed") } .launchIn(CoroutineScope(Dispatchers.Main)) // Native modes: "Transform", "Crop", "Text" engine.editor.setEditMode("Crop") engine.editor.getEditMode() // "Crop" engine.editor.isInteractionHappening() // Query information about the text cursor position engine.editor.getTextCursorPositionInScreenSpaceX() engine.editor.getTextCursorPositionInScreenSpaceY() ``` The CreativeEditor SDK operates in different states called **Edit Modes**, each designed for a specific type of interaction on the canvas: - `Transform`: this is the default mode which allow to move, resize and manipulate things on the canvas - `Text`: Allows to edit the text elements on the canvas - `Crop`: Allow to Crop media blocks (images, videos, etc...) - `Trim`: Trim the clips in video mode - `Playback`: Play the media (mostly video) in video mode While users typically interact with these modes through the UI (e.g., showing or hiding specific controls based on the active mode), it’s also possible to manage them programmatically via the engine’s API, though this isn’t always required. In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to set and query the editor state in the `editor` API, i.e., what type of content the user is currently able to edit. ## State The editor state consists of the current edit mode, which informs what type of content the user is currently able to edit. The edit mode can be set to either `Transform`, `Crop`, `Text`, or a custom user-defined one. You can also query the intended mouse cursor and the location of the text cursor while editing text. Instead of having to constantly query the state in a loop, you can also be notified when the state has changed to then act on these changes in a callback. ```kotlin fun onStateChanged(): Flow ``` Subscribe to changes to the editor state. - Returns flow of editor state change events. ```kotlin fun setEditMode(editMode: String) ``` Set the edit mode of the editor. An edit mode defines what type of content can currently be edited by the user. Note: The initial edit mode is "Transform". - `editMode`: "Transform", "Crop", "Text", "Playback", "Trim" or a custom value. ```kotlin fun getEditMode(): String ``` Get the current edit mode of the editor. An edit mode defines what type of content can currently be edited by the user. - Returns "Transform", "Crop", "Text", "Playback", "Trim" or a custom value. ```kotlin @UnstableEngineApi fun isInteractionHappening(): Boolean ``` If an user interaction is happening, e.g., a resize edit with a drag handle or a touch gesture. - Returns true if an interaction is happening. ## Cursor ```kotlin fun getTextCursorPositionInScreenSpaceX(): Float ``` Get the current text cursor's x position in screen space. - Returns the text cursor's x position in screen space. ```kotlin fun getTextCursorPositionInScreenSpaceY(): Float ``` Get the current text cursor's y position in screen space. - Returns the text cursor's y position in screen space. ## Full Code Here's the full code: ```kotlin engine.editor.onStateChanged() .onEach { println("Editor history has changed") } .launchIn(CoroutineScope(Dispatchers.Main)) // Native modes: "Transform", "Crop", "Text" engine.editor.setEditMode("Crop") engine.editor.getEditMode() // "Crop" engine.editor.isInteractionHappening() // Query information about the text cursor position engine.editor.getTextCursorPositionInScreenSpaceX() engine.editor.getTextCursorPositionInScreenSpaceY() ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Editing Workflow" description: "Control editing access with Creator and Adopter roles, each offering tailored permissions and UI constraints." platform: android url: "https://img.ly/docs/cesdk/android/concepts/editing-workflow-032d27/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Editing Workflow](https://img.ly/docs/cesdk/android/concepts/editing-workflow-032d27/) --- ## Roles User roles allow the CE.SDK to change and adapt its UI layout and functionality to provide the optimal editing experience for a specific purpose. ### Creator The `Creator` role is the most powerful and least restrictive role that is offered by the CE.SDK. Running the editor with this role means that there are no limits to what the user can do with the loaded scene. Elements can be added, moved, deleted, and modified. All types of controls for modifying the selected elements are shown inside of the inspector. ### Adopter The `Adopter` role allows new elements to be added and modified. Existing elements of a scene are only modifiable based on the set of constraints that the `Creator` has manually enabled. This provides the `Adopter` with a simpler interface that is reduced to only the properties that they should be able to change and prevents them from accidentally changing or deleting parts of a design that should not be modified. An example use case for how such a distinction between `Creator` and `Adopter` roles can provide a lot of value is the process of designing business cards. A professional designer (using the `Creator` role) can create a template design of the business card with the company name, logo, colors, etc. They can then use the constraints to make only the name text editable for non-creators. Non-designers (either the employees themselves or the HR department) can then easily open the design in a CE.SDK instance with the `Adopter` role and are able to quickly change the name on the business card and export it for printing, without a designer having to get involved. ### Role customization Roles in the CE.SDK are sets of global scopes and settings. When changing the role via the `setRole` command in the EditorAPI, the internal defaults for that role are applied as described in the previous sections. The CE.SDK and Engine provide a `onRoleChanged` callback subscription on the EditorAPI. Callbacks registered here are invoked whenever the role changes and can be used to configure additional settings or adjust the default scopes and settings. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Events" description: "Subscribe to block creation, update, and deletion events to track changes in your CE.SDK scene." platform: android url: "https://img.ly/docs/cesdk/android/concepts/events-353f97/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Events](https://img.ly/docs/cesdk/android/concepts/events-353f97/) --- ```kotlin reference-only val coroutineScope = CoroutineScope(Dispatchers.Main) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Star)) engine.block.setFill(block, fill = engine.block.createFill(FillType.Color)) engine.block.appendChild(parent = page, child = block) engine.event.subscribe(listOf(block)) .onEach { events -> events.forEach { event -> println("Event: ${event.type} ${event.block}") if (engine.block.isValid(event.block)) { val type = engine.block.getType(event.block) println("Block type: $type") } } } .launchIn(coroutineScope) coroutineScope.launch { delay(1000) engine.block.setRotation(block, radians = 0.5F * PI.toFloat()) delay(1000) engine.block.destroy(block) delay(1000) } ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to subscribe to creation, update, and destruction events of design blocks. ## Subscribing to Events The event API provides a single function to subscribe to design block events. The types of events are: - `Created`: The design block was just created. - `Updated`: A property of the design block was updated. - `Destroyed`: The design block was destroyed. Note that a destroyed block will have become invalid and trying to use Block API functions on it will result in an exception. You can always use the Block API's `isValid` function to verify whether a block is valid before use. All events that occur during an engine update are batched, deduplicated, and always delivered at the very end of the engine update. Deduplication means you will receive at most one `Updated` event per block per subscription, even though there could potentially be multiple updates to a block during the engine update. To be clear, this also means the order of the event list provided to your event callback won't reflect the actual order of events within an engine update. ```kotlin fun subscribe(blocks: List = emptyList()): Flow> ``` Subscribe to block life-cycle events - `blocks`: a list of blocks to filter events by. If the list is empty, events for every block are sent. ## Full Code Here's the full code: ```kotlin val coroutineScope = CoroutineScope(Dispatchers.Main) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Star)) engine.block.setFill(block, fill = engine.block.createFill(FillType.Color)) engine.block.appendChild(parent = page, child = block) engine.event.subscribe(listOf(block)) .onEach { events -> events.forEach { event -> println("Event: ${event.type} ${event.block}") if (engine.block.isValid(event.block)) { val type = engine.block.getType(event.block) println("Block type: $type") } } } .launchIn(coroutineScope) coroutineScope.launch { delay(1000) engine.block.setRotation(block, radians = 0.5F * PI.toFloat()) delay(1000) engine.block.destroy(block) delay(1000) } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Headless" description: "Use the engine directly, without any prebuilt UI." platform: android url: "https://img.ly/docs/cesdk/android/concepts/headless-mode-24ab98/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Headless Mode](https://img.ly/docs/cesdk/android/concepts/headless-mode-24ab98/) --- ```kotlin file=@cesdk_android_examples/engine-guides-create-scene-from-scratch/CreateSceneFromScratch.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun createSceneFromScratch( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope( Dispatchers.Main, ).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block = block, shape = engine.block.createShape(ShapeType.Star)) engine.block.setFill(block = block, fill = engine.block.createFill(FillType.Color)) engine.block.appendChild(parent = page, child = block) engine.stop() } ``` Headless Mode lets you use the CreativeEditor SDK's Engine directly. No prebuilt editor UI required. You initialize the Engine, load or build scenes programmatically, and export to images/PDF/video entirely in code. This is ideal for custom UIs, automation, server-triggered rendering, or batch exports. ## What You'll Learn - What "Headless / Engine-only" means and how it differs from the UI editor. - When to choose Headless Mode (and when not to). - How to initialize the Engine for headless use in Kotlin. - How to load/build a scene and modify blocks programmatically. - How to export (PNG/JPEG/PDF, and notes on video) without launching the UI. ## When to Use It **Pick Headless Mode when you need:** - A fully custom UI: You're building your own editing interface or integrating into an existing app layout. - Programmatic rendering: Generate images/PDFs from templates or data—no user interaction. - Automation & batch work: Merge data at scale, pre-render previews, or run background exports. - Export-only flows: Quickly render a scene without ever opening the editor. **Avoid Headless Mode when:** - You want turnkey editing UX out of the box (use the standard Editor for that). - You don't want to create selection, gestures, or tool panels yourself. ### Quick Comparison |Scenario | Headless (Engine-only) | Standard UI Editor | |---|---|---| |Automate design generation from code|✅|❌| |Export scenes without user interaction|✅|❌| |Let users visually edit with ready-made panels|❌|✅| |Build a custom editor interface|✅|⭘ (extend via config)| ### How Headless Mode Works With the prebuilt editors, user actions call the **Engine** API through the UI. In Headless Mode, you start the Engine and work entirely in Kotlin to: - Scene management: create/load scenes; add pages; read/write properties - Blocks: create text/graphics/shapes; set fills, sizes, transforms; append to parents - Assets: register sources, resolve URIs, add media programmatically - Templates & data: load scene archives/JSON, update text variables, swap images - Export: render blocks or pages to PNG/JPEG/PDF (and trigger video exports where appropriate) ### Initialize the Engine in Headless Mode (Kotlin) ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Engine class HeadlessRenderer { private lateinit var engine: Engine fun startEngine(license: String, userId: String) = CoroutineScope(Dispatchers.Main).launch { engine = Engine.getInstance(id = "ly.img.engine.example") engine.start( license = license, userId = userId ) // Bind offscreen for headless rendering (no UI needed) engine.bindOffscreen(width = 1080, height = 1920) } } ``` This is "headless" because you never instantiate or present the editor UI. You only create an Engine and use its APIs. The `bindOffscreen` method creates an offscreen rendering surface—perfect for headless scenarios where no visible View is needed. ### Create and Export a Scene (Kotlin) Below is a minimal, end-to-end example that works with the preceding class to: - Create a scene with a single page. - Add a rectangle filled with a remote image. - Add a text block. - Export the page as PNG data. ```kotlin import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.DesignBlockType import ly.img.engine.FillType import ly.img.engine.MimeType import ly.img.engine.ShapeType import ly.img.engine.SizeMode import java.nio.ByteBuffer data class ExportResult( val pngData: ByteBuffer, val suggestedFilename: String ) suspend fun HeadlessRenderer.buildAndExport(): ExportResult = withContext(Dispatchers.Main) { // 1) Create an empty scene and a page val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) // Set page size engine.block.setWidth(page, value = 800f) engine.block.setHeight(page, value = 600f) // Attach page to scene root engine.block.appendChild(parent = scene, child = page) // 2) Add an image rectangle val rect = engine.block.create(DesignBlockType.Graphic) val shape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(rect, shape = shape) val imageFill = engine.block.createFill(FillType.Image) engine.block.setString( block = imageFill, property = "fill/image/imageFileURI", // Use your own asset URL or registered source URI value = "https://img.ly/static/ubq_samples/sample_1.jpg" ) engine.block.setFill(rect, fill = imageFill) // Position & size the rect engine.block.setPositionX(rect, value = 100f) engine.block.setPositionY(rect, value = 100f) engine.block.setWidth(rect, value = 400f) engine.block.setHeight(rect, value = 300f) engine.block.appendChild(parent = page, child = rect) // 3) Add text val text = engine.block.create(DesignBlockType.Text) engine.block.replaceText(text, text = "Hello, From Headless Mode!") engine.block.setPositionX(text, value = 100f) engine.block.setPositionY(text, value = 450f) engine.block.setWidthMode(text, mode = SizeMode.AUTO) engine.block.appendChild(parent = page, child = text) // 4) Export the page to PNG val pngData = engine.block.export(page, mimeType = MimeType.PNG) ExportResult(pngData = pngData, suggestedFilename = "headless-output.png") } ``` ### Saving the File (optional) After creating the image, you may want to save it. Here is a minimal code example to save the file to the app's files directory and return the file path. ```kotlin import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File suspend fun savePNG(context: Context, result: ExportResult): File = withContext(Dispatchers.IO) { val outputDir = context.filesDir val destinationFile = File(outputDir, result.suggestedFilename) destinationFile.outputStream().channel.use { channel -> channel.write(result.pngData) } destinationFile } ``` ![The output image from the example code.](assets/headless-ios-1-161.png) The example code creates this `.png` image. Some variations to the preceding code depending on your workflow might be: - Export as different file format such as `.jpeg` or `.pdf` by changing the `mimeType` argument. - Export a sub-tree instead of the entire page. Passing a block's ID exports just that block and its children. ### Working with Assets (Headless) - Default sources: `engine.addDefaultAssetSources()` wires up built-in sources so URIs like `"fill/image/imageFileURI"` can resolve to remote/local assets. - Your own sources: In production, you'll typically register a custom source (from your server, local storage, or device gallery) and then set block properties to URIs your source resolves. - Local files: Use `file://` URLs or Android content URIs that your asset source understands. - Fonts: Bundle or register your fonts the same way. The Engine needs them available for text layout before export. ### Templates & Data-Driven Generation Headless Mode pairs well with: - Scene templates (ZIP/JSON): load and then swap images or set text (update `text/text` or variable placeholders). - Text variables / placeholders: bind your app's data model to text fields and render many variants in a loop. - Batching: loop through data rows → set properties → export → repeat. > **Note:** Keep content keys ("text/text", "fill/image/imageFileURI", etc.) stable across templates or use helper methods, so your code doesn't change when designers iterate. ## Troubleshooting **❌ I'm getting nothing on export (empty data or errors)**: - Verify you export a renderable block (the page or scene root's child). - Ensure remote image URIs are reachable and network permissions are set in AndroidManifest.xml. - For debugging, try a known good URL, then swap. **❌ Images don't load or are missing in output**: - Confirm your asset source can resolve the URI you set. - If using remote URLs, ensure INTERNET permission is declared in AndroidManifest.xml. - Check CORS/network security configuration if using custom domains. **❌ Text looks wrong or uses fallback fonts**: - The Engine needs the exact font used by the text style. Register/bundle the font and ensure it's discoverable before export. **❌ Memory spikes on big batches**: - Reuse a single Engine instance when possible. - Export, write to disk, and release large buffers before rendering the next item. - Call `engine.stop()` when completely done to free resources. ## Next Steps Now that you understand the basics of headless mode, below are some topics to help you expand your knowledge: - [Templates & Variables](https://img.ly/docs/cesdk/android/create-templates/overview-4ebe30/) – Design tokenized templates and drive them from data. - [Exporting](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) – PNG/JPEG/PDF exports, plus format options and best practices. - [Standard Editor vs Headless](https://img.ly/docs/cesdk/android/engine-interface-6fb7cf/) – If you need turnkey UI, start here and decide whether to drop to headless for specific flows. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Working With Resources" description: "Preload all resources for blocks or scenes in CE.SDK to improve performance and avoid runtime delays." platform: android url: "https://img.ly/docs/cesdk/android/concepts/resources-a58d71/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Resources](https://img.ly/docs/cesdk/android/concepts/resources-a58d71/) --- By default, a scene's resources are loaded on-demand. You can manually trigger the loading of all resources in a scene of for specific blocks by calling `forceLoadResources`. Any set of blocks can be passed as argument and whatever resources these blocks require will be loaded. In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to forcibly pre-load all resources contained in a scene. ```kotlin suspend fun forceLoadResources(blocks: List) ``` Begins loading the resources of the given blocks and their children. If the resource had been loaded earlier and resulted in an error, it will be reloaded. Note: This function is useful for preloading resources before they are needed. Warning: For elements with a source set, all elements in the source set will be loaded. - `blocks`: the blocks whose resources should be loaded. The given blocks don't require to have resources and can have children blocks (e.g. a scene block or a page block). ### Full Code Here's the full code: ```kotlin val scene = requireNotNull(engine.scene.get()) // Forcing all resources of all the blocks in a scene or the resources of graphic block to load engine.block.forceLoadResources(listOf(scene)) val graphics = engine.block.findByType(DesignBlockType) engine.block.forceLoadResources(graphics) ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Scenes" description: "Scenes act as the root container for blocks and define the full design structure in CE.SDK." platform: android url: "https://img.ly/docs/cesdk/android/concepts/scenes-e8596d/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Scenes](https://img.ly/docs/cesdk/android/concepts/scenes-e8596d/) --- ```kotlin file=@cesdk_android_examples/engine-guides-modifying-scenes/ModifyingScenes.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun modifyingScenes( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) // In engine only mode we have to create our own scene and page. if (engine.scene.get() == null) { val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) } // Find all pages in our scene. val pages = engine.block.findByType(DesignBlockType.Page) // Use the first page we found. val page = pages.first() // Create a graphic block and add it to the scene's page. val block = engine.block.create(DesignBlockType.Graphic) val fill = engine.block.createFill(FillType.Image) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setFill(block = block, fill = fill) engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/imgly_logo.jpg", ) // The content fill mode 'Contain' ensures the entire image is visible. engine.block.setEnum( block = block, property = "contentFill/mode", value = "Contain", ) engine.block.appendChild(parent = page, child = block) // Zoom the scene's camera on our page. engine.scene.zoomToBlock(page) engine.stop() } ``` Commonly, a scene contains several pages which in turn contain any other blocks such as images and texts. A block (or design block) is the main building unit in CE.SDK. Blocks are organized in a hierarchy through parent-child relationships. A scene is a specialized block that acts as the root of this hierarchy. At any time, the engine holds only a single scene. Loading or creating a scene will replace the current one. ## Interacting With The Scene ### Creating or Using an Existing Scene When using the Engine's API in the context of the CE.SDK editor, there's already an existing scene. You can obtain a handle to this scene by calling the [SceneAPI](https://img.ly/docs/cesdk/android/concepts/scenes-e8596d/)'s `fun get(): DesignBlock?` method. However, when using the Engine on its own you first have to create a scene, e.g. using `fun create(): DesignBlock`. See the [Creating Scenes](https://img.ly/docs/cesdk/android/open-the-editor-23a1db/) guide for more details and options. ```kotlin // In engine only mode we have to create our own scene and page. if (engine.scene.get() == null) { val scene = engine.scene.create() ``` Next, we need a page to place our blocks on. The scene automatically arranges its pages either in a vertical (the default) or horizontal layout. Again in the context of the editor, there's already an existing page. To fetch that page call the [BlockAPI](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/)'s `fun findByType(blockType: DesignBlockType): List` method and use the first element of the returned list. When only using the engine, you have to create a page yourself and append it to the scene. To do that create the page using `fun fun create(): DesignBlock` and append it to the scene with `fun appendChild(parent: DesignBlock, child: DesignBlock)`. ```kotlin val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) } // Find all pages in our scene. val pages = engine.block.findByType(DesignBlockType.Page) // Use the first page we found. val page = pages.first() ``` At this point, you should have a handle to an existing scene as well as a handle to its page. Now it gets interesting when we start to add different types of blocks to the scene's page. ### Modifying the Scene As an example, we create a graphic block using the [BlockAPI](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/)'s `create()` method which we already used for creating our page. Then we set a rect shape and an image fill to this newly created block to give it a visual representation. To see what other kinds of blocks are available see the [Block Types](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) in the API Reference. ```kotlin // Create a graphic block and add it to the scene's page. val block = engine.block.create(DesignBlockType.Graphic) val fill = engine.block.createFill(FillType.Image) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setFill(block = block, fill = fill) ``` We set a property of our newly created image fill by giving it a URL to reference an image file from. We also make sure the entire image stays visible by setting the block's content fill mode to `'Contain'`. To learn more about block properties check out the [Block Properties](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) API Reference. ```kotlin engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/imgly_logo.jpg", ) // The content fill mode 'Contain' ensures the entire image is visible. engine.block.setEnum( block = block, property = "contentFill/mode", value = "Contain", ) ``` And finally, for our image to be visible we have to add it to our page using `appendChild`. ```kotlin engine.block.appendChild(parent = page, child = block) ``` To frame everything nicely and put it into view we direct the scene's camera to zoom on our page. ```kotlin // Zoom the scene's camera on our page. engine.scene.zoomToBlock(page) ``` ### Full Code Here's the full code snippet for interacting with the scene: ```kotlin import kotlinx.coroutines.* import ly.img.engine.* fun modifyingScenes( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) // In engine only mode we have to create our own scene and page. if (engine.scene.get() == null) { val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) } // Find all pages in our scene. val pages = engine.block.findByType(DesignBlockType.Page) // Use the first page we found. val page = pages.first() // Create a graphic block and add it to the scene's page. val block = engine.block.create(DesignBlockType.Graphic) val fill = engine.block.createFill(FillType.Image) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setFill(block = block, fill = fill) engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/imgly_logo.jpg", ) // The content fill mode 'Contain' ensures the entire image is visible. engine.block.setEnum( block = block, property = "contentFill/mode", value = "Contain", ) engine.block.appendChild(parent = page, child = block) // Zoom the scene's camera on our page. engine.scene.zoomToBlock(page) engine.stop() } ``` ## Exploring Scene Contents Using The Scene API Learn how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to explore scene contents through the `scene` API. ```kotlin fun getPages(): List ``` Get the sorted list of pages in the scene. - Returns the sorted list of pages in the scene. ```kotlin fun getCurrentPage(): DesignBlock? ``` Get the current page, i.e., the page of the first selected element if this page is at least 25% visible or, otherwise, the page nearest to the viewport center. - Returns the current page in the scene or null. ```kotlin fun findNearestToViewPortCenterByType(blockType: DesignBlockType): List ``` Finds all blocks with the given type sorted by distance to viewport center. - `blockType`: the type to search for. - Returns a list of block ids sorted by distance to viewport center. ```kotlin fun findNearestToViewPortCenterByKind(blockKind: String): List ``` Finds all blocks with the given kind sorted by distance to viewport center. - `blockKind`: the kind to search for. - Returns a list of block ids sorted by distance to viewport center. ```kotlin fun setDesignUnit(designUnit: DesignUnit) ``` Converts all values of the current scene into the given design unit. - `designUnit`: the new design unit of the scene. ```kotlin fun getDesignUnit(): DesignUnit ``` Returns the design unit of the current scene. - Returns The current design unit. ### Full Code Here's the full code snippet for exploring a scene's contents using the `scene` API: ```kotlin val pages = engine.scene.getPages() val currentPage = engine.scene.getCurrentPage(); val nearestPageByType = engine.scene.findNearestToViewPortCenterByType(DesignBlockType.Page).first(); val nearestImageByKind = engine.sce.findNearestToViewPortCenterByKind("image").first(); engine.scene.setDesignUnit(DesignUnit.PIXEL) /* Now returns DesignUnit.PIXEL */ engine.scene.getDesignUnit() ``` ## Exploring Scene Contents Using The Block API Learn how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to explore scenes through the `block` API. ### Functions ```kotlin fun findAll(): List ``` Return all blocks currently known to the engine. - Returns a list of block ids. ```kotlin fun findAllPlaceholders(): List ``` Return all placeholder blocks in the current scene. - Returns a list of block ids. ```kotlin fun findByType(type: DesignBlockType): List ``` Finds all design blocks with the given type. - `type`: the type to search for. - Returns a list of block ids. ```kotlin fun findByType(type: ShapeType): List ``` Finds all shape blocks with the given type. - `type`: the type to search for. - Returns a list of block ids. ```kotlin fun findByType(type: EffectType): List ``` Finds all effect blocks with the given type. - `type`: the type to search for. - Returns a list of block ids. ```kotlin fun findByType(type: BlurType): List ``` Finds all blur blocks with the given type. - `type`: the type to search for. - Returns a list of block ids. ```kotlin fun findByKind(blockKind: String): List ``` Finds all blocks with the given kind. - `blockKind`: the kind to search for. - Returns a list of block ids. ```kotlin fun findByName(name: String): List ``` Finds all blocks with the given name. - `name`: the name to search for. - Returns a list of block ids. ### Full Code Here's the full code snippet for exploring a scene's contents using the `block` API: ```kotlin val allIds = engine.block.findAll() val allPlaceholderIds = engine.block.findAllPlaceholders() val allPages = engine.block.findByType(DesignBlockType.Page) val allImageFills = engine.block.findByType(FillType.Image) val allStarShapes = engine.block.findByType(ShapeType.Star) val allHalfToneEffects = engine.block.findByType(EffectType.HalfTone) val allUniformBlurs = engine.block.findByType(BlurType.Uniform) val allStickers = engine.block.findByKind("sticker") val ids = engine.block.findByName("someName") ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Undo and History" description: "Manage undo and redo stacks in CE.SDK using multiple histories, callbacks, and API-based controls." platform: android url: "https://img.ly/docs/cesdk/android/concepts/undo-and-history-99479d/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Concepts](https://img.ly/docs/cesdk/android/concepts-c9ff51/) > [Undo and History](https://img.ly/docs/cesdk/android/concepts/undo-and-history-99479d/) --- ```kotlin reference-only // Manage history stacks val newHistory = engine.editor.createHistory() val oldHistory = engine.editor.getActiveHistory() engine.editor.setActiveHistory(newHistory) engine.editor.destroyHistory(oldHistory) engine.editor.onHistoryUpdated() .onEach { println("Editor history updated") } .launchIn(CoroutineScope(Dispatchers.Main)) // Push a new state to the undo stack engine.editor.addUndoStep() // Perform an undo, if possible. if (engine.editor.canUndo()) { engine.editor.undo() } // Perform a redo, if possible. if (engine.editor.canRedo()) { engine.editor.redo() } ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to undo and redo steps in the `editor` API. ## Functions ```kotlin fun createHistory(): History ``` - Brief: Create a history which consists of an undo/redo stack for editing operations. There can be multiple. But only one can be active at a time. - Returns the handle to the created history. ```kotlin fun destroyHistory(history: History) ``` Destroy the given history, returns an error if the handle doesn't refer to a history. - `history`: the history to be destroyed. ```kotlin fun setActiveHistory(history: History) ``` Mark the given history as active, returns an error if the handle doesn't refer to a history. All other histories get cleared from the active state. Undo/redo operations only apply to the active history. - `history`: the history to be marked as active. ```kotlin fun getActiveHistory(): History ``` Get the handle to the currently active history. If there's none it will be created. - Returns the handle to the active history. ```kotlin fun addUndoStep() ``` Adds a new history state to the stack, if undoable changes were made. ```kotlin fun undo() ``` Undo one step in the history if an undo step is available. ```kotlin fun canUndo(): Boolean ``` If an undo step is available. - Returns true if an undo step is available. ```kotlin fun redo() ``` Redo one step in the history if a redo step is available. ```kotlin fun canRedo(): Boolean ``` If a redo step is available. - Returns true if a redo step is available. ```kotlin fun onHistoryUpdated(): Flow ``` Subscribe to changes to the undo/redo history. - Returns flow of history updates. ## Full Code Here's the full code: ```kotlin // Manage history stacks val newHistory = engine.editor.createHistory() val oldHistory = engine.editor.getActiveHistory() engine.editor.setActiveHistory(newHistory) engine.editor.destroyHistory(oldHistory) engine.editor.onHistoryUpdated() .onEach { println("Editor history updated") } .launchIn(CoroutineScope(Dispatchers.Main)) // Push a new state to the undo stack engine.editor.addUndoStep() // Perform an undo, if possible. if (engine.editor.canUndo()) { engine.editor.undo() } // Perform a redo, if possible. if (engine.editor.canRedo()) { engine.editor.redo() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Configuration" description: "Learn how to configure CE.SDK to match your application's functional, visual, and performance requirements." platform: android url: "https://img.ly/docs/cesdk/android/configuration-2c1c3d/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Configuration](https://img.ly/docs/cesdk/android/configuration-2c1c3d/) --- ```kotlin file=@cesdk_android_examples/editor-guides-configuration-basics/BasicEditorSolution.kt reference-only import android.net.Uri import androidx.compose.runtime.Composable import androidx.navigation.NavHostController import ly.img.editor.DesignEditor import ly.img.editor.EngineConfiguration import ly.img.editor.core.engine.EngineRenderTarget import ly.img.editor.rememberForDesign // Add this composable to your NavHost @Composable fun BasicEditorSolution(navController: NavHostController) { val engineConfiguration = EngineConfiguration.rememberForDesign( license = "", // pass null or empty for evaluation mode with watermark userId = "", baseUri = Uri.parse("file:///android_asset/"), sceneUri = EngineConfiguration.defaultDesignSceneUri, renderTarget = EngineRenderTarget.SURFACE_VIEW, ) DesignEditor(engineConfiguration = engineConfiguration) { // You can set result here navController.popBackStack() } } ``` In this example, we will show you how to make basic configurations for the mobile editor. The example is based on the `Design Editor`, however, it is exactly the same for all the other [solutions](https://img.ly/docs/cesdk/android/prebuilt-solutions-d0ed07/). ## Configuration All the basic configuration settings are part of the `EngineConfiguration`. - `license` - the license to activate the [Engine](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) with. ```kotlin highlight-configuration-license license = "", // pass null or empty for evaluation mode with watermark ``` - `userId` - an optional unique ID tied to your application's user. This helps us accurately calculate monthly active users (MAU). Especially useful when one person uses the app on multiple devices with a sign-in feature, ensuring they're counted once. Providing this aids in better data accuracy. The default value is `null`. ```kotlin highlight-configuration-userId userId = "", ``` - `baseUri` - is used to initialize the engine's [setting](https://img.ly/docs/cesdk/android/settings-970c98/) before the editor's [callback](https://img.ly/docs/cesdk/android/user-interface/events-514b70/) is run. It is the foundational URI for constructing absolute paths from relative ones. For example, setting it to the Android assets directory allows loading resources directly from there: `file:///android_asset/`. This URI enables the loading of specific scenes or assets using their relative paths. The default value is pointing at the versioned IMG.LY CDN but it should be changed in production environments. ```kotlin highlight-configuration-baseUri baseUri = Uri.parse("file:///android_asset/"), ``` - `sceneUri` - the [scene](https://img.ly/docs/cesdk/android/open-the-editor/blank-canvas-18ff05/) URI to load content within the editor. Note that this configuration is only available in `EngineConfiguration.rememberFor{solution-name}` helper functions. This URI is used to load the scene in `EdiorConfiguration.onCreate`, therefore, you can configure the scene you load without helper functions too: simply invoke `EditorDefaults.onCreate(engine, sceneUri, eventHandler)` in `EdiorConfiguration.onCreate`. By default, helper functions load the scenes that are available at `EdiorConfiguration.default{solution-name}Scene`. Normally, you should not modify the `sceneUri`, however, you may want to save/restore the editing progress for your customers. If that is the case, you should [save the scene](https://img.ly/docs/cesdk/android/export-save-publish/save-c8b124/) in one of the [callbacks](https://img.ly/docs/cesdk/android/user-interface/events-514b70/), then provide the URI of the newly saved scene when your customer opens the editor next time. ```kotlin highlight-configuration-sceneUri sceneUri = EngineConfiguration.defaultDesignSceneUri, ``` - `renderTarget` - the target which should be used by the [Engine](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) to render. The engine is able to render on both [SurfaceView](https://developer.android.com/reference/android/view/SurfaceView) and [TextureView](https://developer.android.com/reference/android/view/TextureView). The default value is `EngineRenderTarget.SURFACE_VIEW`. ```kotlin highlight-configuration-renderTarget renderTarget = EngineRenderTarget.SURFACE_VIEW, ``` ## Full Code Here's the full code: ```kotlin import android.net.Uri import androidx.compose.runtime.Composable import androidx.navigation.NavHostController import ly.img.editor.DesignEditor import ly.img.editor.EngineConfiguration import ly.img.editor.core.engine.EngineRenderTarget import ly.img.editor.rememberForDesign // Add this composable to your NavHost @Composable fun BasicEditorSolution(navController: NavHostController) { val engineConfiguration = EngineConfiguration.rememberForDesign( license = "", userId = "", baseUri = Uri.parse("file:///android_asset/"), sceneUri = EngineConfiguration.defaultDesignSceneUri, renderTarget = EngineRenderTarget.SURFACE_VIEW, ) DesignEditor(engineConfiguration = engineConfiguration) { // You can set result here navController.popBackStack() } } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Conversion" description: "Convert designs into different formats such as PDF, PNG, MP4, and more using CE.SDK tools." platform: android url: "https://img.ly/docs/cesdk/android/conversion-c3fbb3/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Conversion](https://img.ly/docs/cesdk/android/conversion-c3fbb3/) --- --- ## Related Pages - [Overview](https://img.ly/docs/cesdk/android/conversion/overview-44dc58/) - Convert designs into different formats such as PDF, PNG, MP4, and more using CE.SDK tools. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Overview" description: "Convert designs into different formats such as PDF, PNG, MP4, and more using CE.SDK tools." platform: android url: "https://img.ly/docs/cesdk/android/conversion/overview-44dc58/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Conversion](https://img.ly/docs/cesdk/android/conversion-c3fbb3/) > [Overview](https://img.ly/docs/cesdk/android/conversion/overview-44dc58/) --- CreativeEditor SDK (CE.SDK) allows you to export designs into a variety of formats, making it easy to prepare assets for web publishing, printing, storage, and other workflows. You can trigger conversions either programmatically through the SDK’s API or manually using the built-in export options available in the UI. [Explore Demos](https://img.ly/showcases/cesdk?tags=android) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) ## Supported Input and Output Formats CE.SDK accepts a range of input formats when working with designs, including: When it comes to exporting or converting designs, the SDK supports the following output formats: Each format serves different use cases, giving you the flexibility to adapt designs for your application’s needs. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create Compositions" description: "Combine and arrange multiple elements to create complex, multi-page, or layered design compositions." platform: android url: "https://img.ly/docs/cesdk/android/create-composition-db709c/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Compositions](https://img.ly/docs/cesdk/android/create-composition-db709c/) --- --- ## Related Pages - [Overview](https://img.ly/docs/cesdk/android/create-composition/overview-5b19c5/) - Combine and arrange multiple elements to create complex, multi-page, or layered design compositions. - [Positioning and Alignment](https://img.ly/docs/cesdk/android/insert-media/position-and-align-cc6b6a/) - Precisely position, align, and distribute objects using guides, snapping, and alignment tools. - [Group and Ungroup Objects](https://img.ly/docs/cesdk/android/create-composition/group-and-ungroup-62565a/) - Group multiple elements to move or transform them together; ungroup to edit them individually. - [Layer Management](https://img.ly/docs/cesdk/android/create-composition/layer-management-18f07a/) - Organize design elements using a layer stack for precise control over stacking and visibility. - [Blend Modes](https://img.ly/docs/cesdk/android/create-composition/blend-modes-ad3519/) - Apply blend modes to elements to control how colors and layers interact visually. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Blend Modes" description: "Apply blend modes to elements to control how colors and layers interact visually." platform: android url: "https://img.ly/docs/cesdk/android/create-composition/blend-modes-ad3519/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Compositions](https://img.ly/docs/cesdk/android/create-composition-db709c/) > [Blend Modes](https://img.ly/docs/cesdk/android/create-composition/blend-modes-ad3519/) --- ```kotlin reference-only engine.block.supportsOpacity(image) engine.block.setOpacity(image, value = 0.5F) engine.block.getOpacity(image) engine.block.supportsBlendMode(image) engine.block.setBlendMode(image, blendMode = BlendMode.MULTIPLY) engine.block.getBlendMode(image) if (engine.block.supportsBackgroundColor(image)) { engine.block.setBackgroundColor(page, Color.fromRGBA(r = 1F, g = 0F, b = 0F, a = 1F) // Red engine.block.getBackgroundColor(page) engine.block.setBackgroundColorEnabled(page, enabled = true) engine.block.isBackgroundColorEnabled(page) } ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to modify a blocks appearance through the `block` API. ## Common Properties Common properties are properties that occur on multiple block types. For instance, fill color properties are available for all the shape blocks and the text block. That's why we built convenient setter and getter functions for these properties. So you don't have to use the generic setters and getters and don't have to provide a specific property path. There are also `has*` functions to query if a block supports a set of common properties. ### Opacity Set the translucency of the entire block. ```kotlin fun supportsOpacity(block: DesignBlock): Boolean ``` Query if the given block has an opacity. - `block`: the block to query. - Returns true if the block has an opacity, false otherwise. ```kotlin fun setOpacity( block: DesignBlock, @FloatRange(from = 0.0, to = 1.0) value: Float, ) ``` Set the opacity of the given design block. Required scope: "layer/opacity" - `block`: the block whose opacity should be set. - `value`: the opacity to be set. The valid range is 0 to 1. ```kotlin @FloatRange(from = 0.0, to = 1.0) fun getOpacity(block: DesignBlock): Float ``` Get the opacity of the given design block. - `block`: the block whose opacity should be queried. - Returns the opacity. ### Blend Mode Define the blending behaviour of a block. ```kotlin fun supportsBlendMode(block: DesignBlock): Boolean ``` Query if the given block has a blend mode. - `block`: the block to query. - Returns true if the block has a blend mode, false otherwise. ```kotlin fun setBlendMode( block: DesignBlock, blendMode: BlendMode, ) ``` Set the blend mode of the given design block. Required scope: "layer/blendMode" - `block`: the block whose blend mode should be set. - `blendMode`: the blend mode to be set. ```kotlin fun getBlendMode(block: DesignBlock): BlendMode ``` Get the blend mode of the given design block. - `block`: the block whose blend mode should be queried. - Returns the blend mode. ### Background Color Manipulate the background of a block. To understand the difference between fill and background color take the text block. The glyphs of the text itself are colored by the fill color. The rectangular background given by the bounds of the block on which the text is drawn is colored by the background color. ```kotlin fun supportsBackgroundColor(block: DesignBlock): Boolean ``` Query if the given block has background color properties. - `block`: the block to query. - Returns true if the block has background color properties, false otherwise. ```kotlin fun setBackgroundColor( block: DesignBlock, color: RGBAColor, ) ``` Set the background color of the given design block. Required scope: "fill/change" - `block`: the block whose background color should be set. - `color`: the color to set. ```kotlin fun getBackgroundColor(block: DesignBlock): RGBAColor ``` Get the background color of the given design block. - `block`: the block whose background color should be queried. - Returns the background color. ```kotlin fun setBackgroundColorEnabled( block: DesignBlock, enabled: Boolean, ) ``` Enable or disable the background of the given design block. Required scope: "fill/change" - `block`: the block whose background should be enabled or disabled. - `enabled`: if true, the background will be enabled. ```kotlin fun isBackgroundColorEnabled(block: DesignBlock): Boolean ``` Query if the background of the given design block is enabled. - `block`: the block whose background state should be queried. - Returns true if background is enabled, false otherwise. ## Full Code Here's the full code: ```kotlin engine.block.supportsOpacity(image) engine.block.setOpacity(image, value = 0.5F) engine.block.getOpacity(image) engine.block.supportsBlendMode(image) engine.block.setBlendMode(image, blendMode = BlendMode.MULTIPLY) engine.block.getBlendMode(image) if (engine.block.supportsBackgroundColor(image)) { engine.block.setBackgroundColor(page, Color.fromRGBA(r = 1F, g = 0F, b = 0F, a = 1F) // Red engine.block.getBackgroundColor(page) engine.block.setBackgroundColorEnabled(page, enabled = true) engine.block.isBackgroundColorEnabled(page) } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Group and Ungroup Objects" description: "Group multiple elements to move or transform them together; ungroup to edit them individually." platform: android url: "https://img.ly/docs/cesdk/android/create-composition/group-and-ungroup-62565a/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Compositions](https://img.ly/docs/cesdk/android/create-composition-db709c/) > [Group and Ungroup Objects](https://img.ly/docs/cesdk/android/create-composition/group-and-ungroup-62565a/) --- ```kotlin reference-only // Create blocks and append to scene val member1 = engine.block.create(DesignBlockType.Graphic) val member2 = engine.block.create(DesignBlockType.Graphic) engine.block.appendChild(scene, child = member1) engine.block.appendChild(scene, child = member2) // Check whether the blocks may be grouped if (engine.block.isGroupable(listOf(member1, member2))) { val group = engine.block.group(listOf(member1, member2)) engine.block.setSelected(group, selected = true) engine.block.enterGroup(group) engine.block.setSelected(member1, selected = true) engine.block.exitGroup(member1) engine.block.ungroup(group) } ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to group blocks through the `block` API. Groups form a cohesive unit. ## Grouping Multiple blocks can be grouped together to form a cohesive unit. A group being a block, it can itself be part of a group. > **Note:** **What cannot be grouped*** A scene > * A block that already is part of a group ```kotlin fun isGroupable(blocks: List): Boolean ``` Confirms that a given set of blocks can be grouped together. - `blocks`: a non-empty array of block ids. - Returns whether the blocks can be grouped together. ```kotlin fun group(blocks: List): DesignBlock ``` Group blocks together. - `blocks`: a non-empty array of block ids. - Returns the block id of the created group. ```kotlin fun ungroup(block: DesignBlock) ``` Ungroups a group. - `block`: the group id from a previous call to `group`. ```kotlin fun enterGroup(block: DesignBlock) ``` Changes selection from selected group to a block within that group. Nothing happens if `block` is not a group. Required scope: "editor/select" - `block`: the group id from a previous call to `group`. ```kotlin fun exitGroup(block: DesignBlock) ``` Changes selection from a group's selected block to that group. Nothing happens if `block` is not a group. Required scope: "editor/select" - `block`: a block id. ## Full Code Here's the full code: ```kotlin // Create blocks and append to scene val member1 = engine.block.create(DesignBlockType.Graphic) val member2 = engine.block.create(DesignBlockType.Graphic) engine.block.appendChild(scene, child = member1) engine.block.appendChild(scene, child = member2) // Check whether the blocks may be grouped if (engine.block.isGroupable(listOf(member1, member2))) { val group = engine.block.group(listOf(member1, member2)) engine.block.setSelected(group, selected = true) engine.block.enterGroup(group) engine.block.setSelected(member1, selected = true) engine.block.exitGroup(member1) engine.block.ungroup(group) } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Layer Management" description: "Organize design elements using a layer stack for precise control over stacking and visibility." platform: android url: "https://img.ly/docs/cesdk/android/create-composition/layer-management-18f07a/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Compositions](https://img.ly/docs/cesdk/android/create-composition-db709c/) > [Layers](https://img.ly/docs/cesdk/android/create-composition/layer-management-18f07a/) --- ```kotlin reference-only engine.block.insertChild(parent = page, child = block, index = 0) val parent = engine.block.getParent(block) val childIds = engine.block.getChildren(block) engine.block.appendChild(parent = parent, child = block) ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to modify the hierarchy of blocks through the `block` API. ## Manipulate the hierarchy of blocks > **Note:** Only blocks that are direct or indirect children of a `page` block are > rendered. Scenes without any `page` child may not be properly displayed by the > CE.SDK editor. ```kotlin fun getParent(block: DesignBlock): DesignBlock? ``` Query a block's parent. - `block`: the block to query. - Returns the parent's handle or null if the block has no parent. ```kotlin fun getChildren(block: DesignBlock): List ``` Get all children of the given block. Children are sorted in their rendering order: Last child is rendered in front of other children. - `block`: the block to query. - Returns a list of block ids. ```kotlin fun insertChild( parent: DesignBlock, child: DesignBlock, index: Int, ) ``` Insert a new or existing child at a certain position in the parent's children. Required scope: "editor/add" - `parent`: the block to update. - `child`: the child to insert. Can be an existing child of `parent`. - `index`: the index to insert or move to. ```kotlin fun appendChild( parent: DesignBlock, child: DesignBlock, ) ``` Appends a new or existing child to a block's children. Required scope: "editor/add" - `parent`: the block to update. - `child`: the child to insert. Can be an existing child of `parent`. When adding a block to a new parent, it is automatically removed from its previous parent. ## Full Code Here's the full code: ```kotlin engine.block.insertChild(parent = page, child = block, index = 0) val parent = engine.block.getParent(block) val childIds = engine.block.getChildren(block) engine.block.appendChild(parent = parent, child = block) ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Overview" description: "Combine and arrange multiple elements to create complex, multi-page, or layered design compositions." platform: android url: "https://img.ly/docs/cesdk/android/create-composition/overview-5b19c5/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Compositions](https://img.ly/docs/cesdk/android/create-composition-db709c/) > [Overview](https://img.ly/docs/cesdk/android/create-composition/overview-5b19c5/) --- In CreativeEditor SDK (CE.SDK), a *composition* is an arrangement of multiple design elements—such as images, text, shapes, graphics, and effects—combined into a single, cohesive visual layout. Unlike working with isolated elements, compositions allow you to design complex, multi-element visuals that tell a richer story or support more advanced use cases. All composition processing is handled entirely on the client side, ensuring fast, secure, and efficient editing without requiring server infrastructure. You can use compositions to create a wide variety of projects, including social media posts, marketing materials, collages, and multi-page exports like PDFs. Whether you are building layouts manually through the UI or generating them dynamically with code, compositions give you the flexibility and control to design at scale. [Explore Demos](https://img.ly/showcases/cesdk?tags=android) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) ## Exporting Compositions CE.SDK compositions can be exported in several formats: --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create Templates" description: "Learn how to create, import, and manage reusable templates to streamline design creation in CE.SDK." platform: android url: "https://img.ly/docs/cesdk/android/create-templates-3aef79/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Use Templates](https://img.ly/docs/cesdk/android/create-templates-3aef79/) --- --- ## Related Pages - [Overview](https://img.ly/docs/cesdk/android/create-templates/overview-4ebe30/) - Learn how to create, import, and manage reusable templates to streamline design creation in CE.SDK. - [Create Templates From Scratch in Android (Kotlin)](https://img.ly/docs/cesdk/android/create-templates/from-scratch-663cda/) - Build and save reusable CE.SDK templates programmatically in Android using Kotlin. - [Dynamic Content](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content-53fad7/) - Use variables and placeholders to inject dynamic data into templates at design or runtime. - [Lock the Template](https://img.ly/docs/cesdk/android/create-templates/lock-131489/) - Restrict editing access to specific elements or properties in a template to enforce design rules. - [Overview](https://img.ly/docs/cesdk/android/use-templates/overview-ae74e1/) - Learn how to browse, apply, and dynamically populate templates in CE.SDK to streamline design workflows. - [Apply a Template](https://img.ly/docs/cesdk/android/use-templates/apply-template-35c73e/) - Learn how to apply template scenes via API in the CreativeEditor SDK. - [Generate From Templates](https://img.ly/docs/cesdk/android/use-templates/generate-334e15/) - Learn how to load and populate CE.SDK templates in Kotlin for Android applications. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Dynamic Content" description: "Use variables and placeholders to inject dynamic data into templates at design or runtime." platform: android url: "https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content-53fad7/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Use Templates](https://img.ly/docs/cesdk/android/create-templates-3aef79/) > [Insert Dynamic Content](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content-53fad7/) --- --- ## Related Pages - [Text Variables](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/text-variables-7ecb50/) - Define dynamic text elements that can be populated with custom values during design generation. - [Placeholders](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/placeholders-d9ba8a/) - Use placeholders to mark editable image, video, or text areas within a locked template layout. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Placeholders" description: "Use placeholders to mark editable image, video, or text areas within a locked template layout." platform: android url: "https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/placeholders-d9ba8a/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Use Templates](https://img.ly/docs/cesdk/android/create-templates-3aef79/) > [Insert Dynamic Content](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content-53fad7/) > [Placeholders](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/placeholders-d9ba8a/) --- ```kotlin reference-only // Check if block supports placeholder behavior if (engine.block.supportsPlaceholderBehavior(block)) { // Enable the placeholder behavior engine.block.setPlaceholderBehaviorEnabled(block, enabled = true) val placeholderBehaviorIsEnabled = engine.block.isPlaceholderBehaviorEnabled(block) // Enable the placeholder capabilities (interaction in Adopter mode) engine.block.setPlaceholderEnabled(block, enabled = true) val placeholderIsEnabled = engine.block.isPlaceholderEnabled(block) // Check if block supports placeholder controls if (engine.block.supportsPlaceholderControls(block)) { // Enable the visibility of the placeholder overlay pattern engine.block.setPlaceholderControlsOverlayEnabled(block, enabled = true) val overlayEnabled = engine.block.isPlaceholderControlsOverlayEnabled(block) // Enable the visibility of the placeholder button engine.block.setPlaceholderControlsButtonEnabled(block, enabled = true) val buttonEnabled = engine.block.isPlaceholderControlsButtonEnabled(block) } } ``` In this example, we will demonstrate how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to manage placeholder behavior and controls through the block Api. ## Placeholder Behavior and Controls ```kotlin fun supportsPlaceholderBehavior(block: DesignBlock): Boolean ``` Query whether the block supports placeholder behavior. - `block`: the block to query. - Returns whether the block supports placeholder behavior. ```kotlin fun setPlaceholderBehaviorEnabled( block: DesignBlock, enabled: Boolean, ) ``` Enable or disable the placeholder behavior for a block. - `block`: the block whose placeholder behavior should be enabled or disabled. - `enabled`: Whether the placeholder behavior should be enabled or disabled. ```kotlin fun isPlaceholderBehaviorEnabled(block: DesignBlock): Boolean ``` Query whether the placeholder behavior for a block is enabled. - `block`: the block whose placeholder behavior state should be queried. - Returns the enabled state of the block's placeholder behavior. ```kotlin fun setPlaceholderEnabled( block: DesignBlock, enabled: Boolean, ) ``` Enable or disable the placeholder function for a block. - `block`: the block whose placeholder function should be enabled or disabled. - `enabled`: whether the function should be enabled or disabled. ```kotlin fun isPlaceholderEnabled(block: DesignBlock): Boolean ``` Query whether the placeholder function for a block is enabled. - `block`: the block whose placeholder function state should be queried. - Returns the enabled state of the placeholder function. ```kotlin fun supportsPlaceholderControls(block: DesignBlock): Boolean ``` Checks whether the block supports placeholder controls. - `block`: The block to query. - Returns whether the block supports placeholder controls. ```kotlin fun setPlaceholderControlsOverlayEnabled( block: DesignBlock, enabled: Boolean, ) ``` Enable or disable the visibility of the placeholder overlay pattern for a block. - `block`: The block whose placeholder overlay should be enabled or disabled. - `enabled`: Whether the placeholder overlay should be shown or not. ```kotlin fun isPlaceholderControlsOverlayEnabled(block: DesignBlock): Boolean ``` Query whether the placeholder overlay pattern for a block is shown. - `block`: The block whose placeholder overlay visibility state should be queried. - Returns the visibility state of the block's placeholder overlay pattern. ```kotlin fun setPlaceholderControlsButtonEnabled( block: DesignBlock, enabled: Boolean, ) ``` Enable or disable the visibility of the placeholder button for a block. - `block`: The block whose placeholder button should be shown or not. - `enabled`: Whether the placeholder button should be shown or not. ```kotlin fun isPlaceholderControlsButtonEnabled(block: DesignBlock): Boolean ``` Query whether the placeholder button for a block is shown. - `block`: The block whose placeholder button visibility state should be queried. - Returns the visibility state of the block's placeholder button. ## Full Code Here's the full code: ```kotlin // Check if block supports placeholder behavior if (engine.block.supportsPlaceholderBehavior(block)) { // Enable the placeholder behavior engine.block.setPlaceholderBehaviorEnabled(block, enabled = true) val placeholderBehaviorIsEnabled = engine.block.isPlaceholderBehaviorEnabled(block) // Enable the placeholder capabilities (interaction in Adopter mode) engine.block.setPlaceholderEnabled(block, enabled = true) val placeholderIsEnabled = engine.block.isPlaceholderEnabled(block) // Check if block supports placeholder controls if (engine.block.supportsPlaceholderControls(block)) { // Enable the visibility of the placeholder overlay pattern engine.block.setPlaceholderControlsOverlayEnabled(block, enabled = true) val overlayEnabled = engine.block.isPlaceholderControlsOverlayEnabled(block) // Enable the visibility of the placeholder button engine.block.setPlaceholderControlsButtonEnabled(block, enabled = true) val buttonEnabled = engine.block.isPlaceholderControlsButtonEnabled(block) } } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Text Variables" description: "Define dynamic text elements that can be populated with custom values during design generation." platform: android url: "https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/text-variables-7ecb50/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Use Templates](https://img.ly/docs/cesdk/android/create-templates-3aef79/) > [Insert Dynamic Content](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content-53fad7/) > [Text Variables](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/text-variables-7ecb50/) --- ```kotlin reference-only // Query all variables val variableNames = engine.variable.findAll() // Set, get and remove a variable engine.variable.set(key = "name", value = "Chris") val name = engine.variable.get(key = "name") // Chris engine.variable.remove(key = "name") val block = engine.block.create(DesignBlockType.Graphic) engine.block.referencesAnyVariables(block) ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to modify variables through the `variable` API. The `variable` API lets you set or get the contents of variables that exist in your scene. ## Functions ```kotlin fun findAll(): List ``` Get all text variables currently stored in the engine. - Returns a list of variable names. ```kotlin fun set( key: String, value: String, ) ``` Set a text variable. - `key`: the variable's key. - `value`: the text to replace the variable with. ```kotlin fun get(key: String): String ``` Get a text variable. - `key`: the variable's key. - Returns the text value of the variable. ```kotlin fun remove(key: String) ``` Destroy a text variable. - `key`: the variable's key. ```kotlin fun referencesAnyVariables(block: DesignBlock): Boolean ``` Checks whether the given block references any variables. Doesn't check the block's children. - `block`: the block to query. - Returns true if the block references variables, false otherwise. ## Localizing Variable Keys (CE.SDK only) You can show localized labels for the registered variables to your users by adding a corresponding label property to the object stored at `i18n..variables..label` in the configuration. Otherwise, the name used in `variable.setString()` will be shown. ![](./assets/variables-dark.png) ## Full Code Here's the full code: ```kotlin // Query all variables val variableNames = engine.variable.findAll() // Set, get and remove a variable engine.variable.set(key = "name", value = "Chris") val name = engine.variable.get(key = "name") // Chris engine.variable.remove(key = "name") val block = engine.block.create(DesignBlockType.Graphic) engine.block.referencesAnyVariables(block) ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create Templates From Scratch in Android (Kotlin)" description: "Build and save reusable CE.SDK templates programmatically in Android using Kotlin." platform: android url: "https://img.ly/docs/cesdk/android/create-templates/from-scratch-663cda/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Use Templates](https://img.ly/docs/cesdk/android/create-templates-3aef79/) > [Create From Scratch](https://img.ly/docs/cesdk/android/create-templates/from-scratch-663cda/) --- Templates define a reusable design pattern—text regions, image placeholders, and locked brand elements that your app can populate at runtime. This guide walks you through creating a template **from scratch** in Android using Kotlin, enabling variable bindings, and saving the result as a string or archive for reuse. ## What You'll Learn - Differences between **templates** and **scenes**. - Programmatically build a template scene. - Enable **variable** bindings for dynamic text. - Save templates to **string** or **archive**. - Store basic **metadata** for library use. ## When to Use It Choose this guide when you need to **author** templates programmatically for things such as: - Automation pipelines - Unit tests - Code‑generated layouts. Prefer the web-based CE.SDK editors if your goal is to let designers craft rich templates visually including: - Marking placeholders. - Locking styles. - Setting edit permissions. ## Templates vs Scenes - **Scene**: a complete document (pages, blocks, assets). Edit and export it directly. - **Template**: a reusable pattern applied to scenes; often includes placeholders and variables to control what's editable versus locked. ## Create Templates Programmatically The web-based CE.SDK editors include built-in template logic and UI. You can use them to: - Mark blocks as placeholders - Bind variables - Assign granular edit permissions. For most teams, this is the recommended path to author templates. This guide shows how to achieve similar results **in Android/Kotlin**, which is useful for code‑driven generation, CI pipelines, or dynamic authoring. In the code below: - You'll create a scene. - Add a page. - Insert a text block bound to a variable - Add an image block. ```kotlin import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) // Text block bound to a variable (e.g., {{name}}) val text = engine.block.create(DesignBlockType.Text) engine.block.setString(text, property = "text/text", value = "{{name}}") engine.block.setPositionX(text, value = 0.1F) engine.block.setPositionY(text, value = 0.1F) engine.block.appendChild(parent = page, child = text) // Image block for dynamic content val image = engine.block.create(DesignBlockType.Graphic) val shape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(image, shape = shape) val imageFill = engine.block.createFill(FillType.Image) engine.block.setFill(image, fill = imageFill) engine.block.setWidth(image, value = 300F) engine.block.setHeight(image, value = 200F) engine.block.setPositionX(image, value = 0.1F) engine.block.setPositionY(image, value = 0.3F) engine.block.appendChild(parent = page, child = image) ``` ## Binding Variables - Use variables for [text substitution](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/text-variables-7ecb50/). - Use named blocks or image fills for media that users swap at runtime. Define a variable in text using curly brackets. The variable can be the entire string or part of a string, such as `"Hello, {{guest_name}}"`. To populate variables at runtime: ```kotlin import ly.img.engine.Engine // Populate the template with actual data engine.variable.set(key = "name", value = "John Smith") engine.variable.set(key = "guest_name", value = "Alice") ``` For image replacement in templates, use named blocks: ```kotlin import ly.img.engine.Engine // Give the image block a name for easy lookup engine.block.setString(imageBlock, property = "name", value = "profile-photo") // Later, find and replace the image fill val blocks = engine.block.findByType(DesignBlockType.Graphic) for (block in blocks) { val name = engine.block.getString(block, property = "name") if (name == "profile-photo") { val fill = engine.block.getFill(block) engine.block.setString(fill, property = "fill/image/imageFileURI", value = "https://example.com/photo.jpg") } } ``` ## Saving Templates Templates are scenes with some extra settings. Save templates: - Use the same logic as for scenes. - Save as a **string** for a lightweight file: the template needs to be able to resolve all asset URLs at runtime. - Save as an **archive** for a self-contained, portable file: bundles the assets into the file. ### Save as String ```kotlin import ly.img.engine.Engine val sceneAsString = engine.scene.saveToString(scene = scene) // Persist to your DB or send to a backend ``` ### Save as Archive ```kotlin import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Engine import java.io.File suspend fun saveTemplateAsArchive( engine: Engine, context: Context, scene: Int ): File { val blob = engine.scene.saveToArchive(scene = scene) // Save to file val file = File(context.filesDir, "template_${System.currentTimeMillis()}.cesdk") withContext(Dispatchers.IO) { file.outputStream().channel.use { channel -> channel.write(blob) } } return file } ``` Once you've created the string or data blob, use standard methods to persist it. ## Complete Example Here's a complete example that creates a template from scratch: ```kotlin import android.content.Context import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode import java.io.File fun createTemplateFromScratch( context: Context, license: String, userId: String ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.template") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) try { // Create scene and page val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1080F) engine.block.setHeight(page, value = 1920F) // Background (brand color that stays locked) val background = engine.block.create(DesignBlockType.Graphic) val bgShape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(background, shape = bgShape) val bgFill = engine.block.createFill(FillType.Color) val bgColor = Color.fromRGBA(r = 0.95F, g = 0.95F, b = 0.98F, a = 1.0F) engine.block.setColor(bgFill, property = "fill/color/value", color = bgColor) engine.block.setFill(background, fill = bgFill) engine.block.appendChild(parent = page, child = background) engine.block.fillParent(background) engine.block.sendToBack(background) // Title text with variable val title = engine.block.create(DesignBlockType.Text) engine.block.setString(title, property = "text/text", value = "{{product_name}}") engine.block.setTextFontSize(title, fontSize = 48F) engine.block.setWidthMode(title, mode = SizeMode.AUTO) engine.block.setHeightMode(title, mode = SizeMode.AUTO) engine.block.setPositionX(title, value = 100F) engine.block.setPositionY(title, value = 200F) engine.block.appendChild(parent = page, child = title) // Description text with variable val description = engine.block.create(DesignBlockType.Text) engine.block.setString(description, property = "text/text", value = "{{description}}") engine.block.setTextFontSize(description, fontSize = 24F) engine.block.setWidthMode(description, mode = SizeMode.AUTO) engine.block.setHeightMode(description, mode = SizeMode.AUTO) engine.block.setPositionX(description, value = 100F) engine.block.setPositionY(description, value = 300F) engine.block.appendChild(parent = page, child = description) // Product image placeholder (named for easy replacement) val productImage = engine.block.create(DesignBlockType.Graphic) val imageShape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(productImage, shape = imageShape) val imageFill = engine.block.createFill(FillType.Image) // Set a placeholder image URL engine.block.setString( imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg" ) engine.block.setFill(productImage, fill = imageFill) // Name it for easy lookup later engine.block.setString(productImage, property = "name", value = "product-image") engine.block.setWidth(productImage, value = 600F) engine.block.setHeight(productImage, value = 600F) engine.block.setPositionX(productImage, value = 240F) engine.block.setPositionY(productImage, value = 600F) engine.block.appendChild(parent = page, child = productImage) // Price text with variable val price = engine.block.create(DesignBlockType.Text) engine.block.setString(price, property = "text/text", value = "${{price}}") engine.block.setTextFontSize(price, fontSize = 36F) engine.block.setTextColor( price, color = Color.fromRGBA(r = 0.2F, g = 0.6F, b = 0.2F, a = 1.0F) ) engine.block.setWidthMode(price, mode = SizeMode.AUTO) engine.block.setHeightMode(price, mode = SizeMode.AUTO) engine.block.setPositionX(price, value = 100F) engine.block.setPositionY(price, value = 1350F) engine.block.appendChild(parent = page, child = price) // Save as string val templateString = engine.scene.saveToString(scene = scene) println("Template saved as string (${templateString.length} characters)") // Save to file (for demonstration) val stringFile = File(context.filesDir, "template_string.scene") withContext(Dispatchers.IO) { stringFile.writeText(templateString) } // Save as archive (includes all assets) val archiveBlob = engine.scene.saveToArchive(scene = scene) val archiveFile = File(context.filesDir, "template_archive.cesdk") withContext(Dispatchers.IO) { archiveFile.outputStream().channel.use { channel -> channel.write(archiveBlob) } } println("Template saved as archive: ${archiveFile.absolutePath}") // Example: Populate the template with actual data engine.variable.set(key = "product_name", value = "Premium Coffee Mug") engine.variable.set(key = "description", value = "Hand-crafted ceramic, dishwasher safe") engine.variable.set(key = "price", value = "24.99") println("Template created and saved successfully!") } finally { // Note: Don't stop the engine here if you want to keep using it // engine.stop() } } ``` ## Add Template Metadata Like other assets, you can: - Load templates into the [asset library](https://img.ly/docs/cesdk/android/import-media/asset-library-65d6c4/). - Store metadata in your CMS or local database. - Use the saved metadata later when you register the template as an `AssetDefinition` in an `AssetSource`. That way the UI can display names, thumbnails, and categories. Example metadata structure: ```kotlin data class TemplateMetadata( val id: String, val name: String, val description: String, val thumbnailUrl: String, val category: String, val tags: List, val variables: List, val createdAt: Long, val updatedAt: Long ) ``` ## Lock Template Properties Templates can restrict editing at runtime so that users don't edit any part of the design that should remain static. To protect integrity, you can lock properties such as: - Position - Size - Color - Fill The guide for [locking templates](https://img.ly/docs/cesdk/android/create-templates/lock-131489/) provides details on which properties are lockable and how to set up editor and adopter rules. Example of locking a block: ```kotlin import ly.img.engine.Engine // Lock the background so users can't move or resize it engine.block.setScopeEnabled(background, key = "layer/move", enabled = false) engine.block.setScopeEnabled(background, key = "layer/resize", enabled = false) engine.block.setScopeEnabled(background, key = "fill/change", enabled = false) ``` ## Load and Use Templates Once you've created and saved a template, load it back and populate with data: ### Load from String ```kotlin import android.net.Uri import ly.img.engine.Engine // Load template from string val scene = engine.scene.load(scene = templateString) // Populate with data engine.variable.set(key = "product_name", value = "Wireless Headphones") engine.variable.set(key = "description", value = "Premium sound quality") engine.variable.set(key = "price", value = "149.99") // Find and replace the product image val blocks = engine.block.findByType(DesignBlockType.Graphic) for (block in blocks) { val name = engine.block.getString(block, property = "name") if (name == "product-image") { val fill = engine.block.getFill(block) engine.block.setString( fill, property = "fill/image/imageFileURI", value = "https://example.com/headphones.jpg" ) } } ``` ### Load from Archive Archives need to be unzipped before loading. Here's how to load a template from an archive file: ```kotlin import android.content.Context import android.net.Uri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Engine import java.io.File import java.io.FileOutputStream import java.util.zip.ZipInputStream suspend fun loadTemplateFromArchive( engine: Engine, context: Context, archiveFile: File ): Int { // Unzip archive to cache directory val extractDir = File(context.cacheDir, "template_${System.currentTimeMillis()}") extractDir.mkdirs() withContext(Dispatchers.IO) { ZipInputStream(archiveFile.inputStream()).use { zipInputStream -> var entry = zipInputStream.nextEntry while (entry != null) { val file = File(extractDir, entry.name) if (entry.isDirectory) { file.mkdirs() } else { file.parentFile?.mkdirs() FileOutputStream(file).use { outputStream -> zipInputStream.copyTo(outputStream) } } zipInputStream.closeEntry() entry = zipInputStream.nextEntry } } } // Load the scene.scene file from the extracted archive val sceneFile = File(extractDir, "scene.scene") val sceneUri = Uri.fromFile(sceneFile) return engine.scene.load(sceneUri = sceneUri) } ``` ## Troubleshooting **❌ Variables not populating**: - Confirm the variable syntax uses double curly brackets: `{{variable_name}}` - Verify that `engine.variable.set()` is called with the correct key. - Check that the scene is loaded before setting variables. **❌ Named blocks not found**: - Use `engine.block.getString(block, property = "name")` to verify block names. - Make sure the name was set during template creation. - Search within the correct block type using `engine.block.findByType()`. **❌ Missing fonts/images at runtime**: - Use an archive save to embed assets into a template for portability. - Ensure that the asset URIs are reachable and stable. - For local files, use `file:///android_asset/` for assets in the app's assets folder. **❌ Template won't load**: - Verify the template string or archive file is not corrupted. - Check that all required assets are accessible at the specified URIs. - Ensure the CE.SDK version used to create the template matches the version loading it. ## Next Steps Now that you can create templates, some related topics you may find helpful are: - [Generate scenes](https://img.ly/docs/cesdk/android/use-templates/generate-334e15/) with templates as the source. - [Apply templates](https://img.ly/docs/cesdk/android/use-templates/apply-template-35c73e/) to existing scenes. - [Batch Processing](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/) — automate template population at scale. - [Multi-Image Generation](https://img.ly/docs/cesdk/android/automation/multi-image-generation-2a0de4/) — generate multiple variants from a single template. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Lock the Template" description: "Restrict editing access to specific elements or properties in a template to enforce design rules." platform: android url: "https://img.ly/docs/cesdk/android/create-templates/lock-131489/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Use Templates](https://img.ly/docs/cesdk/android/create-templates-3aef79/) > [Lock the Template](https://img.ly/docs/cesdk/android/create-templates/lock-131489/) --- ```kotlin file=@cesdk_android_examples/engine-guides-scopes/Scopes.kt reference-only import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.GlobalScope fun scopes( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) engine.scene.createFromImage(Uri.parse("https://img.ly/static/ubq_samples/imgly_logo.jpg")) val block = engine.block.findByType(DesignBlockType.Graphic).first() val scopes = engine.editor.findAllScopes() // Let the global scope defer to the block-level. engine.editor.setGlobalScope(key = "layer/move", globalScope = GlobalScope.DEFER) // Manipulation of layout properties of any block will fail at this point. try { engine.block.setPositionX(block, value = 100F) // Not allowed } catch (exception: Exception) { exception.printStackTrace() } // This will return `GlobalScope.DEFER`. engine.editor.getGlobalScope(key = "layer/move") // Allow the user to control the layout properties of the image block. engine.block.setScopeEnabled(block, key = "layer/move", enabled = true) // Manipulation of layout properties of any block is now allowed. try { engine.block.setPositionX(block, value = 100F) // Allowed } catch (exception: Exception) { exception.printStackTrace() } // Verify that the "layer/move" scope is now enabled for the image block. engine.block.isScopeEnabled(block, key = "layer/move") // This will return true as well since the global scope is set to `GlobalScope.DEFER`. engine.block.isAllowedByScope(block, key = "layer/move") engine.stop() } ``` CE.SDK allows you to control which parts of a block can be manipulated. Scopes describe different aspects of a block, e.g. layout or style and can be enabled or disabled for every single block. There's also the option to control a scope globally. When configuring a scope globally you can set an override to always allow or deny a certain type of manipulation for every block. Or you can configure the global scope to defer to the individual block scopes. Initially, the block-level scopes are all disabled while at the global level all scopes are set to `"Allow"`. This overrides the block-level and allows for any kind of manipulation. If you want to implement a limited editing mode in your software you can set the desired scopes on the blocks you want the user to manipulate and then restrict the available actions by globally setting the scopes to `"Defer"`. In the same way you can prevent any manipulation of properties covered by a scope by setting the respective global scope to `"Deny"`. ## Available Scopes You can retrieve all available scopes by calling `engine.editor.findAllScopes()`. ```kotlin highlight-findAllScopes val scopes = engine.editor.findAllScopes() ``` We currently support the following scopes: | Scope | Explanation | | -------------------------- | -------------------------------------------------- | | `"layer/move"` | Whether the block's position can be changed | | `"layer/resize"` | Whether the block can be resized | | `"layer/rotate"` | Whether the block's rotation can be changed | | `"layer/flip"` | Whether the block can be flipped | | `"layer/crop"` | Whether the block's content can be cropped | | `"layer/clipping"` | Whether the block's clipping can be changed | | `"layer/opacity"` | Whether the block's opacity can be changed | | `"layer/blendMode"` | Whether the block's blend mode can be changed | | `"layer/visibility"` | Whether the block's visibility can be changed | | `"appearance/adjustments"` | Whether the block's adjustments can be changed | | `"appearance/filter"` | Whether the block's filter can be changed | | `"appearance/effect"` | Whether the block's effect can be changed | | `"appearance/blur"` | Whether the block's blur can be changed | | `"appearance/shadow"` | Whether the block's shadow can be changed | | `"lifecycle/destroy"` | Whether the block can be deleted | | `"lifecycle/duplicate"` | Whether the block can be duplicated | | `"editor/add"` | Whether new blocks can be added | | `"editor/select"` | Whether a block can be selected or not | | `"fill/change"` | Whether the block's fill can be changed | | `"fill/changeType"` | Whether the block's fill type can be changed | | `"stroke/change"` | Whether the block's stroke can be changed | | `"shape/change"` | Whether the block's shape can be changed | | `"text/edit"` | Whether the block's text can be changed | | `"text/character"` | Whether the block's text properties can be changed | ## Managing Scopes First, we globally defer the `"layer/move"` scope to the block-level using `engine.editor.setGlobalScope(key = "layer/move", globalScope = GlobalScope.DEFER)`. Since all blocks default to having their scopes set to `false` initially, modifying the layout properties of any block will fail at this point. | Value | Explanation | | -------- | ----------------------------------------------------------------- | | `.allow` | Manipulation of properties covered by the scope is always allowed | | `.deny` | Manipulation of properties covered by the scope is always denied | | `.defer` | Permission is deferred to the scope of the individual blocks | ```kotlin highlight-setGlobalScope // Let the global scope defer to the block-level. engine.editor.setGlobalScope(key = "layer/move", globalScope = GlobalScope.DEFER) // Manipulation of layout properties of any block will fail at this point. try { engine.block.setPositionX(block, value = 100F) // Not allowed } catch (exception: Exception) { exception.printStackTrace() } ``` We can verify the current state of the global `"layer/move"` scope using `engine.editor.getGlobalScope(key = "layer/move")`. ```kotlin highlight-getGlobalScope // This will return `GlobalScope.DEFER`. engine.editor.getGlobalScope(key = "layer/move") ``` Now we can allow the `"layer/move"` scope for a single block by setting it to `true` using `fun setScopeEnabled(block: DesignBlock, key: String, enabled: Boolean)`. ```kotlin highlight-setScopeEnabled // Allow the user to control the layout properties of the image block. engine.block.setScopeEnabled(block, key = "layer/move", enabled = true) // Manipulation of layout properties of any block is now allowed. try { engine.block.setPositionX(block, value = 100F) // Allowed } catch (exception: Exception) { exception.printStackTrace() } ``` Again we can verify this change by calling `fun isScopeEnabled(block: DesignBlock, key: String): Boolean`. ```kotlin highlight-isScopeEnabled // Verify that the "layer/move" scope is now enabled for the image block. engine.block.isScopeEnabled(block, key = "layer/move") ``` Finally, `fun isAllowedByScope(block: DesignBlock, key: String): Boolean` will allow us to verify a block's final scope state by taking both the global state as well as block-level state into account. ```kotlin highlight-isAllowedByScope // This will return true as well since the global scope is set to `GlobalScope.DEFER`. engine.block.isAllowedByScope(block, key = "layer/move") ``` ## Full Code Here's the full code: ```kotlin import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.GlobalScope fun scopes( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) engine.scene.createFromImage(Uri.parse("https://img.ly/static/ubq_samples/imgly_logo.jpg")) val block = engine.block.findByType(DesignBlockType.Graphic).first() val scopes = engine.editor.findAllScopes() // Let the global scope defer to the block-level. engine.editor.setGlobalScope(key = "layer/move", globalScope = GlobalScope.DEFER) // Manipulation of layout properties of any block will fail at this point. try { engine.block.setPositionX(block, value = 100F) // Not allowed } catch (exception: Exception) { exception.printStackTrace() } // This will return `GlobalScope.DEFER`. engine.editor.getGlobalScope(key = "layer/move") // Allow the user to control the layout properties of the image block. engine.block.setScopeEnabled(block, key = "layer/move", enabled = true) // Manipulation of layout properties of any block is now allowed. try { engine.block.setPositionX(block, value = 100F) // Allowed } catch (exception: Exception) { exception.printStackTrace() } // Verify that the "layer/move" scope is now enabled for the image block. engine.block.isScopeEnabled(block, key = "layer/move") // This will return true as well since the global scope is set to `GlobalScope.DEFER`. engine.block.isAllowedByScope(block, key = "layer/move") engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Overview" description: "Learn how to create, import, and manage reusable templates to streamline design creation in CE.SDK." platform: android url: "https://img.ly/docs/cesdk/android/create-templates/overview-4ebe30/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Use Templates](https://img.ly/docs/cesdk/android/create-templates-3aef79/) > [Overview](https://img.ly/docs/cesdk/android/create-templates/overview-4ebe30/) --- In CE.SDK, a *template* is a reusable, structured design that defines editable areas and constraints for end users. Templates can be based on static visuals or video compositions and are used to guide content creation, enable mass personalization, and enforce design consistency. Unlike a regular editable design, a template introduces structure through placeholders and constraints, allowing you to define which elements users can change and how. Templates support both static output formats (like PNG, PDF) and videos (like MP4), and can be created or applied using either the CE.SDK UI or API. Templates are a core part of enabling design automation, personalization, and streamlined workflows in any app that includes creative functionality. [Explore Demos](https://img.ly/showcases/cesdk?tags=android) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) These imported designs can then be adapted into editable, structured templates inside CE.SDK. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create Videos" description: "Learn how to create and customize videos in CE.SDK using scenes, assets, and timeline-based editing." platform: android url: "https://img.ly/docs/cesdk/android/create-video-c41a08/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) --- --- ## Related Pages - [Create Videos Overview](https://img.ly/docs/cesdk/android/create-video/overview-b06512/) - Learn how to create and customize videos in CE.SDK using scenes, assets, and timeline-based editing. - [Timeline Editor in Android (Kotlin)](https://img.ly/docs/cesdk/android/create-video/timeline-editor-912252/) - Use the timeline editor to arrange and edit video clips, audio, and animations frame by frame using Kotlin. - [Control Audio and Video](https://img.ly/docs/cesdk/android/create-video/control-daba54/) - Learn how to configure and control audio and video through offset, trim, playback, and resource control. - [Transform](https://img.ly/docs/cesdk/android/edit-video/transform-369f28/) - Documentation for Transform - [Add Captions](https://img.ly/docs/cesdk/android/edit-video/add-captions-f67565/) - Documentation for adding captions to videos - [Annotation in Android (Kotlin)](https://img.ly/docs/cesdk/android/edit-video/annotation-e9cbad/) - Add timed text, shapes, and highlights to videos programmatically in Android using Kotlin. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Control Audio and Video" description: "Learn how to configure and control audio and video through offset, trim, playback, and resource control." platform: android url: "https://img.ly/docs/cesdk/android/create-video/control-daba54/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Control Audio and Video](https://img.ly/docs/cesdk/android/create-video/control-daba54/) --- In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to configure and control audio and video through the `block` API. ## Time Offset and Duration The time offset determines when a block becomes active during playback on the scene's timeline, and the duration decides how long this block is active. Page blocks are a special case in that they have an implicitly calculated time offset that is determined by their order and the total duration of their preceding pages. As with any audio/video-related property, not every block supports these properties. Use `supportsTimeOffset` and `supportsDuration` to check. ```kotlin fun supportsTimeOffset(block: DesignBlock): Boolean ``` Returns whether the block has a time offset property. - `block`: the block to query. - Returns true, if the block has a time offset property. ```kotlin fun setTimeOffset( block: DesignBlock, offset: Double, ) ``` Set the time offset of the given block relative to its parent. The time offset controls when the block is first active in the timeline. Note: The time offset is not supported by the page block. - `block`: the block whose time offset should be changed. - `offset`: the new time offset in seconds. ```kotlin fun getTimeOffset(block: DesignBlock): Double ``` Get the time offset of the given block relative to its parent. - `block`: the block whose time offset should be queried. - Returns the time offset of the block. ```kotlin fun supportsDuration(block: DesignBlock): Boolean ``` Returns whether the block has a duration property. - `block`: the block to query. - Returns true if the block has a duration property. ```kotlin fun setDuration( block: DesignBlock, duration: Double, ) ``` Set the playback duration of the given block in seconds. The duration defines for how long the block is active in the scene during playback. If a duration is set on the page block, it becomes the duration source block. Note: The duration is ignored when the scene is not in "Video" mode. Note: This also adjusts the trim for non looping blocks. - `block`: the block whose duration should be changed. - `duration`: the new duration in seconds. ```kotlin fun getDuration(block: DesignBlock): Double ``` Get the playback duration of the given block in seconds. The duration defines for how long the block is active in the scene during playback. Note: The duration is ignored when the scene is not in `Video` mode. - `block`: the block whose duration should be returned. - Returns the block's duration. ```kotlin fun supportsPageDurationSource( page: DesignBlock, block: DesignBlock, ): Boolean ``` Returns whether the block can be marked as the element that defines the duration of the given page. - `page`: the page block for which to query for. - `block`: the block to query. - Returns true, if the block can be marked as the element that defines the duration of the given page. ```kotlin fun setPageDurationSource( page: DesignBlock, block: DesignBlock, ) ``` Set an block as duration source so that the overall page duration is automatically determined by this. If no defining block is set, the page duration is calculated over all children. Only one block per page can be marked as duration source. Will automatically unmark the previously marked. Note: This is only supported for blocks that have a duration. - `page`: the page block for which it should be enabled. - `block`: the block which should be marked as duration source. ```kotlin fun isPageDurationSource(block: DesignBlock): Boolean ``` Returns whether the block is a duration source block. - `block`: the block whose duration source property should be queried. - Returns if the block is a duration source for a page. ```kotlin fun removePageDurationSource(block: DesignBlock) ``` Remove the block as duration source block for the page. If a scene or page given set as block, it is deactivated for all blocks in the scene or page. - `block`: the block whose duration source property should be removed. ## Trim You can select a specific range of footage from your audio/video resource by providing a trim offset and a trim length. The footage will loop if the trim's length is shorter than the block's duration. This behavior can also be disabled using the `setLooping` function. ```kotlin fun supportsTrim(block: DesignBlock): Boolean ``` Returns whether the block has trim properties. - `block`: the block to query. - Returns true, if the block has trim properties. ```kotlin fun setTrimOffset( block: DesignBlock, offset: Double, ) ``` Set the trim offset of the given block or fill. Sets the time in seconds within the fill at which playback of the audio or video clip should begin. Note: This requires the video or audio clip to be loaded. - `block`: the block whose trim should be updated. - `offset`: the new trim offset, measured in timeline seconds (scaled by playback rate). ```kotlin fun getTrimOffset(block: DesignBlock): Double ``` Get the trim offset of this block. Note: This requires the video or audio clip to be loaded. - `block`: the block whose trim offset should be queried. - Returns the trim offset in timeline seconds. ```kotlin fun setTrimLength( block: DesignBlock, length: Double, ) ``` Set the trim length of the given block or fill. The trim length is the duration of the audio or video clip that should be used for playback. Note: After reaching this value during playback, the trim region will loop. Note: This requires the video or audio clip to be loaded. - `block`: the object whose trim length should be updated. - `length`: the new trim length, measured in timeline seconds (scaled by playback rate). ```kotlin fun getTrimLength(block: DesignBlock): Double ``` Get the trim length of the given block or fill. - `block`: the object whose trim length should be queried. - Returns the trim length of the object measured in timeline seconds (scaled by playback rate). ## Playback Control You can start and pause playback and seek to a certain point on the scene's timeline. There's also a solo playback mode to preview audio and video blocks individually while the rest of the scene stays frozen. Finally, you can enable or disable the looping behavior of blocks and control their audio volume. ```kotlin fun setPlaying( block: DesignBlock, enabled: Boolean, ) ``` Set whether the block should be during active playback. - `block`: the block that should be updated. - `enabled`: whether the block should be playing its contents. ```kotlin fun isPlaying(block: DesignBlock): Boolean ``` Returns whether the block is currently during active playback. - `block`: the block to query. - Returns whether the block is during playback. ```kotlin fun setSoloPlaybackEnabled( block: DesignBlock, enabled: Boolean, ) ``` Set whether the given block or fill should play its contents while the rest of the scene remains paused. Note: Setting this to true for one block will automatically set it to false on all other blocks. - `block`: the block or fill to update. - `enabled`: whether the block's playback should progress as time moves on. ```kotlin fun isSoloPlaybackEnabled(block: DesignBlock): Boolean ``` Return whether the given block or fill is currently set to play its contents while the rest of the scene remains paused. - `block`: the block or fill to query. - Returns whether solo playback is enabled for this block. ```kotlin fun supportsPlaybackTime(block: DesignBlock): Boolean ``` Returns whether the block has a playback time property. - `block`: the block to query. - Returns whether the block has a playback time property. ```kotlin fun setPlaybackTime( block: DesignBlock, time: Double, ) ``` Set the playback time of the given block. - `block`: the block whose playback time should be updated. - `time`: the new playback time of the block in seconds. ```kotlin fun getPlaybackTime(block: DesignBlock): Double ``` Get the playback time of the given block. - `block`: the block to query. - Returns the playback time of the block in seconds. ```kotlin fun isVisibleAtCurrentPlaybackTime(block: DesignBlock): Boolean ``` Returns whether the block should be visible on the canvas at the current playback time. - `block`: the block to query. - Returns the visibility state if the query succeeded. ```kotlin fun supportsPlaybackControl(block: DesignBlock): Boolean ``` Returns whether the block supports a playback control. - `block`: the block to query. - Returns whether the block has playback control. ```kotlin fun setLooping( block: DesignBlock, looping: Boolean, ) ``` Set whether the block should start from the beginning again or stop. - `block`: the block or video fill to update. - Returns whether the block should loop to the beginning or stop. ```kotlin fun isLooping(block: DesignBlock): Boolean ``` Query whether the block is looping. - `block`: the block to query. - Returns whether the block is looping. ```kotlin fun setMuted( block: DesignBlock, muted: Boolean, ) ``` Set whether the audio of the block is muted. - `block`: the block or video fill to update. - `muted`: whether the audio should be muted. ```kotlin fun isMuted(block: DesignBlock): Boolean ``` Query whether the block is muted. - `block`: the block to query. - Returns whether the block is muted. ```kotlin fun setVolume( block: DesignBlock, @FloatRange(from = 0.0, to = 1.0) volume: Float, ) ``` Set the audio volume of the given block. - `block`: the block or video fill to update. - `volume`: the desired volume with a range of `0, 1`. ```kotlin @FloatRange(from = 0.0, to = 1.0) fun getVolume(block: DesignBlock): Float ``` Get the audio volume of the given block. - `block`: the block to query. - Returns volume with a range of `0, 1`. ## Playback Speed You can control the playback speed of audio and video blocks to create slow-motion or fast-forward effects. The playback speed is a multiplier that affects how quickly the content plays back. Audio blocks accept values from 0.25x (quarter speed) to 3.0x (triple speed). Video fills can exceed 3.0x whenever you need more aggressive fast-forward playback. Note that changing the playback speed automatically adjusts both the trim and duration of the block to maintain the same visual timeline length. ```kotlin fun setPlaybackSpeed(block: DesignBlock, speed: Float): Unit ``` Set the playback speed of the given block. Note: This also adjusts the trim and duration of the block. Video fills running faster than 3.0x are force muted until their speed is reduced to 3.0x or below. - `block`: the block or video fill to update. - `speed`: The desired playback speed multiplier. Valid range is \[0.25, 3.0] for audio blocks and \[0.25, ∞) for video fills. ```kotlin fun getPlaybackSpeed(block: DesignBlock): Float ``` Get the playback speed of the given block. - `block`: the block to query. - Returns the playback speed multiplier. ## Resource Control Until an audio/video resource referenced by a block is loaded, properties like the duration of the resource aren't available, and accessing those will lead to an error. You can avoid this by forcing the resource you want to access to load using `forceLoadAVResource`. ```kotlin suspend fun forceLoadAVResource(block: DesignBlock) ``` Begins loading the required audio and video resource for the given video fill or audio block. If the resource had been loaded earlier and resulted in an error, it will be reloaded. - `block`: the video fill or audio block whose resource should be loaded. ```kotlin @UnstableEngineApi fun isAVResourceLoaded(block: DesignBlock): Boolean ``` Returns whether the audio and video resource for the given video fill or audio block is loaded. Note that the function is unstable and mared with `UnstableEngineApi`. - `block`: the video fill or audio block. - Returns whether the resource is loaded. ```kotlin fun getAVResourceTotalDuration(block: DesignBlock): Double ``` Get the duration in seconds of the video or audio resource that is attached to the given block. - `block`: the video fill or audio block. - Returns the video or audio file duration. ```kotlin fun getVideoWidth(videoFill: DesignBlock): Int ``` Get the video width in pixels of the video resource that is attached to the given block. - `videoFill`: the video fill. - Returns the video width in pixels. ```kotlin fun getVideoHeight(videoFill: DesignBlock): Int ``` Get the video height in pixels of the video resource that is attached to the given block. - `videoFill`: the video fill. - Returns the video height in pixels. ## Thumbnail Previews For a user interface, it can be helpful to have image previews in the form of thumbnails for any given video resource. For videos, the engine can provide one or more frames using `generateVideoThumbnailSequence`. Pass the video fill that references the video resource. In addition to video thumbnails, the engine can also render compositions of design blocks over time. To do this pass in the respective design block. The video editor uses these to visually represent blocks in the timeline. In order to visualize audio signals `generateAudioThumbnailSequence` can be used. This generates a sequence of values in the range of 0 to 1 that represent the loudness of the signal. These values can be used to render a waveform pattern in any custom style. Note: there can be at most one thumbnail generation request per block at any given time. If you don't want to wait for the request to finish before issuing a new request, you can cancel it by calling `cancel()` on the `Job` object returned on launching the flow. ```kotlin fun generateVideoThumbnailSequence( block: DesignBlock, thumbnailHeight: Int, timeBegin: Double, timeEnd: Double, numberOfFrames: Int, ): Flow ``` Generate a thumbnail sequence for the given video fill or design block. Note: There can only be one thumbnail generation request in progress for a given block. Note: During playback, the thumbnail generation will be paused. - `block`: the video fill or a design block. - `thumbnailHeight`: the height of a thumbnail. The width will be calculated from the video aspect ratio. - `timeBegin`: the time in seconds relative to the time offset of the design block at which the thumbnail sequence should start. - `timeEnd`: the time in seconds relative to the time offset of the design block at which the thumbnail sequence should end. - `numberOfFrames`: the number of frames to generate. - Returns a flow of `VideoThumbnailResult` object which emits for every generated frame thumbnail. It emits exactly `numberOfFrames` times. ```kotlin fun generateAudioThumbnailSequence( block: DesignBlock, samplesPerChunk: Int, timeBegin: Double, timeEnd: Double, numberOfSamples: Int, numberOfChannels: Int, ): Flow ``` Generate a thumbnail sequence for the given audio block or video fill. A thumbnail in this case is a chunk of samples in the range of 0 to 1. In case stereo data is requested, the samples are interleaved, starting with the left channel. Note: During playback, the thumbnail generation will be paused. - `block`: the audio block or video fill. - `samplesPerChunk`: the number of samples per chunk. - `timeBegin`: the time in seconds at which the thumbnail sequence should start. - `timeEnd`: the time in seconds at which the thumbnail sequence should end. - `numberOfSamples`: the total number of samples to generate. - `numberOfChannels`: the number of channels in the output. 1 for mono, 2 for stereo. - Returns a flow of `AudioThumbnailResult` object which emits numberOfSamples / samplesPerChunk times. ## Full Code Here's the full code: ```kotlin file=@cesdk_android_examples/engine-guides-control-av/ControlAudioVideo.kt import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType suspend fun controlAudioVideo(engine: Engine) = coroutineScope { // Setup a minimal video scene val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1280.0f) engine.block.setHeight(page, value = 720.0f) // Create a video block and track val videoBlock = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(videoBlock, shape = engine.block.createShape(ShapeType.Rect)) val videoFill = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4", ) engine.block.setFill(videoBlock, fill = videoFill) val track = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = track) engine.block.appendChild(parent = track, child = videoBlock) engine.block.fillParent(track) // Create an audio block val audio = engine.block.create(DesignBlockType.Audio) engine.block.appendChild(parent = page, child = audio) engine.block.setString( block = audio, property = "audio/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.audio/audios/far_from_home.m4a", ) // Time Offset and Duration engine.block.supportsTimeOffset(audio) engine.block.setTimeOffset(audio, offset = 2.0) engine.block.getTimeOffset(audio) // Returns 2 engine.block.supportsDuration(page) engine.block.setDuration(page, duration = 10.0) engine.block.getDuration(page) // Returns 10 // Duration of the page can be that of a block engine.block.supportsPageDurationSource(page, videoBlock) engine.block.setPageDurationSource(page, videoBlock) engine.block.isPageDurationSource(videoBlock) engine.block.getDuration(page) // Returns duration plus offset of the block // Duration of the page can be the maximum end time of all page child blocks engine.block.removePageDurationSource(page) engine.block.getDuration(page) // Returns the maximum end time of all page child blocks // Trim engine.block.supportsTrim(videoFill) engine.block.setTrimOffset(videoFill, offset = 1.0) engine.block.getTrimOffset(videoFill) // Returns 1 engine.block.setTrimLength(videoFill, length = 5.0) engine.block.getTrimLength(videoFill) // Returns 5 // Playback Control engine.block.setPlaying(page, enabled = true) engine.block.isPlaying(page) engine.block.setSoloPlaybackEnabled(videoFill, enabled = true) engine.block.isSoloPlaybackEnabled(videoFill) engine.block.supportsPlaybackTime(page) engine.block.setPlaybackTime(page, time = 1.0) engine.block.getPlaybackTime(page) engine.block.isVisibleAtCurrentPlaybackTime(videoBlock) engine.block.supportsPlaybackControl(videoFill) engine.block.setLooping(videoFill, looping = true) engine.block.isLooping(videoFill) engine.block.setMuted(videoFill, muted = true) engine.block.isMuted(videoFill) engine.block.setVolume(videoFill, volume = 0.5F) // 50% volume engine.block.getVolume(videoFill) // Playback Speed engine.block.setPlaybackSpeed(videoFill, speed = 0.5f) // Half speed val currentSpeed = engine.block.getPlaybackSpeed(videoFill) // 0.5 engine.block.setPlaybackSpeed(videoFill, speed = 2.0f) // Double speed engine.block.setPlaybackSpeed(videoFill, speed = 1.0f) // Normal speed // Resource Control engine.block.forceLoadAVResource(videoFill) // Unstable engine api engine.block.isAVResourceLoaded(videoFill) engine.block.getAVResourceTotalDuration(videoFill) val videoWidth = engine.block.getVideoWidth(videoFill) val videoHeight = engine.block.getVideoHeight(videoFill) // Thumbnail Previews launch { engine.block.generateVideoThumbnailSequence( block = videoFill, thumbnailHeight = 128, timeBegin = 0.5, timeEnd = 9.5, numberOfFrames = 10, ).onEach { println("frameIndex = ${it.frameIndex}, width = ${it.width}, height = ${it.height}") }.collect() } launch { engine.block.generateAudioThumbnailSequence( block = audio, samplesPerChunk = 20, timeBegin = 0.5, timeEnd = 9.5, numberOfSamples = 10 * 20, numberOfChannels = 2, ).onEach { println("chunkIndex = ${it.chunkIndex}, samples:size = ${it.samples.size}") // drawWavePattern(it.samples) }.collect() } } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create Videos Overview" description: "Learn how to create and customize videos in CE.SDK using scenes, assets, and timeline-based editing." platform: android url: "https://img.ly/docs/cesdk/android/create-video/overview-b06512/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Overview](https://img.ly/docs/cesdk/android/create-video/overview-b06512/) --- ```kotlin file=@cesdk_android_examples/engine-guides-video/Video.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.MimeType import ly.img.engine.ShapeType fun editVideo( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1280, height = 720) val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1280F) engine.block.setHeight(page, value = 720F) engine.block.setDuration(page, duration = 20.0) val video1 = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(video1, shape = engine.block.createShape(ShapeType.Rect)) val videoFill = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4", ) engine.block.setFill(video1, fill = videoFill) val video2 = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(video1, shape = engine.block.createShape(ShapeType.Rect)) val videoFill2 = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v3/ly.img.video/videos/pexels-kampus-production-8154913.mp4", ) engine.block.setFill(video2, fill = videoFill2) val track = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = track) engine.block.appendChild(parent = track, child = video1) engine.block.appendChild(parent = track, child = video2) engine.block.fillParent(track) engine.block.setDuration(video1, duration = 15.0) // Make sure that the video is loaded before calling the trim APIs. engine.block.forceLoadAVResource(videoFill) engine.block.setTrimOffset(videoFill, offset = 1.0) engine.block.setTrimLength(videoFill, length = 10.0) engine.block.setLooping(videoFill, looping = true) engine.block.setMuted(videoFill, muted = true) val audio = engine.block.create(DesignBlockType.Audio) engine.block.appendChild(parent = page, child = audio) engine.block.setString( block = audio, property = "audio/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.audio/audios/far_from_home.m4a", ) // Set the volume level to 70%. engine.block.setVolume(audio, volume = 0.7F) // Start the audio after two seconds of playback. engine.block.setTimeOffset(audio, offset = 2.0) // Give the Audio block a duration of 7 seconds. engine.block.setDuration(audio, duration = 7.0) // Export page as mp4 video. val blob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { println( "Rendered ${it.renderedFrames} frames and encoded ${it.encodedFrames} frames out of ${it.totalFrames} frames", ) }, ) engine.stop() } ``` In addition to static designs, CE.SDK also allows you to create and edit videos. Working with videos introduces the concept of time into the scene, which requires you to switch the scene into the `"Video"` mode. In this mode, each page in the scene has its own separate timeline within which its children can be placed. The `"playback/time"` property of each page controls the progress of time through the page. In order to add videos to your pages, you can add a block with a `FillType.video` fill. As the playback time of the page progresses, the corresponding point in time of the video fill is rendered by the block. You can also customize the video fill's trim in order to control the portion of the video that should be looped while the block is visible. `DesignBlockType.Audio` blocks can be added to the scene in order to play an audio file during playback. The `playback/timeOffset` property controls after how many seconds the audio should begin to play, while the duration property defines how long the audio should play. The same APIs can be used for other design blocks as well, such as text or graphic blocks. Finally, the whole page can be exported as a video file using the `BlockApi.exportVideo` function. ## Creating a Video Scene First, we create a scene that is set up for video editing by calling the `scene.createForVideo()` API. Then we create a page, add it to the scene and define its dimensions. This page will hold our composition. ```kotlin highlight-setupScene val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1280F) engine.block.setHeight(page, value = 720F) ``` ## Setting Page Durations Next, we define the duration of the page using the `fun setDuration(block: DesignBlock, duration: Double)` API to be 20 seconds long. This will be the total duration of our exported video in the end. ```kotlin highlight-setPageDuration engine.block.setDuration(page, duration = 20.0) ``` ## Adding Videos In this example, we want to show two videos, one after the other. For this, we first create two graphic blocks and assign two `'video'` fills to them. ```kotlin highlight-assignVideoFill val video1 = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(video1, shape = engine.block.createShape(ShapeType.Rect)) val videoFill = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4", ) engine.block.setFill(video1, fill = videoFill) val video2 = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(video1, shape = engine.block.createShape(ShapeType.Rect)) val videoFill2 = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v3/ly.img.video/videos/pexels-kampus-production-8154913.mp4", ) engine.block.setFill(video2, fill = videoFill2) ``` ## Creating a Track While we could add the two blocks directly to the page and manually set their sizes and time offsets, we can alternatively also use the `DesignBlockType.Track` block to simplify this work. A track automatically adjusts the time offsets of its children to make sure that they play one after another without any gaps, based on each child's duration. Tracks themselves cannot be selected directly by clicking on the canvas, nor do they have any visual representation. We create a `DesignBlockType.Track` block, add it to the page and add both videos in the order in which they should play as the track's children. Next, we use the `fillParent` API, which will resize all children of the track to the same dimensions as the page. The dimensions of a track are always derived from the dimensions of its children, so you should not call the `setWidth` or `setHeight` APIs on a track, but on its children instead if you can't use the `fillParent` API. ```kotlin highlight-addToTrack val track = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = track) engine.block.appendChild(parent = track, child = video1) engine.block.appendChild(parent = track, child = video2) engine.block.fillParent(track) ``` By default, each block has a duration of 5 seconds after it is created. If we want to show it on the page for a different amount of time, we can use the `setDuration` API. Note that we can just increase the duration of the first video block to 15 seconds without having to adjust anything about the second video. The `track` takes care of that for us automatically so that the second video starts playing after 15 seconds. ```kotlin highlight-setDuration engine.block.setDuration(video1, duration = 15.0) ``` If the video is longer than the duration of the graphic block that it's attached to, it will cut off once the duration of the graphic is reached. If it is too short, the video will automatically loop for as long as its graphic block is visible. We can also manually define the portion of our video that should loop within the page using the `fun setTrimOffset(block: DesignBlock, offset: Double)` and `fun setTrimLength(block: DesignBlock, length: Double)` APIs. We use the trim offset to cut away the first second of the video and the trim length to only play 10 seconds of the video. ```kotlin highlight-trim // Make sure that the video is loaded before calling the trim APIs. engine.block.forceLoadAVResource(videoFill) engine.block.setTrimOffset(videoFill, offset = 1.0) engine.block.setTrimLength(videoFill, length = 10.0) ``` We can control if a video will loop back to its beginning by calling `fun setLooping(block: DesignBlock, looping: Boolean)`. Otherwise, the video will simply hold its last frame instead and audio will stop playing. Looping behavior is activated for all blocks by default. ```kotlin highlight-looping engine.block.setLooping(videoFill, looping = true) ``` ## Audio If the video of a video fill contains an audio track, that audio will play automatically by default when the video is playing. We can mute it by calling `fun setMuted(block: DesignBlock, muted: Boolean)`. ```kotlin highlight-mute-audio engine.block.setMuted(videoFill, muted = true) ``` We can also add audio-only files to play together with the contents of the scene by adding an `audio` block to the scene and assigning it the uri of the audio file. ```kotlin highlight-audio val audio = engine.block.create(DesignBlockType.Audio) engine.block.appendChild(parent = page, child = audio) engine.block.setString( block = audio, property = "audio/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.audio/audios/far_from_home.m4a", ) ``` We can adjust the volume level of any audio block or video fill by calling `fun setVolume(block: DesignBlock, volume: Float)`. The volume is given as a fraction in the range of 0 to 1. ```kotlin highlight-audio-volume // Set the volume level to 70%. engine.block.setVolume(audio, volume = 0.7F) ``` By default, our audio block will start playing at the very beginning of the page. We can change this by specifying how many seconds into the page it should begin to play using the `fun setTimeOffset(block: DesignBlock, offset: Double)` API. ```kotlin highlight-timeOffset // Start the audio after two seconds of playback. engine.block.setTimeOffset(audio, offset = 2.0) ``` By default, our audio block will have a duration of 5 seconds. We can change this by specifying its duration in seconds by using the `fun setDuration(block: DesignBlock, duration: Double)` API. ```kotlin highlight-audioDuration // Give the Audio block a duration of 7 seconds. engine.block.setDuration(audio, duration = 7.0) ``` ## Exporting Video You can start exporting the entire page as a video file by calling `blockApi.exportVideo`. The encoding process will run in the background. You can get notified about the progress of the encoding process by using `progressCallback` parameter. Since the encoding process runs in the background, the engine will stay interactive. So, you can continue to use the engine to manipulate the scene. Please note that these changes won't be visible in the exported video file because the scene's state has been frozen at the start of the export. ```kotlin highlight-exportVideo // Export page as mp4 video. val blob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { println( "Rendered ${it.renderedFrames} frames and encoded ${it.encodedFrames} frames out of ${it.totalFrames} frames", ) }, ) ``` ## Full Code Here's the full code: ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.MimeType import ly.img.engine.ShapeType fun editVideo( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1280F) engine.block.setHeight(page, value = 720F) engine.block.setDuration(page, duration = 20.0) val video1 = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(video1, shape = engine.block.createShape(ShapeType.Rect)) val videoFill = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4", ) engine.block.setFill(video1, fill = videoFill) val video2 = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(video1, shape = engine.block.createShape(ShapeType.Rect)) val videoFill2 = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v3/ly.img.video/videos/pexels-kampus-production-8154913.mp4", ) engine.block.setFill(video2, fill = videoFill2) val track = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = track) engine.block.appendChild(parent = track, child = video1) engine.block.appendChild(parent = track, child = video2) engine.block.fillParent(track) engine.block.setDuration(video1, duration = 15.0) // Make sure that the video is loaded before calling the trim APIs. engine.block.forceLoadAVResource(videoFill) engine.block.setTrimOffset(videoFill, offset = 1.0) engine.block.setTrimLength(videoFill, length = 10.0) engine.block.setLooping(videoFill, looping = true) engine.block.setMuted(videoFill, muted = true) val audio = engine.block.create(DesignBlockType.Audio) engine.block.appendChild(parent = page, child = audio) engine.block.setString( block = audio, property = "audio/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.audio/audios/far_from_home.m4a", ) // Set the volume level to 70%. engine.block.setVolume(audio, volume = 0.7F) // Start the audio after two seconds of playback. engine.block.setTimeOffset(audio, offset = 2.0) // Give the Audio block a duration of 7 seconds. engine.block.setDuration(audio, duration = 7.0) // Export page as mp4 video. val blob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { println( "Rendered ${it.renderedFrames} frames and encoded ${it.encodedFrames} frames out of ${it.totalFrames} frames", ) }, ) engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Timeline Editor in Android (Kotlin)" description: "Use the timeline editor to arrange and edit video clips, audio, and animations frame by frame using Kotlin." platform: android url: "https://img.ly/docs/cesdk/android/create-video/timeline-editor-912252/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Timeline Editor](https://img.ly/docs/cesdk/android/create-video/timeline-editor-912252/) --- Timeline editing is the heart of any professional video creation tool. With CE.SDK, you can build video editors that use a **timeline model**. Each scene contains tracks and clips that align precisely over time. Developers can build **custom headless timelines** using the `Engine` APIs to programmatically arrange, trim, and export video content. ## What You'll Learn - How the CE.SDK timeline hierarchy works (`Scene → Page → Track → Clip`). - How to create and organize video tracks programmatically. - How to trim and arrange video clips in a timeline. - How to generate thumbnails for a timeline view. - How to connect timeline scenes to export or playback features. ## When You'll Use It - You want to build a **custom video editing interface** that arranges clips. - You need to **trim or rearrange** clips programmatically before export. - You're adding **thumbnail visualization** or building a playback scrubber. - You're automating video generation from templates or data. ## Understanding the Timeline Hierarchy CE.SDK organizes time-based video projects into a structured hierarchy: ```text Scene └── Page (timeline segment) ├── Track (parallel layer) │ ├── Clip (video or audio content) │ ├── Clip … ``` - **Scene:** the root container of your video project. - **Page:** a timeline segment (often a full video composition). - **Track:** a parallel layer for clips (like separate video or audio lanes). - **Clip:** an individual media item placed on a track. > **Note:** By default, a new scene has dimensions of **100 × 100 units**, which is small. For realistic video compositions, explicitly set a size that matches your export or camera input:```kotlin > import ly.img.engine.DesignBlockType > import ly.img.engine.Engine > > val scene = engine.scene.createForVideo() > val page = engine.block.create(DesignBlockType.Page) > > engine.block.appendChild(parent = scene, child = page) > > // Set page dimensions to match a 1080x1920 portrait video > engine.block.setWidth(page, value = 1080F) > engine.block.setHeight(page, value = 1920F) > ``` ## Creating a Timeline Programmatically When you're building a custom UI, create a timeline structure directly through the block API. ```kotlin import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) // Always set a realistic frame size engine.block.setWidth(page, value = 1080F) engine.block.setHeight(page, value = 1920F) // Create a video track val track = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = track) // Insert a video clip val clip = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(clip, shape = engine.block.createShape(ShapeType.Rect)) val fill = engine.block.createFill(FillType.Video) engine.block.setString( block = fill, property = "fill/video/fileURI", value = "https://example.com/video.mp4" ) engine.block.setFill(clip, fill = fill) engine.block.appendChild(parent = track, child = clip) ``` You can repeat this process for all clips and tracks, allowing for multi-layered compositions that include: - background video - overlays - captions When you append a clip to a track, CE.SDK automatically places the new clip **directly after the last clip in that track**. This gives you a continuous, gap-free sequence, so playback flows cleanly from one clip to the next without extra timing math. If you need gaps or overlaps, either: - Place the clips in separate tracks. - Disable automatic offset management for the track and fully control offsets yourself. ```kotlin import ly.img.engine.Engine // Disable automatic offset management for this track engine.block.setBool( videoTrack, property = "track/automaticallyManageBlockOffsets", value = false ) // Manage playback/timeOffset on each clip manually engine.block.setTimeOffset(aRoll, offset = 0.0) engine.block.setTimeOffset(overlayClip, offset = 3.0) ``` ### Multi-Track Example (Video + Overlay + Audio) You can build layered timelines by adding tracks to the same page. Each track maintains its own sequence of clips. The following code creates two video tracks to create a picture-in-picture display with an audio track accompaniment. ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun createMultiTrackTimeline( license: String, userId: String ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.timeline") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) // Create a video scene and page val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) // Set page dimensions and duration engine.block.setWidth(page, value = 1080F) engine.block.setHeight(page, value = 1920F) // Focus the canvas on this page engine.scene.zoomToBlock( page, paddingLeft = 0F, paddingTop = 0F, paddingRight = 0F, paddingBottom = 0F ) // A-roll primary video track val videoTrack = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = videoTrack) val aRoll = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(aRoll, shape = engine.block.createShape(ShapeType.Rect)) val aRollFill = engine.block.createFill(FillType.Video) engine.block.setString( block = aRollFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4" ) engine.block.setFill(aRoll, fill = aRollFill) engine.block.appendChild(parent = videoTrack, child = aRoll) // Force load to get duration engine.block.forceLoadAVResource(aRollFill) val rollDuration = engine.block.getAVResourceTotalDuration(aRollFill) engine.block.setDuration(aRoll, duration = rollDuration) engine.block.fillParent(videoTrack) // Overlay track (B-roll or picture-in-picture) val overlayTrack = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = overlayTrack) val overlayClip = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(overlayClip, shape = engine.block.createShape(ShapeType.Rect)) val overlayFill = engine.block.createFill(FillType.Video) engine.block.setString( block = overlayFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v2/ly.img.video/videos/pexels-kampus-production-8154913.mp4" ) engine.block.setFill(overlayClip, fill = overlayFill) // Position overlay visually engine.block.setPositionX(overlayClip, value = 400F) engine.block.setPositionY(overlayClip, value = 200F) engine.block.setWidth(overlayClip, value = 225F) engine.block.setHeight(overlayClip, value = 500F) engine.block.appendChild(parent = overlayTrack, child = overlayClip) engine.block.forceLoadAVResource(overlayFill) val duration = engine.block.getAVResourceTotalDuration(overlayFill) engine.block.setDuration(overlayClip, duration = duration) // Audio bed track val audioTrack = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = audioTrack) val audioClip = engine.block.create(DesignBlockType.Audio) engine.block.setString( block = audioClip, property = "audio/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.audio/audios/far_from_home.m4a" ) engine.block.appendChild(parent = audioTrack, child = audioClip) // Set duration of composition to be the same as the longer clip engine.block.setDuration(page, duration = maxOf(rollDuration, duration)) // Start playing engine.block.setPlaying(page, enabled = true) } ``` The preceding code creates a complete multi-track video timeline with A-roll, B-roll overlay, and background audio. ## Trimming and Clip Duration The `duration` of the page block controls the length of the final composition. If you don't set a duration for clips, they truncate after a few seconds. Setting a duration for a clip that's longer than the video asset for that clip causes the asset to loop. Setting a duration for a page that's longer than the duration of its clips results in a blank screen. Use `getAVResourceTotalDuration()` on audio clips or video fills to get the duration of the underlying source media. CE.SDK gives you fine control over: - **trim start** - **trim length** - **timeline position** Each clip can define how much of its source video to display and where it begins in the composition's timeline. Assume `aRoll` is a `Graphic` block and `aRollFill` is its `Video` fill. ```kotlin import ly.img.engine.Engine // Skip the first 2 seconds of the source engine.block.setTrimOffset( block = aRollFill, offset = 2.0 ) // Play only 5 seconds after the trim offset engine.block.setTrimLength( block = aRollFill, length = 5.0 ) ``` Use `setTimeOffset` on the clip block to move it along the track: ```kotlin // Start this clip 10 seconds into the track engine.block.setTimeOffset( block = aRoll, offset = 10.0 ) ``` ## Timeline Playback Control You can preview playback using the **Block API** after you've placed and trimmed clips. See [Control Audio and Video](https://img.ly/docs/cesdk/android/create-video/control-daba54/) for detailed playback control. That guide covers: - Play, pause, and seek. - Playback speed and looping. - Current playback time queries. - Synchronization across different tracks. Here's a quick example: ```kotlin import ly.img.engine.Engine // Start playback engine.block.setPlaying(page, enabled = true) // Check if playing val isPlaying = engine.block.isPlaying(page) // Seek to specific time (in seconds) engine.block.setPlaybackTime(page, time = 5.0) // Get current playback time val currentTime = engine.block.getPlaybackTime(page) // Pause playback engine.block.setPlaying(page, enabled = false) ``` ## Generating Timeline Thumbnails You can render thumbnails directly from any video clip using CE.SDK's **asynchronous** thumbnail generator. The API returns a `Flow` that emits thumbnail frames. ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import ly.img.engine.Engine fun generateVideoThumbnails( engine: Engine, videoFill: Int ) = CoroutineScope(Dispatchers.Main).launch { engine.block.generateVideoThumbnailSequence( block = videoFill, thumbnailHeight = 128, timeBegin = 0.0, timeEnd = 10.0, numberOfFrames = 20 ).onEach { thumbnail -> // thumbnail.frameIndex: Int // thumbnail.width: Int // thumbnail.height: Int // thumbnail.data: ByteBuffer (RGBA pixel data) println("Frame ${thumbnail.frameIndex}: ${thumbnail.width}x${thumbnail.height}") // Convert ByteBuffer to Bitmap if needed for display // val bitmap = Bitmap.createBitmap(thumbnail.width, thumbnail.height, Bitmap.Config.ARGB_8888) // bitmap.copyPixelsFromBuffer(thumbnail.data) }.collect() } ``` Each emitted `VideoThumbnail` corresponds to a frame sample along the clip's timeline. You can display these in a `RecyclerView` or custom view to create a scrubber or timeline strip. ### Audio Waveforms Generate **Audio waveforms** in a similar way using `generateAudioThumbnailSequence`. This function emits a `Flow` of `AudioThumbnail` structs, which contain normalized audio samples (0…1). You can use the samples to render a waveform in a custom view. ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import ly.img.engine.Engine fun generateAudioWaveform( engine: Engine, audioClip: Int ) = CoroutineScope(Dispatchers.Main).launch { engine.block.generateAudioThumbnailSequence( block = audioClip, samplesPerChunk = 512, timeBegin = 0.0, timeEnd = 10.0, numberOfSamples = 8192, numberOfChannels = 2 // 1 = mono, 2 = stereo (interleaved L/R) ).onEach { audioThumbnail -> // audioThumbnail.chunkIndex: Int // audioThumbnail.samples: FloatArray (normalized 0.0 to 1.0) println("Audio chunk ${audioThumbnail.chunkIndex}: ${audioThumbnail.samples.size} samples") // Use samples to draw waveform // drawWaveform(audioThumbnail.samples) }.collect() } ``` > **Note:** Rendering a waveform is application-specific and must be implemented using a custom Android view or Canvas drawing. ## Exporting the Timeline To export a timeline, you export the `page` block as a video file. `exportVideo` accepts a progress callback to report export progress. ```kotlin import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Engine import ly.img.engine.MimeType import java.io.File suspend fun exportTimeline( engine: Engine, context: Context, page: Int ): File { // Get page duration val duration = engine.block.getDuration(page) // Export the page as MP4 val blob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = duration, mimeType = MimeType.MP4, progressCallback = { progress -> println("Export progress: ${progress.renderedFrames}/${progress.totalFrames} frames") println("Encoded: ${progress.encodedFrames} frames") } ) // Save to file val outputFile = File(context.filesDir, "export_${System.currentTimeMillis()}.mp4") withContext(Dispatchers.IO) { outputFile.outputStream().channel.use { channel -> channel.write(blob) } } return outputFile } ``` CE.SDK supports standard formats (MP4, MOV, WebM, and audio-only tracks). ## Complete Timeline Example Here's a complete example that creates a timeline, adds multiple clips, trims them, and exports: ```kotlin import android.content.Context import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.MimeType import ly.img.engine.ShapeType import java.io.File fun buildAndExportTimeline( context: Context, license: String, userId: String ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.timeline") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1280, height = 720) // Create scene and page val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1280F) engine.block.setHeight(page, value = 720F) // Create track val track = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = track) // Add first video clip val video1 = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(video1, shape = engine.block.createShape(ShapeType.Rect)) val videoFill1 = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill1, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4" ) engine.block.setFill(video1, fill = videoFill1) engine.block.appendChild(parent = track, child = video1) // Trim first clip: skip first 1 second, play 10 seconds engine.block.forceLoadAVResource(videoFill1) engine.block.setTrimOffset(videoFill1, offset = 1.0) engine.block.setTrimLength(videoFill1, length = 10.0) engine.block.setDuration(video1, duration = 10.0) // Add second video clip (automatically placed after first) val video2 = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(video2, shape = engine.block.createShape(ShapeType.Rect)) val videoFill2 = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill2, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v2/ly.img.video/videos/pexels-kampus-production-8154913.mp4" ) engine.block.setFill(video2, fill = videoFill2) engine.block.appendChild(parent = track, child = video2) engine.block.setDuration(video2, duration = 8.0) engine.block.fillParent(track) // Set page duration (10 + 8 = 18 seconds) engine.block.setDuration(page, duration = 18.0) // Export the timeline val blob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = 18.0, mimeType = MimeType.MP4, progressCallback = { progress -> println("Rendering: ${progress.renderedFrames}/${progress.totalFrames}") } ) // Save to file val outputFile = File(context.filesDir, "timeline_export.mp4") withContext(Dispatchers.IO) { outputFile.outputStream().channel.use { it.write(blob) } } println("Timeline exported to: ${outputFile.absolutePath}") engine.stop() } ``` ## Troubleshooting | Symptom | Likely Cause | Solution | |----------|---------------|-----------| | Clips overlap or play out of order | Misaligned time offset values | Ensure each clip's start time is unique and sequential | | Trim changes ignored | Trim start + duration exceed source length | Use `getAVResourceTotalDuration()` to confirm clip duration | | Thumbnails are blank | Resource not loaded yet | Call `forceLoadAVResource()` before generating thumbnails | | Playback stutters | Too many parallel HD tracks | Reduce simultaneous tracks or use compressed preview | | Export fails | Page duration not set | Always set page duration using `setDuration()` | *** ## Next Steps - Use [Control Audio and Video](https://img.ly/docs/cesdk/android/create-video/control-daba54/) to play, pause, seek, loop, and adjust volume or speed for timeline content. - [Add Captions](https://img.ly/docs/cesdk/android/edit-video/add-captions-f67565/) to place timed text that stays in sync with video and audio. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Edit Image" description: "Use CE.SDK to crop, transform, annotate, or enhance images with editing tools and programmatic APIs." platform: android url: "https://img.ly/docs/cesdk/android/edit-image-c64912/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) --- --- ## Related Pages - [Integrating Background Removal in Android (Kotlin)](https://img.ly/docs/cesdk/android/edit-image/remove-bg-9dfcf7/) - Learn how to implement custom background removal using Google ML Kit's Selfie Segmentation in Android with CE.SDK. - [Transform](https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/) - Crop, resize, rotate, scale, or flip images using CE.SDK's built-in transformation tools. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Integrating Background Removal in Android (Kotlin)" description: "Learn how to implement custom background removal using Google ML Kit's Selfie Segmentation in Android with CE.SDK." platform: android url: "https://img.ly/docs/cesdk/android/edit-image/remove-bg-9dfcf7/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) > [Remove Background](https://img.ly/docs/cesdk/android/edit-image/remove-bg-9dfcf7/) --- The CE.SDK provides a flexible architecture that allows you to extend its capability to meet your specific needs. This guide demonstrates how to integrate a custom background removal feature using Google ML Kit. You can apply the same approach to implement any custom image processing functionality in your Android app. > **Note:** **ML Kit Selfie Segmentation** works best with images containing people. For more general subject segmentation, you may need to use third-party libraries or cloud-based APIs. ## What You'll Learn - How to extract the current image from the CE.SDK engine. - How to implement background removal using ML Kit Selfie Segmentation. - How to process the image and apply the segmentation mask. - How to update the image in the scene with the processed result. - How to keep operations async and handle errors gracefully. ## When To Use It - Want programmatic "Remove BG" functionality in your app. - Prefer on-device processing (no uploads) for latency, privacy, or offline use. - Need to integrate custom image processing logic (ML Kit, third-party libraries, or your own API). - Building custom UI or automation workflows. ## Setup ML Kit First, add the ML Kit Selfie Segmentation dependency to your `build.gradle`: ```kotlin dependencies { implementation("com.google.mlkit:segmentation-selfie:16.0.0-beta5") } ``` ## Extracting the Image A block that displays an image has an `imageFill` which contains the URI of the underlying image. The first step is to extract the image data from the fill. ```kotlin import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import java.io.ByteArrayOutputStream import java.net.URL suspend fun extractImageFromScene( engine: Engine, context: Context ): Bitmap? { // Get the current page from the scene val page = engine.scene.getCurrentPage() ?: return null // Validate that the page contains an image val imageFill = engine.block.getFill(page) val fillType = engine.block.getType(imageFill) if (fillType != "//ly.img.ubq/fill/image") { return null } // Extract the image URI val imageFileURI = engine.block.getString(imageFill, property = "fill/image/imageFileURI") // Download and decode the image return withContext(Dispatchers.IO) { try { val imageData = if (imageFileURI.startsWith("http")) { // Download from URL val url = URL(imageFileURI) val outputStream = ByteArrayOutputStream() url.openStream().use { inputStream -> outputStream.use(inputStream::copyTo) } outputStream.toByteArray() } else { // Load from local file or content URI val uri = Uri.parse(imageFileURI) val inputStream = context.contentResolver.openInputStream(uri) inputStream?.readBytes() ?: byteArrayOf() } BitmapFactory.decodeByteArray(imageData, 0, imageData.size) } catch (e: Exception) { null } } } ``` ## Processing the Image with ML Kit With a `Bitmap`, you can now process the image using ML Kit Selfie Segmentation. Here's a complete implementation: ```kotlin import android.graphics.Bitmap import android.graphics.Color import com.google.mlkit.vision.common.InputImage import com.google.mlkit.vision.segmentation.Segmentation import com.google.mlkit.vision.segmentation.SegmentationMask import com.google.mlkit.vision.segmentation.selfie.SelfieSegmenterOptions import kotlinx.coroutines.tasks.await /** * Removes the background from an image using ML Kit Selfie Segmentation. * @param original The source bitmap to process. * @return A new bitmap with the background removed (transparent), or null if processing fails. */ suspend fun removeBackgroundWithMLKit(original: Bitmap): Bitmap? { try { // Configure ML Kit Selfie Segmentation val options = SelfieSegmenterOptions.Builder() .setDetectorMode(SelfieSegmenterOptions.SINGLE_IMAGE_MODE) .enableRawSizeMask() // Get mask at original image resolution .build() val segmenter = Segmentation.getClient(options) // Create InputImage from bitmap val inputImage = InputImage.fromBitmap(original, 0) // Process the image and get the segmentation mask val segmentationMask: SegmentationMask = segmenter.process(inputImage).await() // Create output bitmap with transparency val width = original.width val height = original.height val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) // Get mask data val mask = segmentationMask.buffer val maskWidth = segmentationMask.width val maskHeight = segmentationMask.height // Apply the mask to create transparent background for (y in 0 until height) { for (x in 0 until width) { // Map image coordinates to mask coordinates val maskX = (x * maskWidth / width).coerceIn(0, maskWidth - 1) val maskY = (y * maskHeight / height).coerceIn(0, maskHeight - 1) // Get mask confidence (0.0 = background, 1.0 = foreground) val maskIndex = maskY * maskWidth + maskX val confidence = mask.getFloat(maskIndex * 4) // 4 bytes per float // Get original pixel color val originalPixel = original.getPixel(x, y) if (confidence > 0.5f) { // Foreground - keep original pixel result.setPixel(x, y, originalPixel) } else { // Background - make transparent result.setPixel(x, y, Color.TRANSPARENT) } } } // Clean up segmenter.close() return result } catch (e: Exception) { e.printStackTrace() return null } } ``` ## Replace the Image in the Scene With a processed image, the last step is to update the fill with the new image: 1. Save the processed bitmap to a file. 2. Get the file URI. 3. Update the image fill's source set with the new URI. ```kotlin import android.content.Context import android.graphics.Bitmap import android.net.Uri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Engine import ly.img.engine.Source import java.io.File import java.io.FileOutputStream suspend fun replaceImageInScene( engine: Engine, context: Context, processedBitmap: Bitmap ) { // Save the bitmap to a file val imageFile = withContext(Dispatchers.IO) { val file = File(context.cacheDir, "bg_removed_${System.currentTimeMillis()}.png") FileOutputStream(file).use { outputStream -> processedBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) } file } val processedImageUri = Uri.fromFile(imageFile) // Get the current page and its fill val page = engine.scene.getCurrentPage() ?: return val imageFill = engine.block.getFill(page) // Update the source set with the new image engine.block.setSourceSet( block = imageFill, property = "fill/image/sourceSet", sourceSet = listOf( Source( uri = processedImageUri, width = processedBitmap.width.toUInt(), height = processedBitmap.height.toUInt() ) ) ) } ``` ## Complete Function Here's the complete function that combines all the steps: ```kotlin import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Color import android.net.Uri import com.google.mlkit.vision.common.InputImage import com.google.mlkit.vision.segmentation.Segmentation import com.google.mlkit.vision.segmentation.SegmentationMask import com.google.mlkit.vision.segmentation.selfie.SelfieSegmenterOptions import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.tasks.await import kotlinx.coroutines.withContext import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.Source import java.io.ByteArrayOutputStream import java.io.File import java.io.FileOutputStream import java.net.URL suspend fun performBackgroundRemoval( engine: Engine, context: Context ): Boolean { return withContext(Dispatchers.Main) { try { // Step 1: Get the current page val page = engine.scene.getCurrentPage() ?: return@withContext false // Step 2: Validate it's an image fill val imageFill = engine.block.getFill(page) val fillType = engine.block.getType(imageFill) if (fillType != "//ly.img.ubq/fill/image") { return@withContext false } // Step 3: Extract image data val imageFileURI = engine.block.getString(imageFill, property = "fill/image/imageFileURI") val originalBitmap = withContext(Dispatchers.IO) { val imageData = if (imageFileURI.startsWith("http")) { val url = URL(imageFileURI) val outputStream = ByteArrayOutputStream() url.openStream().use { inputStream -> outputStream.use(inputStream::copyTo) } outputStream.toByteArray() } else { val uri = Uri.parse(imageFileURI) context.contentResolver.openInputStream(uri)?.readBytes() ?: byteArrayOf() } BitmapFactory.decodeByteArray(imageData, 0, imageData.size) } ?: return@withContext false // Step 4: Process with ML Kit val options = SelfieSegmenterOptions.Builder() .setDetectorMode(SelfieSegmenterOptions.SINGLE_IMAGE_MODE) .enableRawSizeMask() .build() val segmenter = Segmentation.getClient(options) val inputImage = InputImage.fromBitmap(originalBitmap, 0) val segmentationMask: SegmentationMask = segmenter.process(inputImage).await() // Step 5: Apply mask to create transparent background val width = originalBitmap.width val height = originalBitmap.height val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) val mask = segmentationMask.buffer val maskWidth = segmentationMask.width val maskHeight = segmentationMask.height for (y in 0 until height) { for (x in 0 until width) { val maskX = (x * maskWidth / width).coerceIn(0, maskWidth - 1) val maskY = (y * maskHeight / height).coerceIn(0, maskHeight - 1) val maskIndex = maskY * maskWidth + maskX val confidence = mask.getFloat(maskIndex * 4) val originalPixel = originalBitmap.getPixel(x, y) result.setPixel( x, y, if (confidence > 0.5f) originalPixel else Color.TRANSPARENT ) } } segmenter.close() // Step 6: Save processed image val imageFile = withContext(Dispatchers.IO) { val file = File(context.cacheDir, "bg_removed_${System.currentTimeMillis()}.png") FileOutputStream(file).use { outputStream -> result.compress(Bitmap.CompressFormat.PNG, 100, outputStream) } file } val processedImageUri = Uri.fromFile(imageFile) // Step 7: Update the scene engine.block.setSourceSet( block = imageFill, property = "fill/image/sourceSet", sourceSet = listOf( Source( uri = processedImageUri, width = result.width.toUInt(), height = result.height.toUInt() ) ) ) return@withContext true } catch (e: Exception) { e.printStackTrace() return@withContext false } } } ``` ## Usage Example Here's how to use the background removal function in your app: ```kotlin import android.content.Context import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Engine fun removeBackgroundFromCurrentImage( engine: Engine, context: Context ) = CoroutineScope(Dispatchers.Main).launch { val success = performBackgroundRemoval(engine, context) if (success) { println("✅ Background removed successfully!") } else { println("❌ Failed to remove background") } } ``` ## Optimizations ### 1. Performance for Large Images For better performance with large images, downscale before processing: ```kotlin fun downscaleIfNeeded(bitmap: Bitmap, maxSize: Int = 1024): Bitmap { val maxDimension = maxOf(bitmap.width, bitmap.height) if (maxDimension <= maxSize) return bitmap val scale = maxSize.toFloat() / maxDimension val newWidth = (bitmap.width * scale).toInt() val newHeight = (bitmap.height * scale).toInt() return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true) } ``` ### 2. Improve Mask Quality Apply smoothing to the mask for better edge quality: ```kotlin /** * Apply feathering to the mask for softer edges. */ fun applyFeathering(confidence: Float, threshold: Float = 0.5f, feather: Float = 0.1f): Float { return when { confidence >= threshold + feather -> 1.0f confidence <= threshold - feather -> 0.0f else -> { // Linear interpolation in the feather range (confidence - (threshold - feather)) / (2 * feather) } } } ``` Then use it when applying the mask: ```kotlin val alpha = applyFeathering(confidence) if (alpha > 0) { val r = Color.red(originalPixel) val g = Color.green(originalPixel) val b = Color.blue(originalPixel) val newAlpha = (alpha * 255).toInt() result.setPixel(x, y, Color.argb(newAlpha, r, g, b)) } else { result.setPixel(x, y, Color.TRANSPARENT) } ``` ## Troubleshooting **❌ Fill is not an image**: Always check the fill type before processing: ```kotlin val fillType = engine.block.getType(imageFill) if (fillType != "//ly.img.ubq/fill/image") { // Not an image fill, cannot process return } ``` **❌ Mask quality is poor**: - Use `enableRawSizeMask()` for full-resolution masks. - Apply feathering (see optimization section above). - Consider pre-processing the image for better lighting/contrast. **❌ Performance is slow on large images**: - Downscale images before processing (see optimization section). - Use `STREAM_MODE` instead of `SINGLE_IMAGE_MODE` for video. - Process on a background thread (already handled with coroutines in the example). **❌ Segmentation only works for people**: ML Kit Selfie Segmentation is optimized for human subjects. For general object segmentation, consider: - Using TensorFlow Lite models for object detection. - Cloud-based APIs (Google Cloud Vision, etc.). - Third-party segmentation libraries. **❌ App crashes or ML Kit errors**: - Ensure ML Kit dependencies are properly included. - Check that Google Play Services are available on the device. - Handle exceptions gracefully and provide user feedback. ## Alternative: Custom Segmentation Models For more advanced segmentation beyond people, you can use TensorFlow Lite models. Here's a basic structure: ```kotlin import org.tensorflow.lite.Interpreter import java.nio.ByteBuffer class CustomSegmentation(modelPath: String) { private val interpreter: Interpreter = Interpreter(loadModelFile(modelPath)) fun segment(bitmap: Bitmap): Bitmap? { // 1. Preprocess bitmap to model input format // 2. Run inference // 3. Post-process output to mask // 4. Apply mask to create transparent background // Implementation depends on your specific model return null } private fun loadModelFile(modelPath: String): ByteBuffer { // Load TFLite model file // Implementation omitted for brevity TODO("Load your .tflite model file") } } ``` ## Next Steps Now that you can remove backgrounds, explore related guides: - [Scale & Transform](https://img.ly/docs/cesdk/android/edit-image/transform/scale-ebe367/) images after background removal. - [Chroma Key](https://img.ly/docs/cesdk/android/filters-and-effects/chroma-key-green-screen-1e3e99/) for green screen effects. - [Batch Processing](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/) for processing multiple images. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Transform" description: "Crop, resize, rotate, scale, or flip images using CE.SDK's built-in transformation tools." platform: android url: "https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) > [Transform](https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/) --- --- ## Related Pages - [Move](https://img.ly/docs/cesdk/android/edit-image/transform/move-818dd9/) - Position an image relative to its parent using either percentage or units - [Crop Images in Android](https://img.ly/docs/cesdk/android/edit-image/transform/crop-f67a47/) - Cut out specific areas of an image to focus on key content or change aspect ratio. - [Rotate](https://img.ly/docs/cesdk/android/edit-image/transform/rotate-5f39c9/) - Documentation for Rotate - [Resize](https://img.ly/docs/cesdk/android/edit-image/transform/resize-407242/) - Change the size of individual elements or groups. - [Scale in Android (Kotlin)](https://img.ly/docs/cesdk/android/edit-image/transform/scale-ebe367/) - Resize images uniformly in your Android app using Kotlin. - [Flip Images](https://img.ly/docs/cesdk/android/edit-image/transform/flip-035e9f/) - Flip images horizontally or vertically. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Crop Images in Android" description: "Cut out specific areas of an image to focus on key content or change aspect ratio." platform: android url: "https://img.ly/docs/cesdk/android/edit-image/transform/crop-f67a47/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) > [Transform](https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/) > [Crop](https://img.ly/docs/cesdk/android/edit-image/transform/crop-f67a47/) --- The CreativeEditor SDK (CE.SDK) offers both interactive UI components and powerful Kotlin APIs for cropping images. Image cropping is an essential feature for any Android photo editing app, allowing users to focus on important content and fit images to specific dimensions. Whether you need simple aspect ratio adjustments or advanced programmatic, follow this guide to learn how to integrate cropping into your Android app. ## Interactive crop interface The SDK includes ready-to-use crop controls that integrate seamlessly with your Android app. These components: - Handle tap gestures and aspect ratio selection. - Provide immediate visual feedback to users. This is particularly useful for: - Apps targeting social media formats. - Maintaining consistent visual branding. ![Crop tool appears when an image is selected](../mobile-assets/crop-tool.png) ### How users interact with crop tools 1. **Tap the image** to select it for editing. 2. **Tap the crop button** in your app's editing interface. 3. **Drag handles** at corners and edges to define the crop region. 4. **Apply transformations** like flip or rotate before finalizing. 5. **Confirm changes** to complete the crop operation. ![An image that has been scale cropped and rotated slightly showing the cropped and original image.](../mobile-assets/ui-crop-workflow.png) Once cropped, your image: - Updates in the editor. - Preserves the original data and transformation history for future adjustments. ### Configuring crop capabilities By default, cropping is enabled in the editor UI. When building custom interfaces or specialized editing flows, you can control crop availability through configuration settings: ```kotlin engine.editor.setSettingBoolean("doubleClickToCropEnabled", true) engine.editor.setSettingBoolean("controlGizmo/showCropHandles", true) engine.editor.setSettingBoolean("controlGizmo/showCropScaleHandles", true) ``` The cropping handles are only available when a selected block has a fill of type `FillType.Image`. Otherwise setting the edit mode of the `engine.editor` to crop has no effect. ## Crop images with Kotlin code For advanced Android applications, you'll often need precise control over cropping operations through code. This approach suits very well: - Batch processing - Automated workflows - Custom editing interfaces implementations. The SDK automatically handles image fitting when you load content into blocks – if your image dimensions don't match the container, intelligent cropping is applied automatically. When implementing crop operations in your Kotlin code, keep in mind that you're manipulating the underlying image's scale, position, and orientation properties. The examples shown typically modify both x and y axes uniformly, but you can adjust them independently for creative distortion effects. ### Reset Crop When an image is initially placed into a block it will get crop scale and crop translation values. Resetting the crop will return the image to the original values. ![Image with no additional crop applied shown in crop mode](../mobile-assets/crop-example-1.png) This is a block (called `imageBlock` in the example code) with the following elements: - Dimensions of 400 × 400. - Filled with an image that has dimensions of 600 × 530. - The image has slight scaling and translation applied so that it fills the block evenly. At any time, the code can execute the reset crop command to return it to this stage. ```kotlin engine.block.resetCrop(imageBlock) ``` ### Crop Translation The **translation values**: - Adjust the placement of the **origin point** of an image. - Can be read and changed. - Aren't pixel units or centimeters, but are scaled percentages. An image that has its origin point at the origin point of the crop block will have a translation value of 0.0 for x and y. ![Image crop translated one quarter of it's width to the right](../mobile-assets/crop-example-5.png) ```kotlin engine.block.setCropTranslationX(imageBlock, 0.25f) ``` This image: - Has had its translation in the x direction set to 0.25. - Was moved 1/4 of its width to the right as a result. Setting the value to -0.25 would shift the origin to the left. These are absolute values. Setting the x value to 0.25 and then setting it to -0.25 does not move the image to an offset of 0.0. How values might affect the image: - `setCropTranslationY(block: DesignBlock, translationY: Float)` function adjusts the translation of the image in the **vertical direction**. - **Negative** values move the image **up**. - **Positive** values move the image **down**. To read the current crop translation values you can use the convenience getters for the x and y values. ```kotlin val currentX = engine.block.getCropTranslationX(imageBlock) val currentY = engine.block.getCropTranslationY(imageBlock) ``` ### Crop Scale The scale values: - Adjust the height and width of the underlying image. - Make the image **larger** when greater than **1.0**. - Make the image **smaller** when less than 1.0. Unless the image also has offsetting translation applied, the center of the image will move. ![Image crop scaled by 1.5 with no corresponding translation adjustment](../mobile-assets/crop-example-6.png) This image has been scaled by 1.5 in the x and y directions, but the origin point has not been translated. So, the center of the image has moved. ```kotlin engine.block.setCropScaleX(imageBlock, 1.5f) engine.block.setCropScaleY(imageBlock, 1.5f) ``` To read the current crop scale values you can use the convenience getters for the x and y values. ```kotlin val currentX = engine.block.getCropScaleX(imageBlock) val currentY = engine.block.getCropScaleY(imageBlock) ``` ## Crop Rotate Similar to rotating blocks, the crop rotation function uses radians in the following way: - **Positive** values rotate clockwise. - **Negative** values rotate counterclockwise. - The image rotates around its **center**. ![Image crop rotated by pi/4 or 45 degrees](../mobile-assets/crop-example-7.png) ```kotlin import kotlin.math.PI engine.block.setCropRotation(imageBlock, (PI / 4.0).toFloat()) ``` For working with radians, Kotlin has a constant defined for pi. It can be used as `PI` from `kotlin.math.PI`. Because the `setCropRotation` function takes a `Float` for the rotation value, you can use `.toFloat()` to convert the Double to Float. ### Crop to Scale Ratio To center crop an image, you can use the scale ratio. This will adjust the x and y scales of the image evenly, and adjust the translation to keep it centered. ![Image cropped using the scale ratio to remain centered](../mobile-assets/crop-example-2.png) This image has been scaled by 2.0 in the x and y directions. Its translation has been adjusted by -0.5 in the x and y directions to keep the image centered. ```kotlin engine.block.setCropScaleRatio(imageBlock, 2.0f) ``` Using the crop scale ratio function is the same as calling the translation and scale functions, but in one line. ```kotlin engine.block.setCropScaleX(imageBlock, 2.0f) engine.block.setCropScaleY(imageBlock, 2.0f) engine.block.setCropTranslationX(imageBlock, -0.5f) engine.block.setCropTranslationY(imageBlock, -0.5f) ``` ### Chained Crops Crop operations can be chained together. The order of the chaining impacts the final image. ![Image cropped and rotated](../mobile-assets/crop-example-3.png) ```kotlin import kotlin.math.PI engine.block.setCropScaleRatio(imageBlock, 2.0f) engine.block.setCropRotation(imageBlock, (PI / 3.0).toFloat()) ``` ![Image rotated first and then scaled](../mobile-assets/crop-example-4.png) ```kotlin import kotlin.math.PI engine.block.setCropRotation(imageBlock, (PI / 3.0).toFloat()) engine.block.setCropScaleRatio(imageBlock, 2.0f) ``` ### Flipping the Crop There are two functions for crop flipping the image: - Horizontal - Vertical They each flip the image along its center. ![Image crop flipped vertically](../mobile-assets/crop-example-8.png) ```kotlin engine.block.flipCropVertical(imageBlock) engine.block.flipCropHorizontal(imageBlock) ``` The image will be crop flipped every time the function gets called. So calling the function an even number of times will return the image to its original orientation. ### Filling the Frame When the various crop operations cause the background of the crop block to be displayed, such as in the **Crop Translation** example above, the function ```kotlin engine.block.adjustCropToFillFrame(imageBlock, minScaleRatio = 1.0f) ``` will adjust the translation values and the scale values of the image so that the entire crop block is filled. This is not the same as resetting the crop. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Flip Images" description: "Flip images horizontally or vertically." platform: android url: "https://img.ly/docs/cesdk/android/edit-image/transform/flip-035e9f/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) > [Transform](https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/) > [Flip](https://img.ly/docs/cesdk/android/edit-image/transform/flip-035e9f/) --- The CreativeEditor SDK includes a **flipping** feature that you can add to your **Android** app. Flipping is a powerful transformation that Android photo apps use for creating mirror effects, correcting orientation, and achieving symmetrical layouts. Learn in this guide how to implement both **interactive** flip controls and **programmatic** flipping, through clean **Kotlin** APIs. ## Flip features The flip feature enables the following actions: - Horizontal and vertical image mirroring - Integration with template systems and creative workflows - Programmatic flip state management and toggling ## Flip applications Implement flipping for: - Correcting selfie camera orientation in Android camera apps - Creating mirror effects for product photography - Building symmetrical design layouts and compositions *** ## Flip horizontally or vertically Use the `flip/horizontal` and `flip/vertical` properties to control mirroring. These are **boolean** properties with defined helper functions. All flips occur around the center point of a block. ```kotlin engine.block.setFlipVertical(imageBlock, true) engine.block.setFlipHorizontal(imageBlock, true) ``` To determine if a block has been flipped, you can either: - Query the **properties**. - Use **helper functions**. ```kotlin val isFlippedVertical = engine.block.getFlipVertical(imageBlock) val isFlippedHorizontal = engine.block.getFlipHorizontal(imageBlock) ``` *** ## Toggle flipping To toggle the flip state, the code reads the current flip value and sets it to its opposite: ```kotlin val currentVerticalFlip = engine.block.getFlipVertical(imageBlock) engine.block.setFlipVertical(imageBlock, !currentVerticalFlip) val currentHorizontalFlip = engine.block.getFlipHorizontal(imageBlock) engine.block.setFlipHorizontal(imageBlock, !currentHorizontalFlip) ``` ## Reset flipping To reset all flips: ```kotlin engine.block.setFlipVertical(imageBlock, false) engine.block.setFlipHorizontal(imageBlock, false) ``` *** ## Flip multiple elements Group elements to flip them together: ```kotlin val groupId = engine.block.group(listOf(imageBlock, textBlock)) engine.block.setFlipHorizontal(groupId, true) ``` *** --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Move" description: "Position an image relative to its parent using either percentage or units" platform: android url: "https://img.ly/docs/cesdk/android/edit-image/transform/move-818dd9/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) > [Transform](https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/) > [Move](https://img.ly/docs/cesdk/android/edit-image/transform/move-818dd9/) --- The CreativeEditor SDK provides a **positioning** feature you can add to your Android app. Positioning images accurately is fundamental to creating **professional layouts**. Both drag-and-drop interfaces and precise coordinate-based positioning are available through Kotlin. Whether you're building **grid layouts**, **freeform canvases**, or **template-based designs**, this guide covers all positioning needs. ## Movement Capabilities The positioning feature in CE.SDK enables the following movements: - **Precise positioning** with Kotlin coordinate APIs. - **Drag-and-drop** interface for user interaction. - Canvas-based absolute and **percentage** positioning. - Group movement for **maintaining element relationships**. - Position constraints for **template protection**. ## Position control scenarios Implement positioning to: - Create pixel-perfect layouts for Android interfaces. - Enable intuitive drag-and-drop editing experiences. - Create snap-to-grid or guided positioning systems. *** ## Move an image block programmatically Image position is controlled using the `position/x` and `position/y` properties. They can use either absolute or relative (percentage) values. Helper functions are also available for setting properties. For example, the following code moves the image to coordinates (150, 100) on the canvas. ```kotlin engine.block.setFloat(imageBlock, "position/x", 150f) engine.block.setFloat(imageBlock, "position/y", 100f) ``` or ```kotlin engine.block.setPositionX(imageBlock, 150f) engine.block.setPositionY(imageBlock, 100f) ``` For percentage-based positioning, the following code moves the image to the center of the canvas, regardless of the dimensions of the canvas: ```kotlin import ly.img.engine.PositionMode engine.block.setPositionXMode(imageBlock, PositionMode.PERCENT) engine.block.setPositionYMode(imageBlock, PositionMode.PERCENT) engine.block.setPositionX(imageBlock, 0.5f) engine.block.setPositionY(imageBlock, 0.5f) ``` As with setting position, you can update or check the mode using `position/x/mode` and `position/y/mode` properties. ```kotlin val xPosition = engine.block.getPositionX(imageBlock) val yPosition = engine.block.getPositionY(imageBlock) ``` *** ## Move images with the UI Users can drag and drop elements directly in the editor canvas. *** ## Move multiple elements together Group elements before moving to keep them aligned: ```kotlin val groupId = engine.block.group(listOf(imageBlock, textBlock)) engine.block.setPositionX(groupId, 200f) ``` The preceding code moves the entire group to 200 from the left edge. *** ## Move relative to current position To nudge an image instead of setting an absolute position: ```kotlin val xPosition = engine.block.getPositionX(imageBlock) engine.block.setPositionX(imageBlock, xPosition + 20f) ``` The preceding code moves the image 20 points to the right. *** ## Lock movement (optional) When building templates, you might want to lock movement to protect the layout: ```kotlin engine.block.setScopeEnabled(imageBlock, "layer/move", false) ``` You can also disable all transformations by locking, this is regardless of working with a template. ```kotlin engine.block.setTransformLocked(imageBlock, true) ``` *** ## Troubleshooting | Issue | Solution | | ------------------------ | ----------------------------------------------------- | | Image not moving | Ensure it is not constrained or locked | | Unexpected position | Check canvas coordinates and alignment settings | | Grouped items misaligned | Confirm all items share the same reference point | | Can't move via UI | Ensure the move feature is enabled in the UI settings | *** --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Resize" description: "Change the size of individual elements or groups." platform: android url: "https://img.ly/docs/cesdk/android/edit-image/transform/resize-407242/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) > [Transform](https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/) > [Resize](https://img.ly/docs/cesdk/android/edit-image/transform/resize-407242/) --- The CreativeEditor SDK (CE.SDK) provides a **resize** feature for **Android** apps. Precise image sizing is crucial for **fitting content** into specific layouts, or exporting to various formats. CE.SDK offers both **interactive** resize controls and powerful **Kotlin** APIs for dimensional control. Whether you're building **social media templates** or **responsive interfaces**, this guide covers all resizing scenarios. ## Resizing features CE.SDK provides comprehensive tools for adjusting image dimensions in your Android app. The resizing feature enables the following actions: - Interactive resize handles for user control - Programmatic dimension setting with Kotlin - Group resizing for maintaining element relationships - Resize restrictions for layout protection ## Common resizing scenarios Apply resizing for: - Fitting images to Android layout constraints - Creating content for different screen densities - Implementing template-based designs with fixed dimensions ### Resize a block using the UI When a block is selected: - **Handles** appear on the four sides to allow the user to resize either the width or the height of the block. - A setting in the `editor` controls the **visibility** of the handles. - When the handles are **invisible**, a user can't resize using tapping or a mouse. ```kotlin engine.editor.setSettingBoolean("controlGizmo/showResizeHandles", false) ``` ![The image on the left has resize handles, the one on the right does not](../mobile-assets/resize-example-1.png) The image on the left displays all resize handles, while the right image has them disabled. Even with resize handles hidden, users can still access scale and rotation controls. ### Resize a block programmatically Each block contains the following **properties** that you can update to resize the block: - `width` - `height` The values of each property can be: - `SizeMode.AUTO`: automatic sizing. - `SizeMode.PERCENT`: the relationship between a block and its parent. For example, a block containing the following properties: - `SizeMode.PERCENT` mode - `width` set to `1.0f` will make its size to 100% of its parent's width. The following code sets a block to be 400 × 400 px: ```kotlin engine.block.setWidth(imageBlock, 400.0f) engine.block.setHeight(imageBlock, 400.0f) ``` ![Image resized to 400px by 400px](../mobile-assets/resize-example-2.png) There's also a convenience function for setting both width and height at once: ```kotlin engine.block.resize(imageBlock, 400.0f, 400.0f) ``` ![Two blocks sized by percentage](../mobile-assets/resize-example-3.png) ```kotlin import ly.img.engine.SizeMode engine.block.setWidthMode(imageBlock, SizeMode.PERCENT) engine.block.setWidth(imageBlock, 0.5f) // 50% of parent width engine.block.setHeightMode(imageBlock, SizeMode.PERCENT) engine.block.setHeight(imageBlock, 0.5f) // 50% of parent height ``` In this code: - The block on the **left** takes up 50% of the width and height of its parent. - The block on the **right** has been set to 25% of the width and height of its parent. ### Resize blocks as a group Group blocks to resize them together: ```kotlin val groupId = engine.block.group(listOf(imageBlock, textBlock)) engine.block.resize(groupId, 600.0f, 400.0f) ``` ### Lock resizing When working with templates, you can lock a block from being resized by setting its scope: ```kotlin engine.block.setScopeEnabled(imageBlock, "layer/resize", false) ``` To prevent users from transforming an element at all: ```kotlin engine.block.setTransformLocked(imageBlock, true) ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Rotate" description: "Documentation for Rotate" platform: android url: "https://img.ly/docs/cesdk/android/edit-image/transform/rotate-5f39c9/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) > [Transform](https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/) > [Rotate](https://img.ly/docs/cesdk/android/edit-image/transform/rotate-5f39c9/) --- The CreativeEditor SDK provides a **rotation** feature for Android apps. Image rotation is a core editing feature that Android users expect in photo apps. The CreativeEditor SDK offers straightforward methods for incorporating both **touch-based rotation** controls and precise **programmatic rotation** using **Kotlin**. This guide covers everything from basic rotation gestures to advanced group transformations and rotation constraints. ## Key rotation features - Touch-based rotation with intuitive drag handles - Precise angle control through Kotlin APIs - Rotation locking for template protection - Multi-element rotation as grouped objects ### Touch-based image rotation Users can rotate images naturally using the built-in rotation handles. When you select an image: 1. Rotation controls automatically appear. 2. You can now freely rotate the image through drag gestures. ![Rotation handle of the control gizmo](../mobile-assets/rotation-handle.png) ### Implementing rotation in Kotlin Your Android app can control image rotation programmatically using the `setRotation` method. Pass the block ID and rotation angle in radians: ```kotlin import kotlin.math.PI engine.block.setRotation(imageBlock, (PI / 4).toFloat()) ``` Since Android developers often work with degrees, here are handy conversion utilities: ```kotlin val angleInRadians: Float = (angleInDegrees * PI / 180).toFloat() val angleInDegrees: Float = (angleInRadians * 180 / PI).toFloat() ``` To read the current rotation state in your app: ```kotlin val currentRotation = engine.block.getRotation(imageBlock) ``` > **Note:** This rotates the entire block. If you want to rotate an image that is filling > a block but not the block, explore the > [crop rotate](https://img.ly/docs/cesdk/android/edit-image/transform/crop-f67a47/) function. ### Locking Rotation You can remove the rotation handle from the UI by changing the setting for the engine. This will affect *all* blocks. ```kotlin engine.editor.setSettingBoolean("controlGizmo/showRotateHandles", false) ``` Although the code makes the rotation handle invisible, the user can still use the two-finger rotation gesture on a touch device. You can turn off that gesture with the following setting: ```kotlin engine.editor.setSettingBoolean("touch/rotateAction", false) ``` When you want to lock only certain blocks, you can toggle the transform lock property. This will apply to all transformations for the block. ```kotlin engine.block.setTransformLocked(imageBlock, true) ``` ### Rotating As a Group To rotate multiple elements together, first add them to a `group` and then rotate the group. ```kotlin import kotlin.math.PI val groupId = engine.block.group(listOf(imageBlock, textBlock)) engine.block.setRotation(groupId, (PI / 2).toFloat()) ``` ### Troubleshooting Troubleshooting | Issue | Solution | | ----------------------------------- | ------------------------------------------------------------------------------- | | Image appears offset after rotation | Make sure the pivot point is centered (default is center). | | Rotation not applying | Confirm that the image block is inserted and rendered before applying rotation. | | Rotation handle not visible | Check that interactive UI controls are enabled in the settings. | --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Scale in Android (Kotlin)" description: "Resize images uniformly in your Android app using Kotlin." platform: android url: "https://img.ly/docs/cesdk/android/edit-image/transform/scale-ebe367/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Images](https://img.ly/docs/cesdk/android/edit-image-c64912/) > [Transform](https://img.ly/docs/cesdk/android/edit-image/transform-9d189b/) > [Scale](https://img.ly/docs/cesdk/android/edit-image/transform/scale-ebe367/) --- Scaling lets users enlarge or shrink a block directly on the canvas. In CE.SDK, scaling is a transform property that applies uniformly to most block types. This guide shows how to scale images using CE.SDK in your Android app using Kotlin. You'll learn how to scale image blocks proportionally, scale groups, and apply scaling constraints to protect template structure. The standard UI already supports pinch-to-zoom and on-screen scale handles. Scaling programmatically gives you finer control. This is ideal for automation, custom UI, or template-driven apps. When you want to scale the image **inside** the block and leave the block dimensions unchanged, you'll use [crop scale](https://img.ly/docs/cesdk/android/edit-image/transform/crop-f67a47/) instead. ## What You'll Learn - Scale images programmatically using Kotlin. - Scale images proportionally or non-uniformly. - Scale grouped elements. - Enable or disable scaling via pinch gestures or gizmo handles. ## When to Use Use image scaling when your UI needs to: - Let users zoom artwork smoothly without cropping - Enforce a canonical image size in templates - Support controls like sliders instead of gestures - Scale multiple elements together (logos, product bundles, captions) ## Scaling Basics On Android, you scale blocks using the **block API**. The main pieces you'll use are: - `engine.block.scale(block: Int, scaleX: Float, scaleY: Float, anchorX: Float = 0f, anchorY: Float = 0f)` - `width` / `height` and their modes (`setWidthMode`, `setHeightMode`) - crop-related functions like `setCropScaleX`, `setCropScaleY`, and `setCropTranslationX` / `Y` Control the size with the following scale values: - `1.0f`: represents the **original** size. - Larger than `1.0f`: **increases** the size. - Smaller than `1.0f`: **shrinks** the size. > **Note:** The examples below use image blocks, but this same approach works for shapes, text, stickers, and groups as long as you have their block ID. ## Scale an Image Uniformly Uniform scaling uses the `scale()` function. A scale value of `1.0f` is the original scale. Values larger than `1.0f` increase the scale of the block and values lower than `1.0f` scale the block smaller. A value of `2.0f`, for example makes the block twice as large. This scales the image to 150% of its original size. Because the default anchor point is the top-left corner, the block grows outward from that corner. ```kotlin import ly.img.engine.Engine engine.block.scale(imageBlock, scaleX = 1.5f, scaleY = 1.5f) ``` ![Original image and scaled image](../mobile-assets/scale-example-1.png) By default, the anchor point for the image when scaling is the origin point on the top left. The scale function has two optional parameters to move the anchor point in the x and y direction. They can have values between `0.0f` and `1.0f` This scales the image to 150% of its original size. The origin anchor point is 0.5, 0.5 so the image expands from the center. ```kotlin engine.block.scale(imageBlock, scaleX = 1.5f, scaleY = 1.5f, anchorX = 0.5f, anchorY = 0.5f) ``` ![Original image placed over the scaled image, aligned on the center anchor point](../mobile-assets/scale-example-2.png) ## Scale Non-Uniformly To stretch or compress only one axis, thus distorting an image, use this combination: - The crop scale function - The width or height function How you decide to make the adjustment will have different results. Below are three examples of scaling the original image in the x direction only. ![Allowing the engine to scale the image as you adjust the width of the block](../mobile-assets/scale-example-3.png) ```kotlin import ly.img.engine.Engine import ly.img.engine.SizeMode engine.block.setWidthMode(imageBlock, mode = SizeMode.AUTO) val newWidth = engine.block.getWidth(imageBlock) * 1.5f engine.block.setWidth(imageBlock, value = newWidth) ``` The image continues respecting its fill mode (usually `.COVER`), so the content scales automatically as the frame widens. ![Using crop scale for the horizontal axis and adjusting the width of the block](../mobile-assets/scale-example-4.png) ```kotlin engine.block.setCropScaleX(imageBlock, scaleX = 1.5f) engine.block.setWidthMode(imageBlock, mode = SizeMode.AUTO) val newWidth = engine.block.getWidth(imageBlock) * 1.5f engine.block.setWidth(imageBlock, value = newWidth) ``` This uses crop scale to scale the image in a single direction and then adjusts the block's width to match the change. The change in width does not take the crop into account and so distorts the image as it's scaling the scaled image. ![Using crop scale for the horizontal axis and using the maintainCrop property when changing the width](../mobile-assets/scale-example-5.png) ```kotlin engine.block.setCropScaleX(imageBlock, scaleX = 1.5f) engine.block.setWidthMode(imageBlock, mode = SizeMode.AUTO) val newWidth = engine.block.getWidth(imageBlock) * 1.5f engine.block.setWidth(imageBlock, value = newWidth, maintainCrop = true) ``` By setting the `maintainCrop` parameter to true, expanding the width of the image by the scale factor respects the crop scale and the image is less distorted. ## Scale Images with Built-In Gestures or Gizmos The CE.SDK UI supports these interactions automatically: ### Pinch to Zoom Enabled by default: ```kotlin import ly.img.engine.Engine engine.editor.setSettingBoolean("touch/pinchAction", value = true) ``` Setting this to false disables pinch scaling entirely. For environments with keyboard and mouse a similar property exists: ```kotlin engine.editor.setSettingBoolean("mouse/enableZoom", value = true) ``` ### Gizmo Scale Handles The UI can show corner handles for drag-scaling: ```kotlin engine.editor.setSettingBoolean("controlGizmo/showScaleHandles", value = true) ``` This mirrors the behavior of native editors. > **Note:** Changing these settings affects how the CE.SDK interprets user input. It doesn't prevent you from scaling blocks programmatically with `scale()`. ## Scale Multiple Elements Together If you combine multiple blocks into a group, scaling the group scales every member: ```kotlin import ly.img.engine.Engine val groupId = engine.block.group(listOf(imageBlock, textBlock)) engine.block.scale(groupId, scaleX = 0.75f, scaleY = 0.75f) ``` This scales the entire group to 75%. ## Lock Scaling When working with templates, you can lock a block from scaling by setting its scope. The [guide on locking](https://img.ly/docs/cesdk/android/create-templates/lock-131489/) provides more information. ```kotlin import ly.img.engine.Engine engine.block.setScopeEnabled(imageBlock, key = "layer/resize", enabled = false) ``` To prevent users from applying **any** transform to a block: ```kotlin engine.block.setTransformLocked(imageBlock, locked = true) ``` ## Complete Scaling Example Here's a complete example showing different scaling operations in a single function: ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode fun scaleImageExample( license: String, userId: String ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.scale") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) // Create scene and page val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) // Create an image block val imageBlock = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(imageBlock, shape = engine.block.createShape(ShapeType.Rect)) val imageFill = engine.block.createFill(FillType.Image) engine.block.setString( block = imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg" ) engine.block.setFill(imageBlock, fill = imageFill) engine.block.setWidth(imageBlock, value = 300F) engine.block.setHeight(imageBlock, value = 300F) engine.block.appendChild(parent = page, child = imageBlock) // Example 1: Scale uniformly from top-left engine.block.scale(imageBlock, scaleX = 1.5f, scaleY = 1.5f) // Example 2: Scale uniformly from center engine.block.scale( imageBlock, scaleX = 0.75f, scaleY = 0.75f, anchorX = 0.5f, anchorY = 0.5f ) // Example 3: Non-uniform scaling with crop engine.block.setCropScaleX(imageBlock, scaleX = 1.5f) engine.block.setWidthMode(imageBlock, mode = SizeMode.AUTO) val newWidth = engine.block.getWidth(imageBlock) * 1.5f engine.block.setWidth(imageBlock, value = newWidth, maintainCrop = true) // Example 4: Lock scaling engine.block.setScopeEnabled(imageBlock, key = "layer/resize", enabled = false) engine.stop() } ``` ## Troubleshooting |Symptom|Likely Cause|Fix| |---|---|---| |"Property not found: transform/scale/x"|Using old spec property names that no longer exist.|Replace with `engine.block.scale()` for uniform scale. See [Crop](https://img.ly/docs/cesdk/android/edit-image/transform/crop-f67a47/) for more on how crop scale affects scaling results.| |Image changes size but looks oddly distorted|Combining crop and width changes in a surprising way.|Use a simpler pattern: either change width alone, or use a controlled crop/scaleX + width approach and test with sample images.| |Pinch does nothing on canvas|Pinch scaling disabled|Ensure "touch/pinchAction" is true (or not overridden in settings).| |Scale handles don't appear|Gizmo handles disabled in editor settings|Set `controlGizmo/showScaleHandles` to true.| |Image won't scale at all|Block is transform-locked or scope-locked|Check `transformLocked` and any related scopes like "layer/resize". Unlock or re-enable scope if needed.| ## Next Steps Once you're comfortable scaling images, explore the other transform tools: - [Resize](https://img.ly/docs/cesdk/android/edit-image/transform/resize-407242/) for changing the size of a block's frame. - [Crop](https://img.ly/docs/cesdk/android/edit-image/transform/crop-f67a47/) for changing what part of the image is visible. - [Rotate](https://img.ly/docs/cesdk/android/edit-image/transform/rotate-5f39c9/) for rotating images around an anchor. - [Flip](https://img.ly/docs/cesdk/android/edit-image/transform/flip-035e9f/) to mirror images horizontally or vertically. - [Move](https://img.ly/docs/cesdk/android/edit-image/transform/move-818dd9/) to reposition blocks on the canvas. Together, these guides give you a complete picture of how to position and transform images in CE.SDK on Android. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Add Captions" description: "Documentation for adding captions to videos" platform: android url: "https://img.ly/docs/cesdk/android/edit-video/add-captions-f67565/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Add Captions](https://img.ly/docs/cesdk/android/edit-video/add-captions-f67565/) --- ```kotlin file=@cesdk_android_examples/engine-guides-captions/Captions.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.MimeType import ly.img.engine.PositionMode import ly.img.engine.SizeMode fun editVideoCaptions( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1280, height = 720) val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1280F) engine.block.setHeight(page, value = 720F) engine.editor.setSettingBoolean(keypath = "features/videoCaptionsEnabled", value = true) engine.block.setDuration(page, duration = 20.0) val caption1 = engine.block.create(DesignBlockType.Caption) engine.block.setString(caption1, property = "caption/text", value = "Caption text 1") val caption2 = engine.block.create(DesignBlockType.Caption) engine.block.setString(caption2, property = "caption/text", value = "Caption text 2") val captionTrack = engine.block.create("captionTrack") engine.block.appendChild(parent = page, child = captionTrack) engine.block.appendChild(parent = captionTrack, child = caption1) engine.block.appendChild(parent = captionTrack, child = caption2) engine.block.setDuration(caption1, duration = 3.0) engine.block.setDuration(caption2, duration = 5.0) engine.block.setTimeOffset(caption1, offset = 0.0) engine.block.setTimeOffset(caption2, offset = 3.0) // Captions can also be loaded from a caption file, i.e., from SRT and VTT files. // The text and timing of the captions are read from the file. val captions = engine.block.createCaptionsFromURI("https://img.ly/static/examples/captions.srt") for (caption in captions) { engine.block.appendChild(parent = captionTrack, child = caption) } // The position and size are synced with all caption blocks in the scene so only needs to be set once. engine.block.setPositionX(caption1, 0.05F) engine.block.setPositionXMode(caption1, PositionMode.PERCENT) engine.block.setPositionY(caption1, 0.8F) engine.block.setPositionYMode(caption1, PositionMode.PERCENT) engine.block.setHeight(caption1, 0.15F) engine.block.setHeightMode(caption1, SizeMode.PERCENT) engine.block.setWidth(caption1, 0.9F) engine.block.setWidthMode(caption1, SizeMode.PERCENT) // The style is synced with all caption blocks in the scene so only needs to be set once. engine.block.setColor(caption1, property = "fill/solid/color", value = Color.fromRGBA(0.9F, 0.9F, 0F, 1F)) engine.block.setBoolean(caption1, property = "dropShadow/enabled", value = true) engine.block.setColor(caption1, property = "dropShadow/color", value = Color.fromRGBA(0F, 0F, 0F, 0.8F)) // Export page as mp4 video. val blob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { println( "Rendered ${it.renderedFrames} frames and encoded ${it.encodedFrames} frames out of ${it.totalFrames} frames", ) }, ) engine.stop() } ``` For video scenes, open captions can be added in CE.SDK. These allow to follow the content without the audio. Two blocks are available for this. The `DesignBlockType.Caption` blocks hold the text of individual captions and the `DesignBlockType.CaptionTrack` is an optional structuring block to hold the `Caption` blocks, e.g., all captions for one video. The `"playback/timeOffset"` property of each caption block controls when the caption should be shown and the `"playback/duration"` property how long the caption should be shown. Usually, the captions do not overlap. As the playback time of the page progresses, the corresponding caption is shown. With the `"caption/text"` property, the text of the caption can be set. In addition, all text properties are also available for captions, e.g., to change the font, the font size, or the alignment. Position, size, and style changes on caption blocks are automatically synced across all caption blocks. Finally, the whole page can be exported as a video file using the `block.exportVideo` function. ## Creating a Video Scene First, we create a scene that is set up for captions editing by calling the `scene.createForCaptions()` API. Then we create a page, add it to the scene and define its dimensions. This page will hold our composition. ```kotlin highlight-setupScene val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1280F) engine.block.setHeight(page, value = 720F) engine.editor.setSettingBoolean(keypath = "features/videoCaptionsEnabled", value = true) ``` ## Setting Page Durations Next, we define the duration of the page using the `fun setDuration(block: DesignBlock, duration: Double)` API to be 20 seconds long. This will be the total duration of our exported captions in the end. ```kotlin highlight-setPageDuration engine.block.setDuration(page, duration = 20.0) ``` ## Adding Captions In this example, we want to show two captions, one after the other. For this, we create two caption blocks. ```kotlin highlight-createCaptions val caption1 = engine.block.create(DesignBlockType.Caption) engine.block.setString(caption1, property = "caption/text", value = "Caption text 1") val caption2 = engine.block.create(DesignBlockType.Caption) engine.block.setString(caption2, property = "caption/text", value = "Caption text 2") ``` As an alternative to manually creating the captions, changing the text, and adjusting the timings, the captions can also be loaded from a caption file, i.e., an SRT or VTT file with the `createCaptionsFromURI` API. This return a list of caption blocks, with the parsed texts and timings. These can be added to a caption track as well. ```kotlin highlight-createCaptionsFromURI // Captions can also be loaded from a caption file, i.e., from SRT and VTT files. // The text and timing of the captions are read from the file. val captions = engine.block.createCaptionsFromURI("https://img.ly/static/examples/captions.srt") for (caption in captions) { engine.block.appendChild(parent = captionTrack, child = caption) } ``` ## Creating a Captions Track While we could add the two blocks directly to the page, we can alternatively also use the `captionTrack` block to group them. Caption tracks themselves cannot be selected directly by clicking on the canvas, nor do they have any visual representation. We create a `captionTrack` block, add it to the page and add both captions in the order in which they should play as the track's children. The dimensions of a `captionTrack` are always derived from the dimensions of its children, so you should not call the `setWidth` or `setHeight` APIs on a track, but on its children instead. ```kotlin highlight-addToTrack val captionTrack = engine.block.create("captionTrack") engine.block.appendChild(parent = page, child = captionTrack) engine.block.appendChild(parent = captionTrack, child = caption1) engine.block.appendChild(parent = captionTrack, child = caption2) ``` ## Modifying Captions By default, each caption block has a duration of 3 seconds after it is created. If we want to show it on the page for a different amount of time, we can use the `setDuration` API. ```kotlin highlight-setDuration engine.block.setDuration(caption1, duration = 3.0) engine.block.setDuration(caption2, duration = 5.0) ``` ```kotlin highlight-setTimeOffset engine.block.setTimeOffset(caption1, offset = 0.0) engine.block.setTimeOffset(caption2, offset = 3.0) ``` The position and size of the captions is automatically synced across all captions that are attached to the scene. Therefore, changes only need to be made on one of the caption blocks. ```kotlin highlight-positionAndSize // The position and size are synced with all caption blocks in the scene so only needs to be set once. engine.block.setPositionX(caption1, 0.05F) engine.block.setPositionXMode(caption1, PositionMode.PERCENT) engine.block.setPositionY(caption1, 0.8F) engine.block.setPositionYMode(caption1, PositionMode.PERCENT) engine.block.setHeight(caption1, 0.15F) engine.block.setHeightMode(caption1, SizeMode.PERCENT) engine.block.setWidth(caption1, 0.9F) engine.block.setWidthMode(caption1, SizeMode.PERCENT) ``` The styling of the captions is also automatically synced across all captions that are attached to the scene. For example, changing the text color to red on the first block, changes it on all caption blocks. ```kotlin highlight-changeStyle // The style is synced with all caption blocks in the scene so only needs to be set once. engine.block.setColor(caption1, property = "fill/solid/color", value = Color.fromRGBA(0.9F, 0.9F, 0F, 1F)) engine.block.setBoolean(caption1, property = "dropShadow/enabled", value = true) engine.block.setColor(caption1, property = "dropShadow/color", value = Color.fromRGBA(0F, 0F, 0F, 0.8F)) ``` ## Exporting Video You can start exporting the entire page as a captions file by calling `block.exportVideo`. The encoding process will run in the background. You can get notified about the progress of the encoding process by using `progressCallback` parameter. Since the encoding process runs in the background, the engine will stay interactive. So, you can continue to use the engine to manipulate the scene. Please note that these changes won't be visible in the exported captions file because the scene's state has been frozen at the start of the export. ```kotlin highlight-exportVideo // Export page as mp4 video. val blob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { println( "Rendered ${it.renderedFrames} frames and encoded ${it.encodedFrames} frames out of ${it.totalFrames} frames", ) }, ) ``` ## Full Code Here's the full code: ```kotlin file=@cesdk_android_examples/engine-guides-captions/Captions.kt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.MimeType import ly.img.engine.PositionMode import ly.img.engine.SizeMode fun editVideoCaptions( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1280, height = 720) val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1280F) engine.block.setHeight(page, value = 720F) engine.editor.setSettingBoolean(keypath = "features/videoCaptionsEnabled", value = true) engine.block.setDuration(page, duration = 20.0) val caption1 = engine.block.create(DesignBlockType.Caption) engine.block.setString(caption1, property = "caption/text", value = "Caption text 1") val caption2 = engine.block.create(DesignBlockType.Caption) engine.block.setString(caption2, property = "caption/text", value = "Caption text 2") val captionTrack = engine.block.create("captionTrack") engine.block.appendChild(parent = page, child = captionTrack) engine.block.appendChild(parent = captionTrack, child = caption1) engine.block.appendChild(parent = captionTrack, child = caption2) engine.block.setDuration(caption1, duration = 3.0) engine.block.setDuration(caption2, duration = 5.0) engine.block.setTimeOffset(caption1, offset = 0.0) engine.block.setTimeOffset(caption2, offset = 3.0) // Captions can also be loaded from a caption file, i.e., from SRT and VTT files. // The text and timing of the captions are read from the file. val captions = engine.block.createCaptionsFromURI("https://img.ly/static/examples/captions.srt") for (caption in captions) { engine.block.appendChild(parent = captionTrack, child = caption) } // The position and size are synced with all caption blocks in the scene so only needs to be set once. engine.block.setPositionX(caption1, 0.05F) engine.block.setPositionXMode(caption1, PositionMode.PERCENT) engine.block.setPositionY(caption1, 0.8F) engine.block.setPositionYMode(caption1, PositionMode.PERCENT) engine.block.setHeight(caption1, 0.15F) engine.block.setHeightMode(caption1, SizeMode.PERCENT) engine.block.setWidth(caption1, 0.9F) engine.block.setWidthMode(caption1, SizeMode.PERCENT) // The style is synced with all caption blocks in the scene so only needs to be set once. engine.block.setColor(caption1, property = "fill/solid/color", value = Color.fromRGBA(0.9F, 0.9F, 0F, 1F)) engine.block.setBoolean(caption1, property = "dropShadow/enabled", value = true) engine.block.setColor(caption1, property = "dropShadow/color", value = Color.fromRGBA(0F, 0F, 0F, 0.8F)) // Export page as mp4 video. val blob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { println( "Rendered ${it.renderedFrames} frames and encoded ${it.encodedFrames} frames out of ${it.totalFrames} frames", ) }, ) engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Annotation in Android (Kotlin)" description: "Add timed text, shapes, and highlights to videos programmatically in Android using Kotlin." platform: android url: "https://img.ly/docs/cesdk/android/edit-video/annotation-e9cbad/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Annotation](https://img.ly/docs/cesdk/android/edit-video/annotation-e9cbad/) --- Annotations are on-screen callouts: - text notes - shapes - highlights - icons that appear at precise moments in your video. With CE.SDK you can **create, update, and remove overlays programmatically** when building your Android interface. This guide shows how to add timed annotations to video scenes using Kotlin. ## What You'll Learn - Add text and shape annotations to a video scene. - Control **when** annotations appear with `timeOffset` and `duration`. - Read and set the **current playback time** to sync your UI. - Detect whether an annotation is **visible at the current time** and jump the playhead. - Build annotation UI with coroutines and Flow. ## When to Use It Use annotations for tutorials, sports analysis, education, product demos, and any workflow where viewers should notice specific moments without scrubbing manually. > **Note:** When creating scenes and pages programmatically, set `width = 1080F` and `height = 1920F` to match standard video dimensions. Otherwise, text or video clips may appear oddly sized relative to the canvas. ## Add a Text Annotation The following code: - Creates a text block. - Positions it. - Makes it visible from 5–10 seconds on the timeline. The code is the same as for any other block except for the addition of `timeOffset` and `duration` properties. ```kotlin import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.SizeMode fun addTextAnnotation(engine: Engine, page: Int): Int { val text = engine.block.create(DesignBlockType.Text) engine.block.replaceText(text, text = "Watch this part!") engine.block.setTextFontSize(text, fontSize = 32F) // Auto-size + place it visibly engine.block.setWidthMode(text, mode = SizeMode.AUTO) engine.block.setHeightMode(text, mode = SizeMode.AUTO) engine.block.setPositionX(text, value = 160F) engine.block.setPositionY(text, value = 560F) // Timeline: show between 5s and 10s engine.block.setTimeOffset(text, offset = 5.0) engine.block.setDuration(text, duration = 5.0) engine.block.appendChild(parent = page, child = text) return text } ``` Any visual block (text, shapes, stickers) can serve as an annotation. Time properties control when it's active on the page timeline. ## Add a Shape Annotation Use a graphic block with a vector shape for pointers or highlights. ```kotlin import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun addStarAnnotation(engine: Engine, page: Int): Int { val star = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(star, shape = engine.block.createShape(ShapeType.Star)) val fill = engine.block.createFill(FillType.Color) engine.block.setColor( fill, property = "fill/color/value", color = Color.fromRGBA(r = 1F, g = 0F, b = 0F, a = 1F) ) engine.block.setFill(star, fill = fill) engine.block.setPositionX(star, value = 320F) engine.block.setPositionY(star, value = 420F) engine.block.setTimeOffset(star, offset = 12.0) engine.block.setDuration(star, duration = 4.0) engine.block.appendChild(parent = page, child = star) return star } ``` ## Timeline Sync: React to Playback & Highlight Active Annotations Below is a Kotlin pattern using coroutines and Flow to keep your UI in sync with the timeline. It: 1. Retrieves the current page's playback time on an interval. 2. Marks an annotation as **active** when it's visible at that time. 3. Lets you **seek** the playhead to an annotation's start time. ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import ly.img.engine.Engine class TimelineSync( private val engine: Engine, private val page: Int ) { private val _currentTime = MutableStateFlow(0.0) val currentTime: StateFlow = _currentTime.asStateFlow() private val _activeAnnotation = MutableStateFlow(null) val activeAnnotation: StateFlow = _activeAnnotation.asStateFlow() private var pollingJob: Job? = null fun start(annotations: List, scope: CoroutineScope = CoroutineScope(Dispatchers.Main)) { pollingJob?.cancel() pollingJob = scope.launch { while (true) { // 1) Read the page's current playback time val time = engine.block.getPlaybackTime(page) _currentTime.value = time // 2) Determine which annotation is currently visible var foundActive: Int? = null for (id in annotations) { if (engine.block.isVisibleAtCurrentPlaybackTime(id)) { foundActive = id break } } _activeAnnotation.value = foundActive // Poll at ~5 fps (200ms) delay(200) } } } fun stop() { pollingJob?.cancel() pollingJob = null } fun seek(toSeconds: Double) { engine.block.setPlaybackTime(page, time = toSeconds) } } ``` > **Note:** * Use a modest polling rate, start with 5–10 Hz (100-200ms delay). It keeps UI responsive. > * For tighter coupling, combine this with the SDK's event subscriptions elsewhere in your app. **Wire it into your UI (Jetpack Compose example)**: ```kotlin import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import ly.img.engine.Engine @Composable fun AnnotationListView( sync: TimelineSync, engine: Engine, annotations: List ) { val activeAnnotation by sync.activeAnnotation.collectAsState() DisposableEffect(Unit) { sync.start(annotations) onDispose { sync.stop() } } LazyColumn { items(annotations) { id -> val isActive = (activeAnnotation == id) Row( modifier = Modifier .padding(8.dp) .clickable { // Seek to this annotation's start val start = engine.block.getTimeOffset(id) sync.seek(start) }, verticalAlignment = Alignment.CenterVertically ) { Surface( modifier = Modifier.padding(end = 8.dp), shape = CircleShape, color = if (isActive) Color.Red else Color.Gray ) { Box( modifier = Modifier.padding(4.dp) ) } Text( text = "Annotation $id", color = if (isActive) Color.Black else Color.Gray ) } } } } ``` The list: - Highlights active annotations with a red indicator - Jumps the playhead when you tap an annotation. This example doesn't include: - The main UI - The video clips - Playback controls. ## Controlling Playback (Play/Pause, Loop) You can perform actions such as: - Play/pause the page timeline - Set looping - Play solo playback for a single block when previewing. ```kotlin import ly.img.engine.Engine fun play(engine: Engine, page: Int) { engine.block.setPlaying(page, enabled = true) } fun pause(engine: Engine, page: Int) { engine.block.setPlaying(page, enabled = false) } fun setLooping(engine: Engine, blockId: Int, enabled: Boolean) { engine.block.setLooping(blockId, looping = enabled) } fun isPlaying(engine: Engine, page: Int): Boolean { return engine.block.isPlaying(page) } ``` ## Edit & Remove Annotations Following code shows the functions for: - Updating text - Moving an annotation - Deleting an annotation entirely. ```kotlin import ly.img.engine.Engine fun updateAnnotationText(engine: Engine, annotationId: Int, newText: String) { engine.block.replaceText(annotationId, text = newText) } fun moveAnnotation(engine: Engine, annotationId: Int, x: Float, y: Float) { engine.block.setPositionX(annotationId, value = x) engine.block.setPositionY(annotationId, value = y) } fun removeAnnotation(engine: Engine, annotationId: Int) { engine.block.destroy(annotationId) } ``` ## Complete Example Here's a complete example that creates a video scene with multiple annotations: ```kotlin import android.content.Context import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType import ly.img.engine.SizeMode fun createVideoWithAnnotations( context: Context, license: String, userId: String ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.annotations") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) try { // Create video scene val scene = engine.scene.createForVideo() val page = engine.block.create(DesignBlockType.Page) engine.block.appendChild(parent = scene, child = page) engine.block.setWidth(page, value = 1080F) engine.block.setHeight(page, value = 1920F) engine.block.setDuration(page, duration = 30.0) // Add video track val track = engine.block.create(DesignBlockType.Track) engine.block.appendChild(parent = page, child = track) // Add video clip val videoBlock = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(videoBlock, shape = engine.block.createShape(ShapeType.Rect)) val videoFill = engine.block.createFill(FillType.Video) engine.block.setString( block = videoFill, property = "fill/video/fileURI", value = "https://cdn.img.ly/assets/demo/v1/ly.img.video/videos/pexels-drone-footage-of-a-surfer-barrelling-a-wave-12715991.mp4" ) engine.block.setFill(videoBlock, fill = videoFill) engine.block.appendChild(parent = track, child = videoBlock) engine.block.fillParent(track) // Add annotation 1: Intro text (0-5 seconds) val introText = engine.block.create(DesignBlockType.Text) engine.block.replaceText(introText, text = "Welcome!") engine.block.setTextFontSize(introText, fontSize = 48F) engine.block.setWidthMode(introText, mode = SizeMode.AUTO) engine.block.setHeightMode(introText, mode = SizeMode.AUTO) engine.block.setPositionX(introText, value = 100F) engine.block.setPositionY(introText, value = 100F) engine.block.setTimeOffset(introText, offset = 0.0) engine.block.setDuration(introText, duration = 5.0) engine.block.appendChild(parent = page, child = introText) // Add annotation 2: Highlight shape (5-10 seconds) val star = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(star, shape = engine.block.createShape(ShapeType.Star)) val starFill = engine.block.createFill(FillType.Color) engine.block.setColor( starFill, property = "fill/color/value", color = Color.fromRGBA(r = 1F, g = 0.84F, b = 0F, a = 1F) // Gold ) engine.block.setFill(star, fill = starFill) engine.block.setPositionX(star, value = 400F) engine.block.setPositionY(star, value = 300F) engine.block.setWidth(star, value = 100F) engine.block.setHeight(star, value = 100F) engine.block.setTimeOffset(star, offset = 5.0) engine.block.setDuration(star, duration = 5.0) engine.block.appendChild(parent = page, child = star) // Add annotation 3: Detail text (10-15 seconds) val detailText = engine.block.create(DesignBlockType.Text) engine.block.replaceText(detailText, text = "Check this out! 👀") engine.block.setTextFontSize(detailText, fontSize = 32F) engine.block.setWidthMode(detailText, mode = SizeMode.AUTO) engine.block.setHeightMode(detailText, mode = SizeMode.AUTO) engine.block.setPositionX(detailText, value = 100F) engine.block.setPositionY(detailText, value = 800F) engine.block.setTimeOffset(detailText, offset = 10.0) engine.block.setDuration(detailText, duration = 5.0) engine.block.appendChild(parent = page, child = detailText) // Start playback engine.block.setPlaying(page, enabled = true) println("Video with annotations created successfully!") println("Total annotations: 3") println("Video duration: ${engine.block.getDuration(page)}s") } finally { // Note: Don't stop the engine here if you want to keep using it // engine.stop() } } ``` ## Design Tips (Quick Wins) - **Readable contrast:** Light text over dark video (or add a translucent background for the text block). - **Consistent rhythm:** Align callout durations to beats/phrases; use 2–5s for most labels. - **Safe zones:** Keep annotations away from edges (device notches, social crop areas). Pair with your existing Rules/Scopes. - **Hierarchy:** Title (bolder), detail (smaller). Reserve color for emphasis. - **Motion restraint:** Prefer fades and basic transforms over heavy effects for legibility. ## Testing & QA Checklist - **Device playback:** Verify on physical devices; long H.265 exports may differ from emulator previews. - **Performance:** Poll timeline at ~5–10 Hz for UI sync; avoid tight loops. - **Edge timing:** Test annotations starting at `0.0` and ending at page duration; confirm no off-by-one visibility. - **Layer order:** Ensure annotations render above background clips; append after media or bring to front when needed. - **Export parity:** Compare in-app preview vs `.mp4` export for small text and any blurs. ## Troubleshooting **❌ Annotation doesn't show up**: - Confirm you appended it to the **page** (or a track on the page). - Ensure its `timeOffset`/`duration` place it within the page's total duration. - If hidden behind media, append it **after** the background or bring to front using layer management. **❌ Jumps don't seem to work**: - Seek on the **page** block with `setPlaybackTime(page, time)`, not on the annotation itself. - Verify the page supports playback time with `engine.block.supportsPlaybackTime(page)`. **❌ Performance stutters**: - Poll the timeline at 5–10 Hz (100-200ms delay). Avoid tight loops. - Use coroutines with proper dispatchers (Dispatchers.Main for UI updates). **❌ Exported video looks different**: - Make sure the scene mode is **Video** and the page duration property has the correct value. - Long blurs/glows may differ depending on codec settings. ## Next Steps Now that you've explored annotation basics, these topics can deepen your understanding: - [Add Captions & Subtitles](https://img.ly/docs/cesdk/android/edit-video/add-captions-f67565/) to your clips. - [Variables for Dynamic Labels](https://img.ly/docs/cesdk/android/create-templates/add-dynamic-content/text-variables-7ecb50/) for displaying information like usernames or scores. - [Control Audio and Video](https://img.ly/docs/cesdk/android/create-video/control-daba54/) for advanced playback control. - [Timeline Editor](https://img.ly/docs/cesdk/android/create-video/timeline-editor-912252/) for creating multi-track video compositions. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Transform" description: "Documentation for Transform" platform: android url: "https://img.ly/docs/cesdk/android/edit-video/transform-369f28/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Transform](https://img.ly/docs/cesdk/android/edit-video/transform-369f28/) --- --- ## Related Pages - [Move](https://img.ly/docs/cesdk/android/edit-video/transform/move-aa9d89/) - Position a video relative to its parent using either percentage or units - [Crop Video in Android](https://img.ly/docs/cesdk/android/edit-video/transform/crop-8b1741/) - Cut out specific areas of a video to focus on key content or change aspect ratio - [Rotate](https://img.ly/docs/cesdk/android/edit-video/transform/rotate-eaf662/) - Documentation for Rotate - [Resize](https://img.ly/docs/cesdk/android/edit-video/transform/resize-b1ce14/) - Change the size of individual elements or groups. - [Scale](https://img.ly/docs/cesdk/android/edit-video/transform/scale-f75c8a/) - Scale videos uniformly in your Android app. - [Flip Videos](https://img.ly/docs/cesdk/android/edit-video/transform/flip-a603b0/) - Flip videos horizontally or vertically. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Crop Video in Android" description: "Cut out specific areas of a video to focus on key content or change aspect ratio" platform: android url: "https://img.ly/docs/cesdk/android/edit-video/transform/crop-8b1741/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Transform](https://img.ly/docs/cesdk/android/edit-video/transform-369f28/) > [Crop](https://img.ly/docs/cesdk/android/edit-video/transform/crop-8b1741/) --- Video cropping is essential for Android video editing apps, enabling users to focus on important content and optimize videos for different platforms. The CreativeEditor SDK provides both intuitive crop interfaces and powerful Kotlin APIs for video manipulation. Whether you're targeting TikTok, Instagram, or YouTube formats, this guide covers all video cropping scenarios for your Android application. ## Interactive video crop tools The SDK includes specialized video crop controls designed for Android touch interfaces. These components handle real-time video preview, aspect ratio selection, and provide smooth crop adjustments that maintain video quality during editing. ![Crop tools appear when the crop button is tapped in the editor](../mobile-assets/crop-tool.png) ### User interaction workflow 1. **Select the video** you want to crop. 2. **Tap the crop icon** in the editor toolbar. 3. **Adjust the crop area** by dragging the corners or edges or using two-finger gestures. 4. **Use the tools** to modify the crop flip, rotation and angle or to reset the crop. 5. **Close the Sheet** to finalize the crop. The cropped video appears in your project, but the underlying original video and crop values are preserved even when you rotate or resize the cropped video. ### Enable and configure crop tool The default UI allows cropping. When you are creating your own UI or custom toolbars you can configure editing behavior. To ensure the crop tool is available in the UI, make sure it's included in your dock configuration or quick actions. ```kotlin engine.editor.setSettingBoolean("doubleClickToCropEnabled", true) engine.editor.setSettingBoolean("controlGizmo/showCropHandles", true) engine.editor.setSettingBoolean("controlGizmo/showCropScaleHandles", true) ``` The cropping handles are only available when a selected block has a fill of type `FillType.Video`. Otherwise setting the edit mode of the `engine.editor` to crop has no effect. ## Programmatic Cropping Programmatic cropping gives you complete control over video block boundaries, dimensions, and integration with other transformations like rotation or flipping. This is useful for automation, predefined layouts, or server-synced workflows. When you initially create a fill to insert a video into a block, the engine centers the video in the block and crops any dimension that doesn't match. For example: when a block with dimensions of 400.0 × 400.0 is filled with a video that is 600.0 × 500.0, there will be horizontal cropping. When working with cropping using code, it's important to remember that you are modifying the scale, translation, rotation, etc. of the underlying video. The examples below always adjust the x and y values equally. This is not required, but adjusting them unequally can distort the video, which might be just what you want. ### Reset Crop When a video is initially placed into a block it will get crop scale and crop translation values. Resetting the crop will return the video to the original values. ![Video with no additional crop applied shown in crop mode](../mobile-assets/crop-example-1.png) This is a block (called `videoBlock` in the example code) with dimensions of 400 × 400 filled with a video that has dimensions of 720 × 1280. The video has slight scaling and translation applied so that it fills the block evenly. At any time, the code can execute the reset crop command to return it to this stage. ```kotlin engine.block.resetCrop(videoBlock) ``` ### Crop Translation The translation values adjust the placement of the origin point of a video. You can read and change the values. They are not pixel units or centimeters, they are scaled percentages. A video that has its origin point at the origin point of the crop block will have translation value of 0.0 for x and y. ![Video crop translated one quarter of its width to the right](../mobile-assets/crop-example-5.png) ```kotlin engine.block.setCropTranslationX(videoBlock, 0.25f) ``` This video has had its translation in the x direction set to 0.25. That moved the video one quarter of its width to the right. Setting the value to -0.25 would change the offset of the origin to the left. These are absolute values. Setting the x value to 0.25 and then setting it to -0.25 does not move the video to an offset of 0.0. There is a `setCropTranslationY(block: DesignBlock, translationY: Float)` function to adjust the translation of the video in the vertical direction. Negative values move the video up and positive values move the video down. To read the current crop translation values you can use the convenience getters for the x and y values. ```kotlin val currentX = engine.block.getCropTranslationX(videoBlock) val currentY = engine.block.getCropTranslationY(videoBlock) ``` ### Crop scale The scale values adjust the height and width of the underlying video. Values larger than 1.0 will make the video larger while values less than 1.0 make the video smaller. Unless the video also has offsetting translation applied, the center of the video will move. ![Video crop scaled by 1.5 with no corresponding translation adjustment](../mobile-assets/crop-example-6.png) This video has been scaled by 1.5 in the x and y directions, but the origin point has not been translated. So, the center of the video has moved. ```kotlin engine.block.setCropScaleX(videoBlock, 1.5f) engine.block.setCropScaleY(videoBlock, 1.5f) ``` To read the current crop scale values, use the convenience getters for the x and y values. ```kotlin val currentX = engine.block.getCropScaleX(videoBlock) val currentY = engine.block.getCropScaleY(videoBlock) ``` ### Crop rotate The same as when rotating blocks, the crop rotation function uses radians. Positive values rotate clockwise and negative values rotate counter clockwise. The video rotates around its center. ![Video crop rotated by pi/4 or 45 degrees](../mobile-assets/crop-example-7.png) ```kotlin import kotlin.math.PI engine.block.setCropRotation(videoBlock, (PI / 4.0).toFloat()) ``` For working with radians, Kotlin has a constant defined for pi. It can be used as `PI` from `kotlin.math.PI`. Because the `setCropRotation` function takes a `Float` for the rotation value, you can use `.toFloat()` to convert the Double to Float. ### Crop to scale ratio To center crop a video, you can use the scale ratio. This will adjust the x and y scales of the video evenly, and adjust the translation to keep it centered. ![Video cropped using the scale ratio to remain centered](../mobile-assets/crop-example-2.png) This video has been scaled by 2.0 in the x and y directions. Its translation has been adjusted automatically by -0.5 in the x and y directions to keep the video centered. ```kotlin engine.block.setCropScaleRatio(videoBlock, 2.0f) ``` Using the crop scale ratio function is the same as calling the translation and scale functions, but in one line. ```kotlin engine.block.setCropScaleX(videoBlock, 2.0f) engine.block.setCropScaleY(videoBlock, 2.0f) engine.block.setCropTranslationX(videoBlock, -0.5f) engine.block.setCropTranslationY(videoBlock, -0.5f) ``` ### Chained crops Crop operations can be chained together. The order of the chaining impacts the final video. ![Video cropped and rotated](../mobile-assets/crop-example-3.png) ```kotlin import kotlin.math.PI engine.block.setCropScaleRatio(videoBlock, 2.0f) engine.block.setCropRotation(videoBlock, (PI / 3.0).toFloat()) ``` ![Video rotated first and then scaled](../mobile-assets/crop-example-4.png) ```kotlin import kotlin.math.PI engine.block.setCropRotation(videoBlock, (PI / 3.0).toFloat()) engine.block.setCropScaleRatio(videoBlock, 2.0f) ``` ### Flipping the crop There are two functions for crop flipping the video. One for horizontal and one for vertical. They each flip the video along its center. ![Image crop flipped vertically](../mobile-assets/crop-example-8.png) ```kotlin engine.block.flipCropVertical(videoBlock) engine.block.flipCropHorizontal(videoBlock) ``` The video will be crop flipped every time the function gets called. So calling the function an even number of times will return the video to its original orientation. ### Filling the frame When the various crop operations cause the background of the crop block to be displayed, such as in the **Crop Translation** example above, the function ```kotlin engine.block.adjustCropToFillFrame(videoBlock, minScaleRatio = 1.0f) ``` will adjust the translation values and the scale values of the video so that the entire crop block is filled. This is not the same as resetting the crop. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Flip Videos" description: "Flip videos horizontally or vertically to create mirror effects and symmetrical designs." platform: android url: "https://img.ly/docs/cesdk/android/edit-video/transform/flip-a603b0/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Transform](https://img.ly/docs/cesdk/android/edit-video/transform-369f28/) > [Flip](https://img.ly/docs/cesdk/android/edit-video/transform/flip-a603b0/) --- Video flipping in CreativeEditor SDK (CE.SDK) allows you to mirror video content horizontally or vertically. This transformation is useful for creating symmetrical designs, correcting orientation issues, or achieving specific visual effects in your video projects. You can flip videos both through the built-in user interface and programmatically using the SDK's APIs, providing flexibility for different workflow requirements. [Launch Web Demo](https://img.ly/showcases/cesdk) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) ## Available Flip Operations CE.SDK supports two types of video flipping: - **Horizontal Flip**: Mirror the video along its vertical axis, creating a left-right reflection - **Vertical Flip**: Mirror the video along its horizontal axis, creating a top-bottom reflection These operations can be applied individually or combined to achieve the desired visual effect. ## Applying Flips ### UI-Based Flipping You can apply flips directly in the CE.SDK user interface. The editor provides intuitive controls for horizontally and vertically flipping videos, making it easy for users to quickly mirror content without writing code. ### Programmatic Flipping Developers can also apply flips programmatically, using the SDK's API. This allows for dynamic video adjustments based on application logic, user input, or automated processes. ## Combining with Other Transforms Video flipping works seamlessly with other transformation operations like rotation, scaling, and cropping. You can chain multiple transformations to create complex visual effects while maintaining video quality. ## Guides --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Move" description: "Position a video relative to its parent using either percentage or units" platform: android url: "https://img.ly/docs/cesdk/android/edit-video/transform/move-aa9d89/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Transform](https://img.ly/docs/cesdk/android/edit-video/transform-369f28/) > [Move](https://img.ly/docs/cesdk/android/edit-video/transform/move-aa9d89/) --- Video positioning is crucial for creating professional video compositions in Android apps. The CreativeEditor SDK enables precise video placement through both touch-based dragging and coordinate-based Kotlin APIs. Perfect for building video collages, picture-in-picture effects, or complex multi-layer video compositions. ## Video positioning features - Touch-based video dragging with smooth gestures - Coordinate-based positioning through Kotlin code - Multi-video positioning with relationship preservation - Position constraints for maintaining video layouts ## Video positioning scenarios Apply video movement for: - Creating picture-in-picture video layouts - Building video collages and multi-layer compositions - Implementing drag-and-drop video editing interfaces *** ## Move videos with the UI Users can drag and drop elements directly in the editor canvas. *** ## Move a video block programmatically Video block position is controlled using the `position/x` and `position/y` properties. They can either use absolute or percentage (relative) values. In addition to setting the properties, there are helper functions. ```kotlin engine.block.setFloat(videoBlock, "position/x", 150f) engine.block.setFloat(videoBlock, "position/y", 100f) ``` or ```kotlin engine.block.setPositionX(videoBlock, 150f) engine.block.setPositionY(videoBlock, 100f) ``` The preceding code moves the video to coordinates (150, 100) on the canvas. The origin point (0, 0) is at the top-left. ```kotlin import ly.img.engine.PositionMode engine.block.setPositionXMode(videoBlock, PositionMode.PERCENT) engine.block.setPositionYMode(videoBlock, PositionMode.PERCENT) engine.block.setPositionX(videoBlock, 0.5f) engine.block.setPositionY(videoBlock, 0.5f) ``` The preceding code moves the video to the center of the canvas, regardless of the dimensions of the canvas. As with setting position, you can update or check the mode using `position/x/mode` and `position/y/mode` properties. ```kotlin val xPosition = engine.block.getPositionX(videoBlock) val yPosition = engine.block.getPositionY(videoBlock) ``` *** ## Move multiple elements together Group elements before moving to keep them aligned: ```kotlin val groupId = engine.block.group(listOf(videoBlock, textBlock)) engine.block.setPositionX(groupId, 200f) ``` The preceding code moves the entire group to 200 from the left edge. *** ## Move relative to current position To nudge a video instead of setting an absolute position: ```kotlin val xPosition = engine.block.getPositionX(videoBlock) engine.block.setPositionX(videoBlock, xPosition + 20f) ``` The preceding code moves the video 20 points to the right. *** ## Lock movement (optional) When building templates, you might want to lock movement to protect the layout: ```kotlin engine.block.setScopeEnabled(videoBlock, "layer/move", false) ``` You can also disable all transformations for a block by locking, this is regardless of working with a template. ```kotlin engine.block.setTransformLocked(videoBlock, true) ``` *** ## Troubleshooting | Issue | Solution | | ------------------------ | ----------------------------------------------------- | | Video block not moving | Ensure it is not constrained or locked | | Unexpected position | Check canvas coordinates and alignment settings | | Grouped items misaligned | Confirm all items share the same reference point | | Can't move via UI | Ensure the move feature is enabled in the UI settings | *** --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Resize Videos" description: "Change the dimensions of video elements to fit specific layout requirements." platform: android url: "https://img.ly/docs/cesdk/android/edit-video/transform/resize-b1ce14/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Transform](https://img.ly/docs/cesdk/android/edit-video/transform-369f28/) > [Resize](https://img.ly/docs/cesdk/android/edit-video/transform/resize-b1ce14/) --- Video resizing in CreativeEditor SDK (CE.SDK) allows you to change the dimensions of video elements to match specific layout requirements. Unlike scaling, resizing allows independent control of width and height dimensions, making it ideal for fitting videos into predefined spaces or responsive layouts. You can resize videos both through the built-in user interface and programmatically using the SDK's APIs, providing flexibility for different workflow requirements. [Launch Web Demo](https://img.ly/showcases/cesdk) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) ## Resize Methods CE.SDK supports several approaches to video resizing: - **Absolute Dimensions**: Set specific pixel dimensions for precise control - **Percentage-based Resizing**: Size relative to parent container for responsive designs - **UI Resize Handles**: Interactive resize controls in the editor interface - **Aspect Ratio Constraints**: Maintain or ignore aspect ratios during resize operations ## Applying Resizing ### UI-Based Resizing You can resize videos directly in the CE.SDK user interface using resize handles. Users can drag edge and corner handles to adjust dimensions independently or proportionally, making it easy to fit videos into specific layouts visually. ### Programmatic Resizing Developers can apply resizing programmatically, using the SDK's API. This allows for precise dimension control, automated layout adjustments, and integration with responsive design systems or template constraints. ## Combining with Other Transforms Video resizing works seamlessly with other transformation operations like rotation, cropping, and positioning. You can chain multiple transformations to create complex layouts while maintaining video quality and performance. ## Guides --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Rotate" description: "Documentation for Rotate" platform: android url: "https://img.ly/docs/cesdk/android/edit-video/transform/rotate-eaf662/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Transform](https://img.ly/docs/cesdk/android/edit-video/transform-369f28/) > [Rotate](https://img.ly/docs/cesdk/android/edit-video/transform/rotate-eaf662/) --- Video rotation is critical for Android video apps, especially when dealing with content from mobile cameras that may be recorded in different orientations. The CreativeEditor SDK provides smooth video rotation with both touch controls and precise Kotlin APIs, ensuring your users can easily correct orientation and create dynamic video compositions. ## Video rotation features - Smooth video rotation with real-time preview - Orientation correction for mobile-recorded content - Precise angle control through Kotlin programming - Multi-video rotation for synchronized editing ### Rotating a Video Using the UI By default, selecting a block will show handles for resizing and rotating. You can freeform rotate a block by dragging the rotation handle. ![Video rotation handle showing video can be rotated](../mobile-assets/rotate-example-1.png) ### Rotating a Video Using Code You can rotate a video block using the `setRotation` function. It takes the `id` of the block and a rotation amount in radians. ```kotlin import kotlin.math.PI engine.block.setRotation(videoBlock, (PI / 4).toFloat()) ``` If you need to convert between radians and degrees, multiply the number in degrees by pi and divide by 180. ```kotlin val angleInRadians: Float = (angleInDegrees * PI / 180).toFloat() val angleInDegrees: Float = (angleInRadians * 180 / PI).toFloat() ``` You can discover the current rotation of a block using the `getRotation` function. ```kotlin val rotationOfVideo = engine.block.getRotation(videoBlock) ``` ![Video rotated by 45 degrees](../mobile-assets/rotate-example-2.png) > **Note:** This rotates the entire block. If you want to rotate a video that is filling > a block but not the block, explore the crop rotate function. ### Locking Rotation You can remove the rotation handle from the UI by changing the setting for the engine. This will affect *all* blocks. ```kotlin engine.editor.setSettingBoolean("controlGizmo/showRotateHandles", false) ``` Though the handle is gone, the user can still use the two finger rotation gesture on a touch device. You can disable that gesture with the following setting. ```kotlin engine.editor.setSettingBoolean("touch/rotateAction", false) ``` When you want to lock only certain blocks, you can toggle the transform lock property. This will apply to all transformations for the block. ```kotlin engine.block.setTransformLocked(videoBlock, true) ``` ### Rotating As a Group To rotate multiple elements together, first add them to a `group` and then rotate the group. ```kotlin import kotlin.math.PI val groupId = engine.block.group(listOf(videoBlock, textBlock)) engine.block.setRotation(groupId, (PI / 2).toFloat()) ``` ### Troubleshooting Troubleshooting | Issue | Solution | | ----------------------------------- | ------------------------------------------------------------------------------- | | Video appears offset after rotation | Make sure the pivot point is centered (default is center). | | Rotation not applying | Confirm that the video block is inserted and rendered before applying rotation. | | Rotation handle not visible | Check that interactive UI controls are enabled in the settings. | --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Scale" description: "Scale videos uniformly in your Android app." platform: android url: "https://img.ly/docs/cesdk/android/edit-video/transform/scale-f75c8a/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Create and Edit Videos](https://img.ly/docs/cesdk/android/create-video-c41a08/) > [Transform](https://img.ly/docs/cesdk/android/edit-video/transform-369f28/) > [Scale](https://img.ly/docs/cesdk/android/edit-video/transform/scale-f75c8a/) --- This guide shows how to scale videos using CE.SDK in your Android app. You'll learn how to scale video blocks proportionally, scale groups, and apply scaling constraints to protect template structure. ## What you'll learn - Scale videos programmatically using Kotlin - Scale proportionally or non-uniformly - Scale grouped elements - Apply scale constraints in templates ## When to use Use scaling to: - Emphasize or de-emphasize elements - Fit videos to available space without cropping - Enable pinch-to-zoom gestures or dynamic layouts *** ## Scale a video uniformly Scaling uses the `scale(block: DesignBlock, scaleX: Float, scaleY: Float, anchorX: Float = 0f, anchorY: Float = 0f)` function. A scale value of `1.0f` is the original scale. Values larger than `1.0f` increase the scale of the block and values lower than `1.0f` scale the block smaller. A value of `2.0f`, for example makes the block twice as large. The following code scales the video to 150% of its original size. The origin anchor point remains unchanged, so the video expands down and to the right: ```kotlin engine.block.scale(videoBlock, 1.5f, 1.5f) ``` ![Original video and scaled video](../mobile-assets/scale-example-1.png) By default, the anchor point for the video when scaling is the origin point on the top left. The scale function has optional parameters to move the anchor point in the x and y direction. They can have values between `0.0f` and `1.0f` The following code scales the video to 150% of its original size. The origin anchor point is 0.5, 0.5, so the video expands from the center: ```kotlin engine.block.scale(videoBlock, 1.5f, 1.5f, 0.5f, 0.5f) ``` ![Original video placed over the scaled video, aligned on the center anchor point](../mobile-assets/scale-example-2.png) *** ## Scale non-uniformly To stretch or compress only one axis, thus distorting a video, use the crop scale function in combination with the width or height function. How you decide to make the adjustment will have different results. Below are three examples of scaling the original video in the x direction only. ![Allowing the engine to scale the video as you adjust the width of the block](../mobile-assets/scale-example-3.png) ```kotlin import ly.img.engine.SizeMode engine.block.setWidthMode(videoBlock, SizeMode.AUTO) val newWidth = engine.block.getWidth(videoBlock) * 1.5f engine.block.setWidth(videoBlock, newWidth) ``` The preceding code adjusts the width of the block and allows the engine to adjust the scale of the video to maintain it as a fill. ![Using crop scale for the horizontal axis and adjusting the width of the block](../mobile-assets/scale-example-4.png) ```kotlin engine.block.setCropScaleX(videoBlock, 1.5f) engine.block.setWidthMode(videoBlock, SizeMode.AUTO) val newWidth = engine.block.getWidth(videoBlock) * 1.5f engine.block.setWidth(videoBlock, newWidth) ``` The preceding code uses crop scale to scale the video in a single direction and then adjusts the block's width to match the change. The change in width does not take the crop into account and so distorts the video as it's scaling the scaled video. ![Using crop scale for the horizontal axis and using the maintainCrop property when changing the width](../mobile-assets/scale-example-5.png) ```kotlin engine.block.setCropScaleX(videoBlock, 1.5f) engine.block.setWidthMode(videoBlock, SizeMode.AUTO) val newWidth = engine.block.getWidth(videoBlock) * 1.5f engine.block.setWidth(videoBlock, newWidth, true) // maintainCrop = true ``` By setting the `maintainCrop` parameter to true, expanding the width of the video by the scale factor respects the crop scale and the video is less distorted. *** ## Scale multiple elements together Group blocks to scale them proportionally: ```kotlin val groupId = engine.block.group(listOf(videoBlock, textBlock)) engine.block.scale(groupId, 0.75f, 0.75f) ``` The preceding code scales the entire group to 75%. *** ## Lock scaling When working with templates, you can lock a block from scaling by setting its scope. Remember that the global layer has to defer to the blocks using `setGlobalScope`. ```kotlin engine.block.setScopeEnabled(videoBlock, "layer/resize", false) ``` To prevent users from transforming an element at all: ```kotlin engine.block.setTransformLocked(videoBlock, true) ``` *** --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Engine Interface" description: "Understand CE.SDK's architecture and learn when to use direct Engine access for automation workflows" platform: android url: "https://img.ly/docs/cesdk/android/engine-interface-6fb7cf/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Engine](https://img.ly/docs/cesdk/android/engine-interface-6fb7cf/) --- The Creative Engine is the powerhouse behind CE.SDK's cross-platform capabilities. While the UI components provide ready-to-use editing experiences, the Engine interface gives you direct programmatic control over all creative operations—from simple batch processing to complex automated workflows. ## Client-Side vs Server-Side Processing Understanding when to use client-side versus server-side processing is crucial for building efficient creative automation workflows. Each approach offers distinct advantages depending on your use case requirements. ### Client-Side Processing (Mobile Device) Client-side processing runs the Engine directly in the user's device — but importantly, this doesn't mean visible to the user. The Engine operates headlessly in the background, making it perfect for automation tasks that enhance user experience without interrupting their workflow. **Common Implementation Patterns:** **Hidden Engine Instances**: Run a second, invisible Engine instance alongside your main UI for background processing. While users edit in the primary interface, the hidden instance can validate designs, generate previews, or prepare export-ready assets. **Underlying Engine Access**: Access the Engine API directly from prebuilt UI components for custom automation within existing workflows. **Dedicated Engine Packages**: Use platform-specific Engine packages for specialized client-side automation without any UI overhead. **Ideal Client-Side Use Cases:** - **Design Validation**: Check for empty placeholders, low-resolution images, or brand guideline violations in real-time - **Thumbnail Generation**: Create preview images for design galleries or version history - **Effect Previews**: Generate quick previews of filters or effects before applying them to the main design - **Auto-Save Optimization**: Compress and optimize scenes for storage while maintaining editability - **Real-Time Feedback**: Provide instant visual feedback for design rules or constraints ### Server-Side Processing Server-side processing moves the Engine to your backend infrastructure, unlocking powerful capabilities for resource-intensive operations and scalable workflows. **Key Advantages:** - **Enhanced Resources**: Access to more CPU, memory, and storage than client devices - **Secure Asset Access**: Process private assets without exposing them to client-side code - **Background Operations**: Handle long-running tasks without affecting user experience - **Scheduled Automation**: Trigger design generation based on events, schedules, or external APIs **Ideal Server-Side Use Cases:** - **High-Resolution Exports**: Generate print-quality assets that would be too resource-intensive for client devices - **Bulk Generation**: Create thousands of design variations for marketing campaigns or product catalogs - **Data Pipeline Integration**: Connect to databases, APIs, or file systems for automated content generation - **Multi-Format Output**: Export designs in multiple formats and resolutions simultaneously - **Workflow Orchestration**: Coordinate complex multi-step automation processes **Hybrid Workflows**: Often, the most effective approach combines both client and server-side processing. Users can design and preview on the client with instant feedback, while heavy processing happens on the server in the background. ## Engine-Powered Use Cases The Engine interface unlocks [powerful automation scenarios](https://img.ly/docs/cesdk/android/automation/overview-34d971/) that can scale creative workflows: ### Batch Processing Process multiple designs simultaneously with consistent results. Whether you're applying filters to hundreds of images or generating variations of a marketing template, the Engine handles bulk operations efficiently both client-side and server-side. ### Auto-Resize Automatically adapt designs to different aspect ratios and platforms. The Engine intelligently repositions elements, adjusts text sizes, and maintains visual hierarchy across formats—from Instagram stories to LinkedIn posts. ### Data Merge Connect external data sources (CSV, JSON, APIs) to templates for personalized content generation. Perfect for creating thousands of product cards, personalized certificates, or location-specific campaigns. ### Product Variations Generate multiple versions of product designs with different colors, sizes, or configurations. Ideal for e-commerce platforms needing to showcase product options without manual design work. ### Design Generation Create entirely new designs programmatically based on rules, templates, or AI inputs. The Engine can compose layouts, select appropriate fonts, and arrange elements according to your design guidelines. ### Multiple Image Generation Efficiently process and export designs in various formats and resolutions. Generate web-optimized previews alongside print-ready high-resolution files in a single workflow. ### Actions Implement complex multi-step operations as reusable actions. Chain together filters, transformations, and exports to create sophisticated automated workflows that can be triggered programmatically. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create Thumbnail in Android (Kotlin)" description: "Generate small preview images for scenes and pages using CE.SDK export options in Kotlin." platform: android url: "https://img.ly/docs/cesdk/android/export-save-publish/create-thumbnail-749be1/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Export Media Assets](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) > [Create Thumbnail](https://img.ly/docs/cesdk/android/export-save-publish/create-thumbnail-749be1/) --- Thumbnails are scaled down previews of your designs. They let you show galleries or document picker previews without loading the full editor. CE.SDK generates thumbnails using the same API you use for final output, just with smaller target dimensions and often lower quality settings. This guide focuses on **image thumbnails**: small PNG, JPEG or WebP previews for use in grids, lists and document icons. It **doesn't cover audio waveforms** or arrays of **preview frames** for scrubbers. ## What You'll Learn - How to export a scene or page as a small preview image. - How to control thumbnail dimensions while preserving aspect ratio. - When to choose PNG, JPEG, or WebP for thumbnails. - How to tune quality and file size using `ExportOptions`. - How to batch-generate different thumbnail sizes safely. ## When You'll Use It - Showing a "My Designs" or "Recent Files" gallery. - Rendering previews for templates or drafts. - Generating document icons or share sheet previews. - Creating thumbnail sizes for different UI contexts. ## How Thumbnail Export Works In CE.SDK, `export` means *rendering bitmap image data from the engine*. When you call `export`, the SDK: 1. Renders the current visual state of a block (for example, a page). 2. Composites all **visible** child blocks (images, text, shapes, effects). 3. Produces raw bitmap image data in the requested format (PNG, JPEG, or WebP). Exporting **doesn't** imply writing a file to disk. The result of the export call is an in-memory `ByteBuffer` that you can: - Convert to a `Bitmap`. - Cache in memory. - Write to disk if needed. - Upload elsewhere. CE.SDK doesn't provide a separate "thumbnail API". If you build your own UI, you call `engine.block.export(...)` directly whenever you want. If you use the **prebuilt editor UI** (the default CE.SDK editors), there *is* a convenient hook for the built-in Export/Share button: the editor exposes an `DesignEditor` configuration. The default export: 1. Renders (PDF for design scenes, MP4 for video scenes). 2. Writes the result to a temporary file 3. Opens the system share sheet. That hook is great for customizing what happens when the user taps Export in the prebuilt UI, but under the hood it still uses the same engine export APIs that you use for thumbnails. ## Export a Scene Thumbnail You generate thumbnails by exporting a design block. In most cases this should be either: - The **page block**, which represents the full visible canvas. - The scene, if your design is single-page. Exporting the page block is the safest choice when you want a thumbnail that matches what the user sees on screen. ```kotlin import android.content.Context import android.net.Uri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import java.io.File suspend fun exportSceneThumbnail( engine: Engine, context: Context, scene: Int ): File { // Define thumbnail size val options = ExportOptions( targetWidth = 400, targetHeight = 300, jpegQuality = 0.8f ) // Export the scene as JPEG val blob = engine.block.export( block = scene, mimeType = MimeType.JPEG, options = options ) // Save to temporary file val thumbnailFile = File(context.cacheDir, "thumbnail_${System.currentTimeMillis()}.jpg") withContext(Dispatchers.IO) { thumbnailFile.outputStream().channel.use { channel -> channel.write(blob) } } return thumbnailFile } ``` The preceding code exports a scene at 400×300 resolution with JPEG quality set to 0.8 for efficient file size. ## Control Thumbnail Size and Aspect Ratio ### How `targetWidth` and `targetHeight` behave When both `targetWidth` and `targetHeight` have values, CE.SDK renders the block large enough to **fill the target box while maintaining its aspect ratio**. Important implications: - You don't need to calculate aspect-fit or aspect-fill yourself. - The exported image may exceed one of the target dimensions internally to preserve aspect ratio. - Consider `targetWidth` and `targetHeight` as a *desired bounding box*, not a hard crop. ### Typical Thumbnail Sizes Common choices include: - 150 × 150 for dense grids - 161 × 161 for Instagram Video Feeds - 55 × 55 or 222 × 150 for Pinterest - 400 × 300 for list previews - 800 × 600 for high-quality previews ## Choose the Right Thumbnail Format CE.SDK supports PNG, JPEG, and WebP for image export. It provides a `MimeType` enum including `JPEG`, `PNG` and `WEBP`. ### PNG - Preserves transparency - Lossless quality - Compression affects speed, not quality - Best for stickers, cutouts, or UI elements ### JPEG - Smaller and faster for photographic content - No transparency - Control quality via `jpegQuality` ### WebP - Efficient compression - Supports lossless and lossy modes - Requires WebP support everywhere you display thumbnails Switching formats only requires changing the `mimeType` and relevant quality option. ```kotlin import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import java.io.File suspend fun exportJPEGThumbnail( engine: Engine, context: Context, page: Int ): File { val options = ExportOptions( jpegQuality = 0.8f, targetWidth = 400, targetHeight = 300 ) val blob = engine.block.export( block = page, mimeType = MimeType.JPEG, options = options ) val file = File(context.cacheDir, "thumbnail.jpg") withContext(Dispatchers.IO) { file.outputStream().channel.use { channel -> channel.write(blob) } } return file } ``` When you need **different thumbnails of different sizes or image formats**, call `export` for each permutation. Pass in the correct mime type and an `ExportOptions` configuration. ```kotlin import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import java.io.File suspend fun exportMultipleThumbnails( engine: Engine, context: Context, page: Int ): Map { val results = mutableMapOf() // Small thumbnail (22x22) val smallOptions = ExportOptions( jpegQuality = 0.8f, targetWidth = 22, targetHeight = 22 ) val smallBlob = engine.block.export(page, MimeType.JPEG, smallOptions) val smallFile = File(context.cacheDir, "thumbnail_small.jpg") withContext(Dispatchers.IO) { smallFile.outputStream().channel.use { it.write(smallBlob) } } results["small"] = smallFile // Medium thumbnail (150x150) val mediumOptions = ExportOptions( jpegQuality = 0.8f, targetWidth = 150, targetHeight = 150 ) val mediumBlob = engine.block.export(page, MimeType.JPEG, mediumOptions) val mediumFile = File(context.cacheDir, "thumbnail_medium.jpg") withContext(Dispatchers.IO) { mediumFile.outputStream().channel.use { it.write(mediumBlob) } } results["medium"] = mediumFile return results } ``` > **Note:** ## Caching ThumbnailsThumbnail export is expensive compared to image display.Even a basic in-memory cache (for example, `LruCache`) can dramatically improve scrolling performance in galleries and `RecyclerView` lists. ## Tune Quality and File Size with `ExportOptions` `ExportOptions` lets you balance visual quality, file size, and export speed. Key fields for thumbnails: - `pngCompressionLevel` (0–9, default 5) - `jpegQuality` (0.0–1.0, default 0.9) - `webpQuality` (0.0–1.0, default 1.0) - `targetWidth` / `targetHeight` CE.SDK applies only the options relevant to the chosen MIME type. Others are ignored. ```kotlin import ly.img.engine.ExportOptions import ly.img.engine.MimeType // PNG with maximum compression (slower, smaller file) val pngOptions = ExportOptions( pngCompressionLevel = 9, targetWidth = 400, targetHeight = 300 ) val pngBlob = engine.block.export(page, MimeType.PNG, pngOptions) // JPEG with lower quality (faster, much smaller file) val jpegOptions = ExportOptions( jpegQuality = 0.6f, targetWidth = 400, targetHeight = 300 ) val jpegBlob = engine.block.export(page, MimeType.JPEG, jpegOptions) // WebP with lossless mode val webpOptions = ExportOptions( webpQuality = 1.0f, targetWidth = 400, targetHeight = 300 ) val webpBlob = engine.block.export(page, MimeType.WEBP, webpOptions) ``` ## Headless and Background Thumbnail Generation CE.SDK offers two common ways to export without blocking your UI: ### Use Your Existing Engine For occasional thumbnail creation (for example, when a user saves a draft), it's often fine to export from the same `Engine` instance that powers the editor. ### Use a Separate Headless Engine Instance For batch thumbnail generation (for example, populating a large gallery), you can create a separate `Engine` instance, load the same scene data into it, and export thumbnails there. ```kotlin import android.content.Context import android.net.Uri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import java.io.File suspend fun generateThumbnailsHeadless( context: Context, license: String, userId: String, sceneUris: List ): List { val thumbnails = mutableListOf() // Create a headless engine instance val engine = Engine.getInstance(id = "ly.img.engine.thumbnail") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) try { for (sceneUri in sceneUris) { // Load scene val scene = engine.scene.load(sceneUri = sceneUri) // Export thumbnail val options = ExportOptions( jpegQuality = 0.8f, targetWidth = 400, targetHeight = 300 ) val blob = engine.block.export(scene, MimeType.JPEG, options) // Save to file val file = File(context.cacheDir, "thumb_${System.currentTimeMillis()}.jpg") withContext(Dispatchers.IO) { file.outputStream().channel.use { it.write(blob) } } thumbnails.add(file) } } finally { engine.stop() } return thumbnails } ``` > **Note:** When you're using the **prebuilt editor UI** in Android, you can also customize what happens when the user taps the Export button via the editor's export configuration. The default callback writes the exported data to a temporary file and triggers the share sheet. You could generate thumbnails here and control the export instead. ## Thumbnails from Video Blocks (Single Frame) Although this guide focuses on static image thumbnails, it's worth calling out an important edge case that often surprises developers: If you export a **paused video fill block**, the result is a **single image thumbnail**, just like exporting a graphic or page. This is *not* the same as generating a stream of video thumbnails or scrubbing previews. ### How This Works - Video blocks render their current frame when exported. - You can control *which* frame becomes the thumbnail by setting the playhead time before calling `export`. Conceptually: 1. Seek the video to the desired time. 2. Pause playback. 3. Export the block using the thumbnail export flow shown earlier. This produces a single static image suitable for: - Gallery previews - Document icons - Poster-frame–style thumbnails ## Troubleshooting | Symptom | Likely Cause | Solution | |---|---|---| | Thumbnail only shows part of the design | Exported a child block instead of the page | Export the page block to capture the full visible canvas | | Thumbnail size looks wrong | Missing or zero target dimension | Set both `targetWidth` and `targetHeight` | | Export is slow | Large target size or high PNG compression | Reduce dimensions or compression level | | File size too large | Quality settings too high | Lower JPEG/WebP quality or size | | Thumbnail looks blurry | Target size too small | Increase target dimensions | | Export fails | Scene not loaded | Ensure `engine.scene.get()` returns a valid scene | ## Next Steps - To learn more about exporting images and controlling output quality, see [Export designs to image formats](https://img.ly/docs/cesdk/android/export-save-publish/export/overview-9ed3a8/). - Reduce file size or tune quality for thumbnails and previews, with [Compress exported images](https://img.ly/docs/cesdk/android/export-save-publish/export/compress-29105e/). - If you need to generate thumbnails at scale or as part of automated workflows, take a look at [Batch processing designs](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/). --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Export" description: "Explore export options, supported formats, and configuration features for sharing or rendering output." platform: android url: "https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Export Media Assets](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) --- --- ## Related Pages - [Options](https://img.ly/docs/cesdk/android/export-save-publish/export/overview-9ed3a8/) - Explore export options, supported formats, and configuration features for sharing or rendering output. - [For Audio Processing](https://img.ly/docs/cesdk/android/guides/export-save-publish/export/audio-68de25/) - Learn how to export audio in WAV or MP4 format from any block type in CE.SDK for Android. - [To PDF](https://img.ly/docs/cesdk/android/export-save-publish/export/to-pdf-95e04b/) - Learn how to export pages to PDF. - [Compress Exports for Smaller Files](https://img.ly/docs/cesdk/android/export-save-publish/export/compress-29105e/) - Learn how to reduce file sizes during export from CE.SDK for Android by tuning format-specific compression settings in Kotlin. - [Create Thumbnail in Android (Kotlin)](https://img.ly/docs/cesdk/android/export-save-publish/create-thumbnail-749be1/) - Generate small preview images for scenes and pages using CE.SDK export options in Kotlin. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Compress Exports for Smaller Files" description: "Learn how to reduce file sizes during export from CE.SDK for Android by tuning format-specific compression settings in Kotlin." platform: android url: "https://img.ly/docs/cesdk/android/export-save-publish/export/compress-29105e/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Export Media Assets](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) > [Compress](https://img.ly/docs/cesdk/android/export-save-publish/export/compress-29105e/) --- Compression's goal is to reduce file sizes during export while maintaining as much visual quality as possible. With the CreativeEditor SDK (CE.SDK) for Android, you can fine-tune compression settings for both images and videos in Kotlin. This allows your app to balance performance, quality, and storage efficiency across all Android devices. ## What You'll Learn - How to configure compression for PNG, JPEG, and WebP exports in Kotlin. - How to control video file size using bitrate and resolution scaling. - How to balance file size, quality, and export performance for different use cases. - How to configure compression programmatically during automation or batch operations. ## When to Use It Compression tuning is useful whenever: - Exported media is too large for upload limits - You need to optimize storage quotas - You have constrained network bandwidth Use it when preparing images or videos for any workflow that benefits from: - Faster load times and smaller files, like: - Social media - Web delivery - Consistent file size and predictable performance, like: - Batch export - Automation scenarios ## Understanding Compression Options by Format Each format supports its own parameters for balancing: - Speed - File size - Quality You pass these through the `ExportOptions` structure when calling the export functions. | Format | Parameter | Type | Effect | Default | | ------- | ---------- | ---- | ------- | -------- | | PNG | `pngCompressionLevel` | 0–9 | Higher = smaller, slower (lossless) | 5 | | JPEG | `jpegQuality` | 0.0–1.0 | Lower = smaller, lower quality | 0.9 | | WebP | `webpQuality` | 0.0–1.0 | 1.0 = lossless, \<1.0 = lossy | 1.0 | | MP4 | Video bitrate via options | bits/sec | Higher = larger, higher quality | Auto | ## Export Images with Compression Below is an example that exports a design block as PNG and JPEG while tuning compression options. ```kotlin import android.content.Context import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import java.io.File fun exportCompressedImages( context: Context, license: String, userId: String ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) // Load a demo scene val sceneUri = Uri.parse("https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene") val scene = engine.scene.load(sceneUri = sceneUri) // Select the first graphic block to export val blocks = engine.block.findByType(DesignBlockType.Graphic) if (blocks.isNotEmpty()) { val block = blocks.first() // Export PNG with maximum compression (lossless) val pngOptions = ExportOptions(pngCompressionLevel = 9) val pngData = engine.block.export(block, mimeType = MimeType.PNG, options = pngOptions) // Save PNG to file val pngFile = File(context.filesDir, "compressed.png") withContext(Dispatchers.IO) { pngFile.outputStream().channel.use { channel -> channel.write(pngData) } } // Export JPEG with balanced quality (lossy) val jpegOptions = ExportOptions(jpegQuality = 0.7f) val jpegData = engine.block.export(block, mimeType = MimeType.JPEG, options = jpegOptions) // Save JPEG to file val jpegFile = File(context.filesDir, "compressed.jpg") withContext(Dispatchers.IO) { jpegFile.outputStream().channel.use { channel -> channel.write(jpegData) } } } engine.stop() } ``` Choose a format depending on what matters the most for your output: - **PNG** is ideal for flat graphics or assets that require **transparency**. - **JPEG** is best for photographs where slight **compression** artifacts are acceptable. - **WebP** can serve **both** roles: it supports transparency like PNG and delivers smaller files like JPEG. ## Combine Compression with Resolution Scaling You can further reduce file size by downscaling exports: ```kotlin import ly.img.engine.ExportOptions import ly.img.engine.MimeType val scaledOptions = ExportOptions( pngCompressionLevel = 7, targetWidth = 1080f, targetHeight = 1080f ) val scaledData = engine.block.export(block, mimeType = MimeType.PNG, options = scaledOptions) ``` When you specify only one dimension, CE.SDK automatically preserves aspect ratio for consistent results. ## Compress Video Exports For video exports, you can control compression through various export parameters. The Android SDK uses callback-based video export with progress tracking. ```kotlin import android.content.Context import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.MimeType import java.io.File fun exportCompressedVideo( context: Context, license: String, userId: String ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1280, height = 720) // Load or create a video scene val scene = engine.scene.createForVideo() // ... add video content ... // Export page as compressed MP4 val page = engine.block.findByType(DesignBlockType.Page).firstOrNull() if (page != null) { val videoData = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { progress -> println("Rendered ${progress.renderedFrames}/${progress.totalFrames} frames") println("Encoded ${progress.encodedFrames}/${progress.totalFrames} frames") } ) // Save video to file val videoFile = File(context.filesDir, "compressed_video.mp4") withContext(Dispatchers.IO) { videoFile.outputStream().channel.use { channel -> channel.write(videoData) } } } engine.stop() } ``` About the video compression: - Video bitrate and encoding settings are handled automatically by the engine - The SDK optimizes compression based on the content and target resolution - You can control output quality through resolution scaling using `targetWidth` and `targetHeight` in export options ## Performance and Trade-Offs Higher compression results in smaller files but slower export speeds. For example: - PNG Level 9 may take twice as long to encode as Level 3–5, though it produces smaller files. - JPEG and WebP are faster but can introduce visible compression artifacts. Video exports are more demanding and depend heavily on device CPU and GPU performance. You can check available export limits before encoding: ```kotlin import ly.img.engine.Engine val maxSize = engine.editor.getMaxExportSize() println("Max export size: $maxSize pixels") ``` ## Real-World Compression Comparison (1080 × 1080) The following table compares average results across different compression settings for photo-like and graphic-like images. | Format | Setting | Avg. File Size (KB) | Encode Time (ms) | PSNR (dB)\* | Notes | | ------- | -------- | ------------------- | ---------------- | ------------ | ------ | | **PNG** | Level 0 | ~1 450 | ~44 | ∞ (lossless) | Fastest, largest | | | Level 5 | ~1 260 | ~61 | ∞ | Balanced speed and size | | | Level 9 | ~1 080 | ~88 | ∞ | Smallest, slowest | | **JPEG** | Quality 95 | ~640 | ~24 | 43 | Near-lossless appearance | | | Quality 80 | ~420 | ~20 | 39 | Good default for photos | | | Quality 60 | ~290 | ~17 | 35 | Some artifacts visible | | | Quality 40 | ~190 | ~15 | 31 | Heavy compression | | **WebP** | Quality 95 | ~510 | ~27 | 44 | Smaller than JPEG | | | Quality 80 | ~350 | ~23 | 39 | Excellent web balance | | | Quality 60 | ~240 | ~20 | 35 | Mild artifacts | | | Quality 40 | ~160 | ~18 | 31 | Compact, noticeable loss | | | Lossless | ~830 | ~33 | ∞ | Smaller than PNG, keeps alpha | \*PSNR > 40 dB ≈ visually lossless; 30–35 dB shows mild artifacts. **Key Takeaways**: - **WebP** achieves 70–85 % smaller files than uncompressed PNG with high quality around `webpQuality = 0.8f`. - **JPEG** performs well for photographs; use `jpegQuality = 0.8f–0.9f` for web or print, `0.6f` for compact exports. - **PNG** is essential for transparency and vector-like shapes; higher levels reduce size modestly at the cost of speed. - Test on realistic assets: complex photos and flat graphics compress differently. ## Practical Presets These presets provide starting points for common export scenarios. | Use Case | Format | Typical Settings | Result | Notes | |-----------|---------|------------------|---------|-------| | **Web or Social Sharing** | JPEG / WebP | `jpegQuality: 0.8f` or `webpQuality: 0.8f` | ~60–70 % smaller than PNG | Balanced quality and size | | **UI Graphics / Transparent Assets** | PNG / WebP | `pngCompressionLevel: 6–8` or `webpQuality: 1.0f (lossless)` | ~25 % smaller than default PNG | Maintains transparency | | **High-Quality Print or Archival** | PNG / WebP Lossless | `pngCompressionLevel: 9` or `webpQuality: 1.0f` | Maximum fidelity | Slower export, large files | | **Video for Web / Social** | MP4 | Use default settings with resolution scaling | Smooth playback, small file | Adjust resolution as needed | | **Video for Download / HD** | MP4 | Higher resolution (1920×1080) | Full HD quality | Larger file, slower encode | **PDF and Print**: PDF exports use vector graphics when possible and aren't compressed by default. > **Note:** Consider showing users an **estimated file size** before export. It helps them make informed choices about quality vs. performance. ## Automating Compression in Batch Exports When exporting multiple elements, apply the same compression settings programmatically: ```kotlin import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import java.io.File suspend fun exportAllGraphics(engine: Engine, context: Context) { val blocks = engine.block.findByType(DesignBlockType.Graphic) val options = ExportOptions(jpegQuality = 0.8f) blocks.forEachIndexed { index, block -> val data = engine.block.export(block, mimeType = MimeType.JPEG, options = options) val file = File(context.filesDir, "export_$index.jpg") withContext(Dispatchers.IO) { file.outputStream().channel.use { channel -> channel.write(data) } } } } ``` This ensures consistent quality and file size across all exported assets. ## Troubleshooting **❌ File size not reduced**: - Ensure correct property name such as `jpegQuality`, `webpQuality`. - Check that values are floats (e.g., `0.8f` not `0.8`). **❌ JPEG Quality too low**: - Increase quality to `0.9f` or use PNG/WebP lossless. **❌ Export slow**: - Check for excessive compression level. - Lower PNG level to 5–6. - Use `withContext(Dispatchers.IO)` for file operations. **❌ Video export issues**: - Ensure video scene is properly configured. - Check available memory with `engine.editor.getMaxExportSize()`. - Monitor progress callback for encoding status. **❌ Out of memory errors**: - Reduce target resolution with `targetWidth` and `targetHeight`. - Export smaller blocks instead of full scene. - Call `engine.stop()` and restart for fresh memory state. ## Next Steps Compression is one of the most practical tools for optimizing export workflows. By adjusting the `ExportOptions` structure in Kotlin, you can deliver high-quality results efficiently—whether your users are exporting social media posts, UI assets, or professional-grade print layouts. - [Export Overview](https://img.ly/docs/cesdk/android/export-save-publish/export/overview-9ed3a8/) to learn about all available export formats. - Apply compression consistently in automated exports using [batch processing](https://img.ly/docs/cesdk/android/automation/batch-processing-ab2d18/). - Combine scaling and compression for [thumbnails](https://img.ly/docs/cesdk/android/export-save-publish/create-thumbnail-749be1/). - Learn about [export formats](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) and their capabilities. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Options" description: "Explore export options, supported formats, and configuration features for sharing or rendering output." platform: android url: "https://img.ly/docs/cesdk/android/export-save-publish/export/overview-9ed3a8/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Export Media Assets](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) > [Overview](https://img.ly/docs/cesdk/android/export-save-publish/export/overview-9ed3a8/) --- ```kotlin file=@cesdk_android_examples/engine-guides-exporting-blocks/ExportingBlocks.kt reference-only import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.Engine import ly.img.engine.ExportOptions import ly.img.engine.MimeType import ly.img.engine.addDefaultAssetSources import java.io.File import java.util.UUID fun exportingBlocks( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) engine.scene.create() engine.editor.setSettingString( "basePath", value = "https://cdn.img.ly/packages/imgly/cesdk-engine/1.70.0/assets", ) engine.addDefaultAssetSources() val sceneUri = Uri.parse( "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", ) val scene = engine.scene.load(sceneUri = sceneUri) // Export scene as PNG image. val mimeType = MimeType.PNG // Optionally, the maximum supported export size can be checked before exporting. val maxExportSizeInPixels = engine.editor.getMaxExportSize() // Optionally, the compression level and the target size can be specified. val options = ExportOptions(pngCompressionLevel = 9, targetWidth = null, targetHeight = null) val blob = engine.block.export(scene, mimeType = mimeType, options = options) // Save the blob to file withContext(Dispatchers.IO) { File.createTempFile(UUID.randomUUID().toString(), ".png").apply { outputStream().channel.write(blob) } } engine.stop() } ``` Exporting via the `block.export` endpoint allows fine-grained control of the target format. CE.SDK currently supports exporting scenes, pages, groups, or individual blocks in the MP4, PNG, TGA, JPEG, WEBP, BINARY and PDF formats. To specify the desired type, just pass in the corresponding `mimeType`. Pass additional options in a mime-type specific object: | Format | MimeType | Options (Default) | | ------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | PNG | `image/png` | `pngCompressionLevel (5)` - The compression level is a trade-off between file size and encoding/decoding speed, but doesn't affect quality. Valid values are `[0-9]` ranging from no to maximum compression. | | JPEG | `image/jpeg` | `jpegQuality (0.9)` - Directly influences the resulting files visual quality. Smaller = worse quality, but lower file size. Valid values are `(0-1]` | | WEBP | `image/webp` | `webpQuality (1.0)` - Controls the desired output quality. 1.0 results in a special lossless encoding that usually produces smaller file sizes than PNG. Valid values are (0-1], higher means better quality. | | BINARY | `application/octet-stream` | No additional options. This type returns the raw image data in RGBA8888 order in a blob. | | PDF | `application/pdf` | `exportPdfWithHighCompatibility (true)` - Increase compatibility with different PDF viewers. Images and effects will be rasterized with regard to the scene's DPI value instead of simply being embedded. | | PDF | `application/pdf` | `exportPdfWithUnderlayer (false)` - An underlayer is generated by adding a graphics block behind the existing elements of the shape of the elements on the page. | | PDF | `application/pdf` | `underlayerSpotColorName ("")` - The name of the spot color to be used for the underlayer's fill. | | PDF | `application/pdf` | `underlayerOffset (0.0)` - The adjustment in size of the shape of the underlayer. | Certain formats allow additional configuration, e.g. `options.jpegQuality` controls the output quality level when exporting to JPEG. These format-specific options are ignored when exporting to other formats. You can choose which part of the scene to export by passing in the respective block as the first parameter. For all formats, an optional `targetWidth` and `targetHeight` in pixels can be specified. If used, the block will be rendered large enough, that it fills the target size entirely while maintaining its aspect ratio. The supported export size limit can be queried with `editor.getMaxExportSize()`, the width and height should not exceed this value. ### PDF Export Performance The `exportPdfWithHighCompatibility` option controls how images and gradients are embedded in PDFs. When set to `false`, PDF exports can be **6-15x faster** for high-DPI content by embedding images directly instead of rasterizing them. However, this may cause rendering issues in Safari and macOS Preview with gradients that use transparency. Export details: - Exporting automatically performs an internal update to resolve the final layout for all blocks. - Only blocks that belong to the scene hierarchy can be exported. - The export will include the block and all child elements in the block hierarchy. - If the exported block itself is rotated it will be exported without rotation. - If a margin is set on the block it will be included. - If an outside stroke is set on the block it will be included except for pages. - Exporting a scene with more than one page may result in transparent areas between the pages, it is recommended to export the individual pages instead. - Exporting as JPEG drops any transparency on the final image and may lead to unexpected results. ```kotlin reference-only val scene = engine.scene.get()!! val page = engine.scene.getCurrentPage()!! val exportOptions = options = ExportOptions( /** * The PNG compression level to use, when exporting to PNG. * Valid values are 0 to 9, higher means smaller, but slower. * Quality is not affected. * Ignored for other encodings. * The default value is 5. */ pngCompressionLevel = 5, /** * The JPEG quality to use when encoding to JPEG. * Valid values are (0F-1F], higher means better quality. * Ignored for other encodings. * The default value is 0.9F. */ jpegQuality = 0.9F, /** * The WebP quality to use when encoding to WebP. Valid values are (0-1], higher means better quality. * WebP uses a special lossless encoding that usually produces smaller file sizes than PNG. * Ignored for other encodings. Defaults to 1.0. */ webpQuality = 1.0F, /** * An optional target width used in conjunction with target height. * If used, the block will be rendered large enough, that it fills the target * size entirely while maintaining its aspect ratio. * The default value is null. */ targetWidth = null, /** * An optional target height used in conjunction with target with. * If used, the block will be rendered large enough, that it fills the target * size entirely while maintaining its aspect ratio. * The default value is null. */ targetHeight = null, /** * Export the PDF document with a higher compatibility to different PDF viewers. * Bitmap images and some effects like gradients will be rasterized with the DPI * setting instead of embedding them directly. * The default value is true. */ exportPdfWithHighCompatibility = true, /** * Export the PDF document with an underlayer. * An underlayer is generated by adding a graphics block behind the existing elements of the shape of the elements on * the page. */ exportPdfWithUnderlayer = false, /** * The name of the spot color to be used for the underlayer's fill. */ underlayerSpotColorName = "" /** * The adjustment in size of the shape of the underlayer. */ underlayerOffset = 0.0F ) val blob = engine.block.export( block = scene, mimeType = MimeType.PNG, options = exportOptions ) val colorMaskedBlob = engine.block.exportWithColorMask( block = scene, mimeType = MimeType.PNG, maskColor = Color.fromRGBA(r = 1F, g = 0F, b = 0F) options = exportOptions ) val videoExportOptions = ExportVideoOptions( /** * Determines the encoder feature set and in turn the quality, size and speed of the encoding process. * The default value is 77 (Main Profile). */ h264Profile = 77, /** * Controls the H.264 encoding level. This relates to parameters used by the encoder such as bit rate, * timings and motion vectors. Defined by the spec are levels 1.0 up to 6.2. To arrive at an integer value, * the level is multiplied by ten. E.g. to get level 5.2, pass a value of 52. * The default value is 52. */ h264Level = 52, /** * The video bitrate in bits per second. The maximum bitrate is determined by h264Profile and h264Level. * If the value is 0, the bitrate is automatically determined by the engine. */ videoBitrate = 0, /** * The audio bitrate in bits per second. If the value is 0, the bitrate is automatically determined by the engine (128kbps for stereo AAC stream). */ audioBitrate = 0, /** * The target frame rate of the exported video in Hz. * The default value is 30. */ frameRate = 30.0F, /** * An optional target width used in conjunction with target height. * If used, the block will be rendered large enough, that it fills the target * size entirely while maintaining its aspect ratio. */ targetWidth = 1280, /** * An optional target height used in conjunction with target width. * If used, the block will be rendered large enough, that it fills the target * size entirely while maintaining its aspect ratio. */ targetHeight = 720 ) val videoBlob = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { println("Rendered ${it.renderedFrames} frames and encoded ${it.encodedFrames} frames out of ${it.totalFrames} frames") } ) val maxExportSizeInPixels = engine.editor.getMaxExportSize() val availableMemoryInBytes = engine.editor.getAvailableMemory() ``` ## Export a Static Design ```kotlin suspend fun export( block: DesignBlock, mimeType: MimeType, options: ExportOptions? = null, onPreExport: suspend Engine.() -> Unit = {}, ): ByteBuffer ``` Exports a design block element as a file of the given mime type. Performs an internal update to resolve the final layout for the blocks. Note: The export happens in a background thread and the `Engine` instance in the `onPreExport` lambda is a separate instance and is alive until the suspending function resumes. Use this lambda to configure the background engine for export. - `block`: the design block element to export. - `mimeType`: the mime type of the output file. - `options`: the options for exporting the block type - `onPreExport`: the lambda to configure the engine before export. This lambda is called on a background thread, and the `Engine` parameter of this lambda is a separate engine instance running in that background thread. - Returns the exported data. ## Export with a Color Mask ```kotlin suspend fun exportWithColorMask( block: DesignBlock, mimeType: MimeType, maskColor: RGBAColor, options: ExportOptions? = null, onPreExport: suspend Engine.() -> Unit = {}, ): Pair ``` Exports a design block element as a file of the given mime type. Performs an internal update to resolve the final layout for the blocks. Note: The export happens in a background thread and the `Engine` instance in the `onPreExport` lambda is a separate instance and is alive until the suspending function resumes. Use this lambda to configure the background engine for export. - `block`: the design block element to export. - `mimeType`: the mime type of the output file. - `maskColor`: the mask color. - `options`: the options for exporting the block type - `onPreExport`: the lambda to configure the engine before export. This lambda is called on a background thread, and the `Engine` parameter of this lambda is a separate engine instance running in that background thread. - Returns a pair where first is the exported image data and second is the mask data. ## Export a Video Export a page as a video file of the given mime type. ```kotlin suspend fun exportVideo( block: DesignBlock, timeOffset: Double, duration: Double, mimeType: MimeType, progressCallback: (ExportVideoProgress) -> Unit, options: ExportVideoOptions? = null, onPreExport: suspend Engine.() -> Unit = {}, ): ByteBuffer ``` Exports a design block as a video file of the given mime type. Note: The export will run across multiple iterations of the update loop. In each iteration a frame is scheduled for encoding. Note: The export happens in a background thread and the `Engine` instance in the `onPreExport` lambda is a separate instance and is alive until the suspending function resumes. Use this lambda to configure the background engine for export. - `block`: the design block to export. Currently, only page blocks are supported. - `timeOffset`: the time offset in seconds of the page's timeline from which the video will start. - `duration`: the duration in seconds of the final video. - `mimeType`: the mime type of the output video file. - `progressCallback`: a callback that reports on the progress of the export. It informs the receiver of the number of frames rendered by the engine, the number of encoded frames, and the total number of frames to encode. - `options`: the options used for the export of the block. - `onPreExport`: the lambda to configure the engine before export. This lambda is called on a background thread, and the `Engine` parameter of this lambda is a separate engine instance running in that background thread. - Returns the exported video as a file in filesDir. Note that you are responsible for deleting the file after it is used. - Returns the exported data. ## Export Information Before exporting, the maximum export size and available memory can be queried. ```kotlin fun getMaxExportSize(): Int ``` Get the export size limit in pixels on the current device. An export is only possible when both the width and height of the output are below or equal this limit. However, this is only an upper limit as the export might also not be possible due to other reasons, e.g., memory constraints. - Returns the upper export size limit in pixels or an unlimited size, i.e, the maximum signed 32-bit integer value, if the limit is unknown. ```kotlin fun getAvailableMemory(): Long ``` Get the currently available memory in bytes. - Returns the currently available memory in bytes. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "To PDF" description: "Learn how to export pages to PDF." platform: android url: "https://img.ly/docs/cesdk/android/export-save-publish/export/to-pdf-95e04b/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Export Media Assets](https://img.ly/docs/cesdk/android/export-save-publish/export-82f968/) > [To PDF](https://img.ly/docs/cesdk/android/export-save-publish/export/to-pdf-95e04b/) --- ```kotlin file=@cesdk_android_examples/editor-guides-configuration-callbacks/CallbacksEditorSolution.kt reference-only import android.net.Uri import android.widget.Toast import androidx.compose.runtime.Composable import androidx.core.content.FileProvider import androidx.navigation.NavHostController import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.editor.DesignEditor import ly.img.editor.DismissVideoExportEvent import ly.img.editor.EditorDefaults import ly.img.editor.EngineConfiguration import ly.img.editor.HideLoading import ly.img.editor.OnSceneLoaded import ly.img.editor.ShareUriEvent import ly.img.editor.ShowCloseConfirmationDialogEvent import ly.img.editor.ShowErrorDialogEvent import ly.img.editor.ShowLoading import ly.img.editor.ShowVideoExportErrorEvent import ly.img.editor.ShowVideoExportProgressEvent import ly.img.editor.ShowVideoExportSuccessEvent import ly.img.editor.core.event.EditorEvent import ly.img.editor.core.library.data.TextAssetSource import ly.img.editor.core.library.data.TypefaceProvider import ly.img.engine.MimeType import ly.img.engine.SceneMode import ly.img.engine.addDefaultAssetSources import ly.img.engine.addDemoAssetSources import kotlin.coroutines.cancellation.CancellationException // Add this composable to your NavHost @Composable fun CallbacksEditorSolution(navController: NavHostController) { val engineConfiguration = EngineConfiguration.remember( license = "", // pass null or empty for evaluation mode with watermark onCreate = { // Note that lambda is copied from EditorDefaults.onCreate coroutineScope { // In case of process recovery, engine automatically recovers the scene that is why we need to check if (editorContext.engine.scene.get() == null) { editorContext.engine.scene.load(EngineConfiguration.defaultDesignSceneUri) } launch { editorContext.engine.addDefaultAssetSources() val defaultTypeface = TypefaceProvider().provideTypeface(editorContext.engine, "Roboto") requireNotNull(defaultTypeface) editorContext.engine.asset.addSource(TextAssetSource(editorContext.engine, defaultTypeface)) } launch { editorContext.engine.addDemoAssetSources( sceneMode = editorContext.engine.scene.getMode(), withUploadAssetSources = true, ) } } editorContext.eventHandler.send(HideLoading) editorContext.eventHandler.send(OnSceneLoaded()) }, onLoaded = { editorContext.engine.editor.setSettingEnum("touch/pinchAction", "Scale") coroutineScope { launch { editorContext.engine.editor .onHistoryUpdated() .collect { Toast.makeText(editorContext.activity, "History is updated!", Toast.LENGTH_SHORT).show() } } launch { editorContext.engine.editor .onStateChanged() .map { editorContext.engine.editor.getEditMode() } .distinctUntilChanged() .collect { Toast.makeText(editorContext.activity, "Edit mode is updated to $it!", Toast.LENGTH_SHORT).show() } } } }, onExport = { val engine = editorContext.engine val eventHandler = editorContext.eventHandler val context = engine.applicationContext EditorDefaults.run { if (engine.scene.getMode() == SceneMode.VIDEO) { val page = engine.scene.getCurrentPage() ?: engine.scene.getPages()[0] var exportProgress = 0f eventHandler.send(ShowVideoExportProgressEvent(exportProgress)) runCatching { val buffer = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, progressCallback = { progress -> val newProgress = progress.encodedFrames.toFloat() / progress.totalFrames if (newProgress >= exportProgress + 0.01f) { exportProgress = newProgress eventHandler.send(ShowVideoExportProgressEvent(exportProgress)) } }, ) val file = writeToTempFile(buffer, MimeType.MP4) withContext(Dispatchers.IO) { FileProvider.getUriForFile( context, "${context.packageName}.ly.img.editor.fileprovider", file, ) } }.onSuccess { uri -> eventHandler.send(ShowVideoExportSuccessEvent(uri, MimeType.MP4.key)) }.onFailure { if (it is CancellationException) { eventHandler.send(DismissVideoExportEvent) } else { eventHandler.send(ShowVideoExportErrorEvent) } } } else { eventHandler.send(ShowLoading) runCatching { val buffer = engine.block.export( block = requireNotNull(engine.scene.get()), mimeType = MimeType.PDF, ) { scene.getPages().forEach { block.setScopeEnabled(it, key = "layer/visibility", enabled = true) block.setVisible(it, visible = true) } } val file = writeToTempFile(buffer, MimeType.PDF) withContext(Dispatchers.IO) { FileProvider.getUriForFile( context, "${context.packageName}.ly.img.editor.fileprovider", file, ) } }.onSuccess { uri -> eventHandler.send(HideLoading) eventHandler.send(ShareUriEvent(uri, MimeType.PDF.key)) }.onFailure { eventHandler.send(HideLoading) eventHandler.send(ShowErrorDialogEvent(error = it)) } } } }, onUpload = { assetDefinition, _ -> val meta = assetDefinition.meta ?: return@remember assetDefinition val sourceUri = Uri.parse(meta["uri"]) val uploadedUri = sourceUri // todo upload the asset here and return remote uri val newMeta = meta + listOf( "uri" to uploadedUri.toString(), "thumbUri" to uploadedUri.toString(), ) assetDefinition.copy(meta = newMeta) }, onClose = { hasUnsavedChanges -> if (hasUnsavedChanges) { editorContext.eventHandler.send(ShowCloseConfirmationDialogEvent) } else { editorContext.eventHandler.send(EditorEvent.CloseEditor()) } }, onError = { error -> editorContext.eventHandler.send(ShowErrorDialogEvent(error)) }, ) DesignEditor(engineConfiguration = engineConfiguration) { // You can set result here navController.popBackStack() } } ``` In this guide, you'll learn how to configure the prebuilt editor (Design Editor, Photo Editor, etc.) to export designs as PDFs. You can keep the built-in file write and share behavior, or inject export options (DPI, underlayer, high compatibility) while preserving the default flow. ## What You'll Learn - Switch the prebuilt editor's export to PDF via `onExport`. - Preserve the default event flow (share file) without custom UI code. - Add export options (DPI, high-compat, underlayer) for design scenes. - Keep MP4 export for video scenes while using PDF for design scenes. ## When to Use It - You want editor UI out of the box, with export customized to PDF. - You want to avoid writing your own share/save UI. - You need optional export tweaks but prefer the editor's default UX. ## Export Control The prebuilt editors have a share control in the navigation bar. This control exports the current scene as either a PNG or MP4, depending on the scene's mode. Then it displays a share sheet or system share dialog. ![Location of export button in prebuilt editors](assets/create-pdf-android-1.png) The editors provide an `onExport` callback you can use to control this behavior. If your export to PDF isn't complicated, configuring the MIME type may be all you need. ```kotlin val engineConfiguration = EngineConfiguration.remember( license = "", onExport = { val engine = editorContext.engine val eventHandler = editorContext.eventHandler EditorDefaults.run { eventHandler.send(ShowLoading) runCatching { val buffer = engine.block.export( block = requireNotNull(engine.scene.get()), mimeType = MimeType.PDF, ) writeToTempFile(buffer, MimeType.PDF) }.onSuccess { file -> eventHandler.send(HideLoading) eventHandler.send(ShareFileEvent(file, MimeType.PDF.key)) }.onFailure { eventHandler.send(HideLoading) eventHandler.send(ShowErrorDialogEvent(error = it)) } } } ) DesignEditor(engineConfiguration = engineConfiguration) { navController.popBackStack() } ``` The preceding code: - Tells the default handler to export design scenes to PDF. - Writes the PDF to a temp file and shares it. - Displays loading and error states using the event handler. ## Adding Options to Export For more complex configurations, you can use `ExportOptions` to customize the PDF output. The `onExport` callback gets access to the engine and event handler. ```kotlin val engineConfiguration = EngineConfiguration.remember( license = "", onExport = { val engine = editorContext.engine val eventHandler = editorContext.eventHandler EditorDefaults.run { when (engine.scene.getMode()) { SceneMode.VIDEO -> { val page = engine.scene.getCurrentPage() ?: engine.scene.getPages()[0] runCatching { val buffer = engine.block.exportVideo( block = page, timeOffset = 0.0, duration = engine.block.getDuration(page), mimeType = MimeType.MP4, ) writeToTempFile(buffer, MimeType.MP4) }.onSuccess { file -> eventHandler.send(ShareFileEvent(file, MimeType.MP4.key)) } } else -> { eventHandler.send(ShowLoading) runCatching { val options = ExportOptions( exportPdfWithHighCompatibility = true, ) val buffer = engine.block.export( block = requireNotNull(engine.scene.get()), mimeType = MimeType.PDF, options = options, ) writeToTempFile(buffer, MimeType.PDF) }.onSuccess { file -> eventHandler.send(HideLoading) eventHandler.send(ShareFileEvent(file, MimeType.PDF.key)) }.onFailure { eventHandler.send(HideLoading) eventHandler.send(ShowErrorDialogEvent(error = it)) } } } } } ) ``` The preceding code creates an `ExportOptions` object and passes it to the PDF export function. You can configure additional options like underlayer generation and DPI settings. Once the PDF has been written to disk, the callback passes it to the event handler to display the share dialog. For video scenes, the callback exports as MP4 instead. ## Export Options The `ExportOptions` class provides these parameters for PDF customization: ```kotlin val options = ExportOptions( exportPdfWithHighCompatibility = true, // Flatten complex elements exportPdfWithUnderlayer = true, // Generate print underlayer underlayerSpotColorName = "White", // Spot color for underlayer ink underlayerOffset = -2.0f, // Offset in design units ) ``` **Available options:** - `exportPdfWithHighCompatibility`: Flatten PDF for better compatibility with older viewers. - `exportPdfWithUnderlayer`: Generate an underlayer shape for specialized printing (fabric, glass). - `underlayerSpotColorName`: Specify the spot color name for the underlayer ink. - `underlayerOffset`: Adjust underlayer size with positive (larger) or negative (smaller) offset. > **Note:** When using underlayer, do not flatten the resulting PDF or you will remove the underlayer. The underlayer sits behind your design as a separate layer. ## Troubleshooting **❌ Export still shares PNG/MP4**: - Ensure `MimeType.PDF` is specified in the `onExport` callback. - Verify the callback is properly configured in `EngineConfiguration`. **❌ PDF looks soft**: - Check the "scene/dpi" property of the scene and increase it if necessary. - Ensure placed images have enough pixels to cover the frame. **❌ Underlayer absent in output**: - Verify the spot color name is correct and matches what the printer expects. - Ensure `exportPdfWithUnderlayer = true` is set in `ExportOptions`. - Do not flatten the PDF or the underlayer will be lost. **❌ Video scene shows different flow**: - This is expected. The SDK exports videos slightly differently than static image scenes. ## Next Steps Now that you know how to configure the editor to export to PDF, here are some topics to explore: - [Spot Colors](https://img.ly/docs/cesdk/android/colors-a9b79c/) – use spot colors for specialized printing. - [Asset Sources & Upload](https://img.ly/docs/cesdk/android/import-media-4e3703/) – bring user images/videos in, then export as PDF. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Save" description: "Save design progress locally or to a backend service to allow for later editing or publishing." platform: android url: "https://img.ly/docs/cesdk/android/export-save-publish/save-c8b124/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Save](https://img.ly/docs/cesdk/android/export-save-publish/save-c8b124/) --- The CreativeEngine allows you to save scenes in a binary format to share them between editors or store them for later editing. Saving a scene can be done as a either scene file or as an archive file. A scene file does not include any fonts or images. Only the source URIs of assets, the general layout, and element properties are stored. When loading scenes in a new environment, ensure previously used asset URIs are available. Conversely, an archive file contains within it the scene's assets and references them as relative URIs. > **Note:** **Warning** A scene file does not include any fonts or images. Only the source > URIs of assets, the general layout, and element properties are stored. When > loading scenes in a new environment, ensure previously used asset URIs are > available. ```kotlin file=@cesdk_android_examples/engine-guides-save-scene-to-archive/SaveSceneToArchive.kt reference-only import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.Engine import java.net.HttpURLConnection import java.net.URL import java.nio.channels.Channels fun saveSceneToArchive( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val sceneUri = Uri.parse( "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", ) val scene = engine.scene.load(sceneUri = sceneUri) val blob = engine.scene.saveToArchive(scene = scene) withContext(Dispatchers.IO) { val connection = URL("https://example.com/upload/").openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.doOutput = true connection.outputStream.use { Channels.newChannel(it).write(blob) } connection.connect() } engine.stop() } ``` ## Save Scenes to an Archive In this example, we will show you how to save scenes as an archive with the [CreativeEditor SDK](https://img.ly/products/creative-sdk). As an archive, the resulting `Blob` includes all pages and any hidden elements and all the asset data. To get hold of such a `Blob`, you need to use `engine.scene.saveToArchive()`. This is an asynchronous method. After waiting for the coroutine to finish, we receive a `Blob` holding the entire scene currently loaded in the editor including its assets' data. ```kotlin highlight-saveToArchive val blob = engine.scene.saveToArchive(scene = scene) ``` That `Blob` can then be treated as a form file parameter and sent to a remote location. ```kotlin highlight-create-form-data-archive withContext(Dispatchers.IO) { val connection = URL("https://example.com/upload/").openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.doOutput = true connection.outputStream.use { Channels.newChannel(it).write(blob) } connection.connect() } ``` ### Full Code Here's the full code: ```kotlin import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.Engine import java.net.HttpURLConnection import java.net.URL import java.nio.channels.Channels fun saveSceneToArchive( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) val sceneUri = Uri.parse( "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", ) val scene = engine.scene.load(sceneUri = sceneUri) val blob = engine.scene.saveToArchive(scene = scene) withContext(Dispatchers.IO) { val connection = URL("https://example.com/upload/").openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.doOutput = true connection.outputStream.use { Channels.newChannel(it).write(blob) } connection.connect() } engine.stop() } ``` ```kotlin file=@cesdk_android_examples/engine-guides-save-scene-to-blob/SaveSceneToBlob.kt reference-only import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.Engine import java.net.HttpURLConnection import java.net.URL fun saveSceneToBlob( license: String?, // pass null or empty for evaluation mode with watermark userId: String, uploadUrl: String, ) = CoroutineScope( Dispatchers.Main, ).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val sceneUri = Uri.parse( "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", ) val scene = engine.scene.load(sceneUri = sceneUri) val savedSceneString = engine.scene.saveToString(scene = scene) val blob = savedSceneString.toByteArray(Charsets.UTF_8) runCatching { withContext(Dispatchers.IO) { val connection = URL(uploadUrl).openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.doOutput = true connection.outputStream.use { it.write(blob) } connection.connect() } } engine.stop() } ``` ## Save Scenes to a Blob In this example, we will show you how to save scenes as a `Blob` with the [CreativeEditor SDK](https://img.ly/products/creative-sdk). This is done by converting the contents of a scene to a string, which can then be stored or transferred. For sending these to a remote location, we wrap them in a `Blob` and treat it as a file object. To get hold of the scene contents as string, you need to use `engine.scene.saveToString()`. This is an asynchronous method. After waiting for the coroutine to finish, we receive a plain string holding the entire scene currently loaded in the editor. This includes all pages and any hidden elements but none of the actual asset data. ```kotlin highlight-saveToBlob val savedSceneString = engine.scene.saveToString(scene = scene) ``` The returned string consists solely of ASCII characters and can safely be used further or written to a database. ```kotlin highlight-create-blob val blob = savedSceneString.toByteArray(Charsets.UTF_8) ``` That object can then be treated as a form file parameter and sent to a remote location. ```kotlin highlight-create-form-data-blob runCatching { withContext(Dispatchers.IO) { val connection = URL(uploadUrl).openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.doOutput = true connection.outputStream.use { it.write(blob) } connection.connect() } } ``` ### Full Code Here's the full code: ```kotlin import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import ly.img.engine.Engine import java.net.HttpURLConnection import java.net.URL fun saveSceneToBlob( license: String, userId: String, uploadUrl: String, ) = CoroutineScope( Dispatchers.Main, ).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) val sceneUri = Uri.parse( "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", ) val scene = engine.scene.load(sceneUri = sceneUri) val savedSceneString = engine.scene.saveToString(scene = scene) val blob = savedSceneString.toByteArray(Charsets.UTF_8) runCatching { withContext(Dispatchers.IO) { val connection = URL(uploadUrl).openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.doOutput = true connection.outputStream.use { it.write(blob) } connection.connect() } } engine.stop() } ``` ```kotlin file=@cesdk_android_examples/engine-guides-save-scene-to-string/SaveSceneToString.kt reference-only import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Engine fun saveSceneToString( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val sceneUri = Uri.parse( "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", ) val scene = engine.scene.load(sceneUri = sceneUri) val savedSceneString = engine.scene.saveToString(scene = scene) println(savedSceneString) engine.stop() } ``` ## Save Scenes to a String In this example, we will show you how to save scenes as a string with the [CreativeEditor SDK](https://img.ly/products/creative-sdk). This is done by converting the contents of a scene to a single string, which can then be stored or transferred. To get hold of such a string, you need to use `engine.scene.saveToString()`. This is an asynchronous method. After waiting for the coroutine to finish, we receive a plain string holding the entire scene currently loaded in the editor. This includes all pages and any hidden elements, but none of the actual asset data. ```kotlin highlight-saveToString val savedSceneString = engine.scene.saveToString(scene = scene) ``` The returned string consists solely of ASCII characters and can safely be used further or written to a database. ```kotlin highlight-result-string println(savedSceneString) ``` ## Full Code ```kotlin import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Engine fun saveSceneToString( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) val sceneUri = Uri.parse( "https://cdn.img.ly/assets/demo/v1/ly.img.template/templates/cesdk_postcard_1.scene", ) val scene = engine.scene.load(sceneUri = sceneUri) val savedSceneString = engine.scene.saveToString(scene = scene) println(savedSceneString) engine.stop() } ``` ## Compression Options CE.SDK supports optional compression for saved scenes to reduce file size. Compression is particularly useful for large scenes or when storage space is limited. ```kotlin // Save with Zstd compression (recommended) val compressed = engine.scene.saveToString( scene = scene, options = SaveToStringOptions( compression = CompressionOptions( format = CompressionFormat.ZSTD, level = CompressionLevel.DEFAULT ) ) ) ``` **Compression Formats:** - `CompressionFormat.NONE` - No compression (default) - `CompressionFormat.ZSTD` - Zstandard compression (recommended for best performance) **Compression Levels:** - `CompressionLevel.FASTEST` - Fastest compression, larger output - `CompressionLevel.DEFAULT` - Balanced speed and size (recommended) - `CompressionLevel.BEST` - Best compression, slower **Performance:** Compression adds minimal overhead while reducing scene size by approximately 64%. The default level provides the best balance of speed and compression ratio. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Store Custom Metadata" description: "Attach and persist metadata alongside your design, such as tags, version info, or creator details." platform: android url: "https://img.ly/docs/cesdk/android/export-save-publish/store-custom-metadata-337248/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Store Custom Metadata](https://img.ly/docs/cesdk/android/export-save-publish/store-custom-metadata-337248/) --- ```kotlin file=@cesdk_android_examples/engine-guides-store-metadata/StoreMetadata.kt reference-only import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine fun storeMetadata( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) var scene = engine.scene.createFromImage( Uri.parse("https://img.ly/static/ubq_samples/imgly_logo.jpg"), ) val block = engine.block.findByType(DesignBlockType.Page).first() engine.block.setMetadata(scene, key = "author", value = "img.ly") engine.block.setMetadata(block, key = "customer_id", value = "1234567890") engine.block.setMetadata(block, key = "customer_name", value = "Name") // This will return "img.ly" engine.block.getMetadata(scene, key = "author") // This will return "1000000" engine.block.getMetadata(block, key = "customer_id") // This will return ["customer_id", "customer_name"] engine.block.findAllMetadata(block) engine.block.removeMetadata(block, key = "customer_id") // This will return false engine.block.hasMetadata(block, key = "customer_id") // We save our scene and reload it from scratch val sceneString = engine.scene.saveToString(scene) scene = engine.scene.load(scene = sceneString) // This still returns "img.ly" engine.block.getMetadata(scene, key = "author") // And this still returns "Name" engine.block.getMetadata(block, key = "customer_name") engine.stop() } ``` CE.SDK allows you to store custom metadata in your scenes. You can attach metadata to your scene or directly to your individual design blocks within the scene. This metadata is persistent across saving and loading of scenes. It simply consists of key value pairs of strings. Using any string-based serialization format such as JSON will allow you to store even complex objects. Please note that when duplicating blocks their metadata will also be duplicated. ## Working with Metadata We can add metadata to any design block using `fun setMetadata(block: DesignBlock, key: String, value: String)`. This also includes the scene block. ```kotlin highlight-setMetadata engine.block.setMetadata(scene, key = "author", value = "img.ly") engine.block.setMetadata(block, key = "customer_id", value = "1234567890") engine.block.setMetadata(block, key = "customer_name", value = "Name") ``` We can retrieve metadata from any design block or scene using `fun getMetadata(block: DesignBlock, key: String): String`. Before accessing the metadata you check for its existence using `fun hasMetadata(block: DesignBlock, key: String): Boolean`. ```kotlin highlight-getMetadata // This will return "img.ly" engine.block.getMetadata(scene, key = "author") // This will return "1000000" engine.block.getMetadata(block, key = "customer_id") ``` We can query all metadata keys from any design block or scene using `fun findAllMetadata(block: DesignBlock): List`. For blocks without any metadata, this will return an empty list. ```kotlin highlight-findAllMetadata // This will return ["customer_id", "customer_name"] engine.block.findAllMetadata(block) ``` If you want to get rid of any metadata, you can use `fun removeMetadata(block: DesignBlock, key: String)`. ```kotlin highlight-removeMetadata engine.block.removeMetadata(block, key = "customer_id") // This will return false engine.block.hasMetadata(block, key = "customer_id") ``` Metadata will automatically be saved and loaded as part the scene. So you don't have to worry about it getting lost or having to save it separately. ```kotlin highlight-persistence // We save our scene and reload it from scratch val sceneString = engine.scene.saveToString(scene) scene = engine.scene.load(scene = sceneString) // This still returns "img.ly" engine.block.getMetadata(scene, key = "author") // And this still returns "Name" engine.block.getMetadata(block, key = "customer_name") ``` ## Full Code Here's the full code: ```kotlin import android.net.Uri import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.Engine fun storeMetadata( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) var scene = engine.scene.createFromImage( Uri.parse("https://img.ly/static/ubq_samples/imgly_logo.jpg"), ) val block = engine.block.findByType(DesignBlockType.Graphic).first() engine.block.setMetadata(scene, key = "author", value = "img.ly") engine.block.setMetadata(block, key = "customer_id", value = "1234567890") engine.block.setMetadata(block, key = "customer_name", value = "Name") // This will return "img.ly" engine.block.getMetadata(scene, key = "author") // This will return "1000000" engine.block.getMetadata(block, key = "customer_id") // This will return ["customer_id", "customer_name"] engine.block.findAllMetadata(block) engine.block.removeMetadata(block, key = "customer_id") // This will return false engine.block.hasMetadata(block, key = "customer_id") // We save our scene and reload it from scratch val sceneString = engine.scene.saveToString(scene) scene = engine.scene.load(scene = sceneString) // This still returns "img.ly" engine.block.getMetadata(scene, key = "author") // And this still returns "Name" engine.block.getMetadata(block, key = "customer_name") engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "File Format Support" description: "See which image, video, audio, font, and template formats CE.SDK supports for import and export." platform: android url: "https://img.ly/docs/cesdk/android/file-format-support-3c4b2a/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Compatibility & Security](https://img.ly/docs/cesdk/android/compatibility-fef719/) > [File Format Support](https://img.ly/docs/cesdk/android/file-format-support-3c4b2a/) --- ## Importing Media ### SVG Limitations ## Exporting Media ## Importing Templates ## Font Formats ## Video & Audio Codecs CE.SDK supports the most widely adopted video and audio codecs to ensure compatibility across platforms: ## Size Limits ### Image Resolution Limits ### Video Resolution & Duration Limits --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Fills" description: "Apply solid colors, gradients, images, or videos as fills to shapes, text, and other design elements." platform: android url: "https://img.ly/docs/cesdk/android/fills-402ddc/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Fills](https://img.ly/docs/cesdk/android/fills-402ddc/) --- --- ## Related Pages - [Fills](https://img.ly/docs/cesdk/android/fills/overview-3895ee/) - Apply solid colors, gradients, images, or videos as fills to shapes, text, and other design elements. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Fills" description: "Apply solid colors, gradients, images, or videos as fills to shapes, text, and other design elements." platform: android url: "https://img.ly/docs/cesdk/android/fills/overview-3895ee/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Fills](https://img.ly/docs/cesdk/android/fills-402ddc/) > [Overview](https://img.ly/docs/cesdk/android/fills/overview-3895ee/) --- ```kotlin file=@cesdk_android_examples/engine-guides-using-fills/UsingFills.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun usingFills( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setWidth(block, value = 100F) engine.block.setHeight(block, value = 100F) engine.block.setFill(block, fill = engine.block.createFill(FillType.Color)) engine.block.appendChild(parent = page, child = block) engine.block.supportsFill(scene) // Returns false engine.block.supportsFill(block) // Returns true val colorFill = engine.block.getFill(block) val defaultRectFillType = engine.block.getType(colorFill) val allFillProperties = engine.block.findAllProperties(colorFill) engine.block.setColor( block = colorFill, property = "fill/color/value", value = Color.fromRGBA(r = 1.0F, g = 0.0F, b = 0.0F, a = 1.0F), ) engine.block.setFillEnabled(block, enabled = false) engine.block.setFillEnabled(block, enabled = !engine.block.isFillEnabled(block)) val imageFill = engine.block.createFill(FillType.Image) engine.block.setString( block = imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) engine.block.destroy(colorFill) engine.block.setFill(block, fill = imageFill) /* // The following line would also destroy imageFill engine.block.destroy(circle) */ val duplicateBlock = engine.block.duplicate(block) engine.block.setPositionX(duplicateBlock, value = 450F) val autoDuplicateFill = engine.block.getFill(duplicateBlock) engine.block.setString( block = autoDuplicateFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_2.jpg", ) /* // We could now assign this fill to another block. val manualDuplicateFill = engine.block.duplicate(autoDuplicateFill) engine.block.destroy(manualDuplicateFill) */ val sharedFillBlock = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(sharedFillBlock, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(sharedFillBlock, value = 350F) engine.block.setPositionY(sharedFillBlock, value = 400F) engine.block.setWidth(sharedFillBlock, value = 100F) engine.block.setHeight(sharedFillBlock, value = 100F) engine.block.appendChild(parent = page, child = sharedFillBlock) engine.block.setFill(sharedFillBlock, fill = engine.block.getFill(block)) engine.stop() } ``` Some [design blocks](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) in CE.SDK allow you to modify or replace their fill. The fill is an object that defines the contents within the shape of a block. CreativeEditor SDK supports many different types of fills, such as images, solid colors, gradients and videos. Similarly to blocks, each fill has a numeric id which can be used to query and [modify its properties](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/). We currently support the following fill types: - `FillType.Color` - `FillType.LinearGradient` - `FillType.RadialGradient` - `FillType.ConicalGradient` - `FillType.Image` - `FillType.Video` - `FillType.PixelStream` ## Accessing Fills Not all types of design blocks support fills, so you should always first call the `fun supportsFill(block: DesignBlock): Boolean` API before accessing any of the following APIs. ```kotlin highlight-supportsFill engine.block.supportsFill(scene) // Returns false engine.block.supportsFill(block) // Returns true ``` In order to receive the fill id of a design block, call the `fun getFill(block: DesignBlock): DesignBlock` API. You can now pass this id into other APIs in order to query more information about the fill, e.g. its type via the `fun getType(block: DesignBlock): String` API. ```kotlin highlight-getFill val colorFill = engine.block.getFill(block) val defaultRectFillType = engine.block.getType(colorFill) ``` ## Fill Properties Just like design blocks, fills with different types have different properties that you can query and modify via the API. Use `fun findAllProperties(block: DesignBlock): List` in order to get a list of all properties of a given fill. For the solid color fill in this example, the call would return `["fill/color/value", "type"]`. Please refer to the [design blocks](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) for a complete list of all available properties for each type of fill. ```kotlin highlight-getProperties val allFillProperties = engine.block.findAllProperties(colorFill) ``` Once we know the property keys of a fill, we can use the same APIs as for design blocks in order to modify those properties. For example, we can use `fun setColor(block: DesignBlock, property: String, value: Color)` in order to change the color of the fill to red. Once we do this, our graphic block with rect shape will be filled with solid red. ```kotlin highlight-modifyProperties engine.block.setColor( block = colorFill, property = "fill/color/value", value = Color.fromRGBA(r = 1.0F, g = 0.0F, b = 0.0F, a = 1.0F), ) ``` ## Disabling Fills You can disable and enable a fill using the `fun setFillEnabled(block: DesignBlock, enabled: Boolean)` API, for example in cases where the design block should only have a stroke but no fill. Notice that you have to pass the id of the design block and not of the fill to the API. ```kotlin highlight-disableFill engine.block.setFillEnabled(block, enabled = false) engine.block.setFillEnabled(block, enabled = !engine.block.isFillEnabled(block)) ``` ## Changing Fill Types All design blocks that support fills allow you to also exchange their current fill for any other type of fill. In order to do this, you need to first create a new fill object using `fun createFill(fillType: FillType): DesignBlock`. ```kotlin highlight-createFill val imageFill = engine.block.createFill(FillType.Image) engine.block.setString( block = imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) ``` In order to assign a fill to a design block, simply call `fun setFill(block: DesignBlock, fill: DesignBlock)`. Make sure to delete the previous fill of the design block first if you don't need it any more, otherwise we will have leaked it into the scene and won't be able to access it any more, because we don't know its id. Notice that we don't use the `appendChild` API here, which only works with design blocks and not fills. When a fill is attached to one design block, it will be automatically destroyed when the block itself gets destroyed. ```kotlin highlight-replaceFill engine.block.destroy(colorFill) engine.block.setFill(block, fill = imageFill) /* // The following line would also destroy imageFill engine.block.destroy(circle) */ ``` ## Duplicating Fills If we duplicate a design block with a fill that is only attached to this block, the fill will automatically be duplicated as well. In order to modify the properties of the duplicate fill, we have to query its id from the duplicate block. ```kotlin highlight-duplicateFill val duplicateBlock = engine.block.duplicate(block) engine.block.setPositionX(duplicateBlock, value = 450F) val autoDuplicateFill = engine.block.getFill(duplicateBlock) engine.block.setString( block = autoDuplicateFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_2.jpg", ) /* // We could now assign this fill to another block. val manualDuplicateFill = engine.block.duplicate(autoDuplicateFill) engine.block.destroy(manualDuplicateFill) */ ``` ## Sharing Fills It is also possible to share a single fill instance between multiple design blocks. In that case, changing the properties of the fill will apply to all of the blocks that it's attached to at once. Destroying a block with a shared fill will not destroy the fill until there are no other design blocks left that still use that fill. ```kotlin highlight-sharedFill val sharedFillBlock = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(sharedFillBlock, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(sharedFillBlock, value = 350F) engine.block.setPositionY(sharedFillBlock, value = 400F) engine.block.setWidth(sharedFillBlock, value = 100F) engine.block.setHeight(sharedFillBlock, value = 100F) engine.block.appendChild(parent = page, child = sharedFillBlock) engine.block.setFill(sharedFillBlock, fill = engine.block.getFill(block)) ``` ## Full Code Here is the full code for working with fills: ```kotlin import kotlinx.coroutines.* import ly.img.engine.* fun usingFills( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setWidth(block, value = 100F) engine.block.setHeight(block, value = 100F) engine.block.setFill(block, fill = engine.block.createFill(FillType.Color)) engine.block.appendChild(parent = page, child = block) engine.block.supportsFill(scene) // Returns false engine.block.supportsFill(block) // Returns true val colorFill = engine.block.getFill(block) val defaultRectFillType = engine.block.getType(colorFill) val allFillProperties = engine.block.findAllProperties(colorFill) engine.block.setColor( block = colorFill, property = "fill/color/value", value = Color.fromRGBA(r = 1.0F, g = 0.0F, b = 0.0F, a = 1.0F), ) engine.block.setFillEnabled(block, enabled = false) engine.block.setFillEnabled(block, enabled = !engine.block.isFillEnabled(block)) val imageFill = engine.block.createFill(FillType.Image) engine.block.setString( block = imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) engine.block.destroy(colorFill) engine.block.setFill(block, fill = imageFill) /* // The following line would also destroy imageFill engine.block.destroy(circle) */ val duplicateBlock = engine.block.duplicate(block) engine.block.setPositionX(duplicateBlock, value = 450F) val autoDuplicateFill = engine.block.getFill(duplicateBlock) engine.block.setString( block = autoDuplicateFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_2.jpg", ) /* // We could now assign this fill to another block. val manualDuplicateFill = engine.block.duplicate(autoDuplicateFill) engine.block.destroy(manualDuplicateFill) */ val sharedFillBlock = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(sharedFillBlock, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(sharedFillBlock, value = 350F) engine.block.setPositionY(sharedFillBlock, value = 400F) engine.block.setWidth(sharedFillBlock, value = 100F) engine.block.setHeight(sharedFillBlock, value = 100F) engine.block.appendChild(parent = page, child = sharedFillBlock) engine.block.setFill(sharedFillBlock, fill = engine.block.getFill(block)) engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Filters and Effects" description: "Enhance visual elements with filters and effects such as blur, duotone, LUTs, and chroma keying." platform: android url: "https://img.ly/docs/cesdk/android/filters-and-effects-6f88ac/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Filters and Effects](https://img.ly/docs/cesdk/android/filters-and-effects-6f88ac/) --- --- ## Related Pages - [Android Filters & Effects SDK](https://img.ly/docs/cesdk/android/filters-and-effects/overview-299b15/) - Enhance visual elements with filters and effects such as blur, duotone, LUTs, and chroma keying. - [Add a Filter or Effect](https://img.ly/docs/cesdk/android/filters-and-effects/create-custom-filters-c796ba/) - Programmatically or manually add effects to design elements to modify their visual style. - [Chroma Key (Green Screen) in Android (Kotlin)](https://img.ly/docs/cesdk/android/filters-and-effects/chroma-key-green-screen-1e3e99/) - Use CE.SDK's green/blue screen keyer to replace backgrounds, tune edges & spill, and composite subjects over virtual scenes. - [Blur](https://img.ly/docs/cesdk/android/filters-and-effects/blur-71d642/) - Apply blur effects to soften backgrounds or create depth and focus in your designs. - [Create a Custom LUT Filter](https://img.ly/docs/cesdk/android/filters-and-effects/create-custom-lut-filter-6e3f49/) - Create and apply custom LUT filters to achieve consistent, brand-aligned visual styles. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Blur" description: "Apply blur effects to soften backgrounds or create depth and focus in your designs." platform: android url: "https://img.ly/docs/cesdk/android/filters-and-effects/blur-71d642/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Filters and Effects](https://img.ly/docs/cesdk/android/filters-and-effects-6f88ac/) > [Apply Blur](https://img.ly/docs/cesdk/android/filters-and-effects/blur-71d642/) --- ```kotlin reference-only // Create and configure a radial blur val radialBlur = engine.block.createBlur(type = BlurType.Radial) engine.block.setFloat(radialBlur, property = "radial/radius", value = 100F) engine.block.setBlur(block, blurBlock = radialBlur) engine.block.setBlurEnabled(block, enabled = true) val isBlurEnabled = engine.block.isBlurEnabled(block) // Access an existing blur if (engine.block.supportsBlur(block)) { val existingBlur = engine.block.getBlur(block) } ``` In this example, we will show you how to use the [CreativeEditor SDK](https://img.ly/products/creative-sdk)'s CreativeEngine to modify a block's blur through the `block` API. Blurs can be added to any shape or page. You can create and configure individual blurs and apply them to blocks. Blocks support at most one blur at a time. The same blur may be used for different blocks at the same time. ## Creating a Blur To create a blur simply use `fun createBlur(type: BlurType): DesignBlock`. ```kotlin fun createBlur(type: BlurType): DesignBlock ``` Create a new blur, fails if type is unknown or not a valid blur type. - `type`: the type id of the block. - Returns the handle of the newly created blur. We currently support the following blur types: - `BlurType.Uniform` - `BlurType.Linear` - `BlurType.Mirrored` - `BlurType.Radial` ```kotlin highlight-BlockApi-createBlur // Create and configure a radial blur val radialBlur = engine.block.createBlur(type = BlurType.Radial) ``` ## Configuring a Blur You can configure blurs just like you configure design blocks. See [Modify Properties](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) for more detail. ```kotlin highlight-configureBlur engine.block.setFloat(radialBlur, property = "radial/radius", value = 100F) engine.block.setBlur(block, blurBlock = radialBlur) engine.block.setBlurEnabled(block, enabled = true) val isBlurEnabled = engine.block.isBlurEnabled(block) ``` ## Functions ```kotlin fun setBlur( block: DesignBlock, blurBlock: DesignBlock, ) ``` Connects `block`'s blur to the given `blurBlock`. Required scope: "appearance/blur" - `block`: the block to update. - `blurBlock`: a blur block. ```kotlin fun setBlurEnabled( block: DesignBlock, enabled: Boolean, ) ``` Enable or disable the blur of the given design block. - `block`: the block to update. - `enabled`: the new enabled value. ```kotlin fun isBlurEnabled(block: DesignBlock): Boolean ``` Query if blur is enabled for the given block. - `block`: the block to query. - Returns true if the blur is enabled, false otherwise. ```kotlin fun supportsBlur(block: DesignBlock): Boolean ``` Checks whether the block supports blur. - `block`: the block to query. - Returns true if the block supports blur, false otherwise. ```kotlin fun getBlur(block: DesignBlock): DesignBlock ``` Get the blur block of the given design block. - `block`: the block to query. - Returns the blur block. ## Full Code Here's the full code: ```kotlin // Create and configure a radial blur val radialBlur = engine.block.createBlur(type = BlurType.Radial) engine.block.setFloat(radialBlur, property = "radial/radius", value = 100F) engine.block.setBlur(block, blurBlock = radialBlur) engine.block.setBlurEnabled(block, enabled = true) val isBlurEnabled = engine.block.isBlurEnabled(block) // Access an existing blur if (engine.block.supportsBlur(block)) { val existingBlur = engine.block.getBlur(block) } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Chroma Key (Green Screen) in Android (Kotlin)" description: "Use CE.SDK's green/blue screen keyer to replace backgrounds, tune edges & spill, and composite subjects over virtual scenes." platform: android url: "https://img.ly/docs/cesdk/android/filters-and-effects/chroma-key-green-screen-1e3e99/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Filters and Effects](https://img.ly/docs/cesdk/android/filters-and-effects-6f88ac/) > [Apply Chroma Key (Green Screen)](https://img.ly/docs/cesdk/android/filters-and-effects/chroma-key-green-screen-1e3e99/) --- Chroma keying removes a uniform background color (often green or blue) from a video or image so you can composite the foreground over a new scene. In CE.SDK for Android, chroma keying is an **effect** you attach to an image or video block, with parameters for **color selection**, **similarity threshold**, **edge smoothing**, and **spill suppression**. This guide walks you through applying the effect in Kotlin, dialing it in for clean edges, and composing the keyed result with a replacement background. ## What You'll Learn - How to add the **Green Screen** effect to **image** and **video** blocks. - How to set the key color (green by default, but any color works). - How to tune **colorMatch** (similarity), **smoothness** (edge falloff), and **spill** (desaturating color cast). - How to layer a new background behind the keyed subject. - How to persist, export, and protect templates that include chroma key. ## When to Use It Use chroma key when your source contains a uniform backdrop (green, blue, or a solid brand color) and you want to: - Replace the background with a **virtual set**, branded plate, or blurred depth backdrop. - Place talent over **slides** or **product footage**. - Standardize a team's talking‑head videos with consistent backgrounds. - Composite when using an asset formats such as MP4, H.264 or, JPEG that don't support transparency. Avoid chroma key if the subject's clothing, props, or lighting contains the same hue as your key color, or if the background is highly textured. > **Chroma Key vs. Background Removal:** The `effect/green_screen` shader operates on color similarity directly on the GPU. Unlike AI-based background removal, chroma keying provides predictable, real‑time control for studio footage where lighting and backdrop color are controlled. ## Apply the Green Screen Effect In a Prebuilt Editor Chroma key is one of the standard effects available for images and video clips in the prebuilt editors, such as the Design Editor and the Video Editor. Use it as follows: 1. Select a key image or video clip. 2. Look for the `Effects` button in the inspector and tap it. ![Location of the Effect button in the Inspector](assets/chroma-key-ios-159-0.png) Scroll through the effects until you find "Green Screen". Once you tap it, the effect implements immediately. ![Arrow pointing to the Green Screen effect button](assets/chroma-key-ios-159-1.png) An options indicator appears for the effect. Tap it to show the options. ![Green screen effect button showing options indicator](assets/chroma-key-ios-159-2.png) Use the sliders and the color wheel, to change the settings for: - key color - color match - smoothness - spill ![Effect controls for key color, color match, smoothness and, spill](assets/chroma-key-ios-159-3.png) The "Tuning the Effect" section below explains each of these in detail. ## Apply the Green Screen Effect In Code CE.SDK exposes chroma key as the `EffectType.GreenScreen` effect type with the following key properties: - `effect/green_screen/fromColor` the color to key out (default green). - `effect/green_screen/colorMatch` similarity threshold \[0…1]. - `effect/green_screen/smoothness` edge falloff \[0…1]. - `effect/green_screen/spill` desaturates remaining color spill \[0…1]. ### Key an Image Block ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.EffectType import ly.img.engine.Engine fun applyGreenScreenToImage( engine: Engine, imageBlock: Int ) = CoroutineScope(Dispatchers.Main).launch { // 1) Create the effect and attach it to the block val keyer = engine.block.createEffect(type = EffectType.GreenScreen) engine.block.appendEffect(imageBlock, effectBlock = keyer) // 2) Choose the key color (here: pure green); any color works engine.block.setColor( keyer, property = "effect/green_screen/fromColor", color = Color.fromRGBA(r = 0.0f, g = 1.0f, b = 0.0f, a = 1.0f) ) // 3) Tune similarity, smoothness, and spill engine.block.setFloat(keyer, property = "effect/green_screen/colorMatch", value = 0.40f) engine.block.setFloat(keyer, property = "effect/green_screen/smoothness", value = 0.08f) engine.block.setFloat(keyer, property = "effect/green_screen/spill", value = 0.15f) } ``` ### Key a Video Block Video blocks use video fills instead of image fills, but the rest of the workflow is identical. ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.Color import ly.img.engine.EffectType import ly.img.engine.Engine fun applyGreenScreenToVideo( engine: Engine, videoBlock: Int ) = CoroutineScope(Dispatchers.Main).launch { val keyer = engine.block.createEffect(type = EffectType.GreenScreen) engine.block.appendEffect(videoBlock, effectBlock = keyer) // Blue screen example engine.block.setColor( keyer, property = "effect/green_screen/fromColor", color = Color.fromRGBA(r = 0.0f, g = 0.25f, b = 1.0f, a = 1.0f) ) engine.block.setFloat(keyer, property = "effect/green_screen/colorMatch", value = 0.35f) engine.block.setFloat(keyer, property = "effect/green_screen/smoothness", value = 0.10f) engine.block.setFloat(keyer, property = "effect/green_screen/spill", value = 0.25f) } ``` Order matters: if you add other effects, like color adjustments, place the **keyer first** in the stack so later effects operate on the premultiplied result. ### Pick the Key Color from the Image Hard‑coding `fromColor` works for controlled shoots. In general, sample the background color under the user's tap. > **Note:** CE.SDK doesn't provide a built-in API to read a pixel at a screen coordinate. In your Android app, map the tap location to the image/video buffer you control and sample the pixel using APIs such as `Bitmap.getPixel()` or `Canvas`. Convert the sampled RGBA to `Color.fromRGBA()` and set `effect/green_screen/fromColor`. If you embed the `DesignEditor`, keep an app-level copy of the media to sample from, since the editor's preview is GPU-rendered. Tie the sampled color back to the effect: ```kotlin import ly.img.engine.Color import ly.img.engine.Engine fun setKeyColor(engine: Engine, keyer: Int, r: Float, g: Float, b: Float) { engine.block.setColor( keyer, property = "effect/green_screen/fromColor", color = Color.fromRGBA(r = r, g = g, b = b, a = 1.0f) ) } ``` For polished UIs, show a zoomed loupe and a live matte preview as the user drags. ### Composite over a Replacement Background A keyed subject is transparent where the background was, so you **layer a background block beneath** the keyed block. ```kotlin import ly.img.engine.DesignBlockType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun addBackgroundBehind(engine: Engine, page: Int, subject: Int, imageUrl: String) { val bg = engine.block.create(DesignBlockType.Graphic) val shape = engine.block.createShape(ShapeType.Rect) engine.block.setShape(bg, shape = shape) val fill = engine.block.createFill(FillType.Image) engine.block.setString(fill, property = "fill/image/imageFileURI", value = imageUrl) engine.block.setFill(bg, fill = fill) // Make background full‑bleed on the page // Place background **behind** subject engine.block.insertChild(parent = page, child = bg, index = 0) engine.block.fillParent(bg) engine.block.sendToBack(bg) } ``` For video, create a video fill instead of an image fill and align durations in your export. ## Tuning the Effect The three parameters for tuning chroma key composition are: - color match - spill - smoothness Knowing what they impact can help decide your strategy when the composition doesn't look correct. The examples below all show how these values can change this chroma key image. ![Example composited image.](assets/chroma-key-ios-159-4.png) > **Recommended Starting Values:** | Background | colorMatch | smoothness | spill | > |-------------|-------------|------------|--------| > | **Green Screen** | 0.35–0.45 | 0.08–0.12 | 0.15–0.25 | > | **Blue Screen** | 0.30–0.40 | 0.10–0.15 | 0.25–0.35 | > | **Custom Color** | 0.40–0.50 | 0.08–0.12 | 0.10–0.20 |Tune `colorMatch` first for coverage, then refine edge softness with `smoothness`, and finally correct color tint with `spill`. ### Color Match Color Match determines how close a pixel's color has to be to the key color to be considered *background*. When the value is low, only exact matches are removed. When the value is high, a larger range of colors similar to the key color get removed. What to watch for when the value is wrong: - Too low: you may see patches of the green screen still visible around edges, especially if lighting is uneven or shadows present. - Too high: you risk keying out part of the subject (hair strands, clothing edges, reflective items) creating holes or transparency because the effect is too aggressive. ![Color match range examples.](assets/color-match-range-ios.jpg) The preceding image shows color match values of 0.0, 0.5 and 1.0. ### Smoothness Smoothness controls how gradually or sharply the transitions occur, how soft the matte edges of the gradients are. A low value produces sharp transition between keyed and un-keyed areas. When the value is high, there is softer transition. What to watch for when the value is wrong: - Too low: harsh edges, visible fringes around hair or "hard cutouts" that look unnatural. - Too high: a halo effect or the subject blends into the background. ![Smoothness range examples.](assets/smoothness-range-ios.jpg) The preceding image shows smoothness values of 0.0, 0.5 and 1.0. ### Spill Spill impacts the unwanted "color spill", when your key color reflects or bleeds onto the subject. This is especially noticeable around edges, hair and, shiny objects. What to watch for when the value is wrong: - Too low: you may see green reflection on the subject (especially edges/hair/shoulders) that doesn't get cleaned up, making it look unnatural or floating. - Too high: the subject's actual color edges are desaturated, making hair or detail look gray, faded or too soft. ![Spill range examples.](assets/spill-range-ios.jpg) The preceding image shows spill values of 0.0, 0.5 and 1.0. ## Lighting & Capture Tips - Keep your backdrop evenly lit and 1–2 stops brighter than your subject. - Avoid shadows or wrinkles. Uneven color creates transparency artifacts. - Separate your subject from the background by at least 1 m to reduce spill. ## Template & Scope Considerations If you ship templates that include a keyer, you might want to lock down parameters to protect quality: - Use **Scopes/Permissions** to limit which effect properties the end‑user can change. - Store platform‑tested defaults (match, smoothness, spill) in the template. - Provide preset chips like **"Green Screen"**, **"Blue Screen"**, **"Brand Cyan"** to switch `fromColor` quickly. ## Performance and Rendering Pipeline CE.SDK runs chroma keying directly on the graphics card for smooth, real-time results. Place the keyer near the start of your effect list so that later effects, like color or tone adjustments, apply correctly to the transparent areas. To keep playback fast, avoid heavy effects such as blur or LUTs before the keyer. ## Export Tips - Prefer **ProRes 4444** (or other alpha‑carrying formats) when exporting an intermediate keyed asset to reuse elsewhere. - For final composites, export with the background enabled and a standard delivery codec/format. ## Testing Checklist - Verify background color is uniform and well lit. - Check for reflective surfaces that might cause spill. - Test both **720p** and **4K** previews to compare performance. - Try different wardrobe colors. Avoid those close to the key color. - Examine edges on hair or fine detail under motion. - Validate output formats (e.g., MP4 with solid background vs. ProRes with alpha). ## Troubleshooting **❌ Holes in the matte (background not fully removed)**: - Increase `colorMatch` slightly. If edges get harsh, bump `smoothness` too. **❌ Foreground punched out (you lose subject detail)**: - Lower `colorMatch` until detail returns; then reduce `spill` if the subject appears tinted. **❌ Green/blue color cast on edges**: - Raise `spill` (try 0.2–0.4). If it looks gray, back it down. **❌ Jagged edges**: - Increase `smoothness` in small steps (0.05–0.15). - Consider adding a light `effect/blur` **after** the keyer for video. **❌ Uneven backgrounds / shadows**: - Sample a darker patch of the backdrop or increase `colorMatch` and compensate with `spill`. **❌ Nothing turns transparent:** - Verify the effect is attached to the **right block** and not to the page. - Check `fromColor` is close to the actual backdrop hue (sample it!). - Ensure your block type supports effects (graphic, video are supported). **❌ Performance drops with 4K video**: - Avoid stacking extra heavy effects **before** the keyer. - Render proxies or downscale the preview while tuning; export at full res. **❌ Skin tones look dull**: - Reduce `spill` and re‑tune `colorMatch`. **❌ Hair/fur looks crunchy:** - Raise `smoothness` incrementally (and consider light post‑blur). ## Next Steps With the core of chroma key compositing mastered, here are some other topics that may be interesting: - Learn about other [Filters & Effects](https://img.ly/docs/cesdk/android/filters-and-effects/overview-299b15/) and try combining the keyer with adjustments for color matching. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Add a Filter or Effect" description: "Programmatically or manually add effects to design elements to modify their visual style." platform: android url: "https://img.ly/docs/cesdk/android/filters-and-effects/create-custom-filters-c796ba/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Filters and Effects](https://img.ly/docs/cesdk/android/filters-and-effects-6f88ac/) > [Create Custom Filters](https://img.ly/docs/cesdk/android/filters-and-effects/create-custom-filters-c796ba/) --- ```kotlin file=@cesdk_android_examples/engine-guides-using-effects/UsingEffects.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.EffectType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun usingEffects( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 100F) engine.block.setPositionY(block, value = 50F) engine.block.setWidth(block, value = 300F) engine.block.setHeight(block, value = 300F) engine.block.appendChild(parent = page, child = block) val fill = engine.block.createFill(FillType.Image) engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) engine.block.setFill(block, fill = fill) engine.block.supportsEffects(scene) // Returns false engine.block.supportsEffects(block) // Returns true val pixelize = engine.block.createEffect(type = EffectType.Pixelize) val adjustments = engine.block.createEffect(type = EffectType.Adjustments) engine.block.appendEffect(block, effectBlock = pixelize) engine.block.insertEffect(block, effectBlock = adjustments, index = 0) // engine.block.removeEffect(rect, index = 0) // This will return [adjustments, pixelize] val effectsList = engine.block.getEffects(block) val unusedEffect = engine.block.createEffect(type = EffectType.HalfTone) engine.block.destroy(unusedEffect) val allPixelizeProperties = engine.block.findAllProperties(pixelize) val allAdjustmentProperties = engine.block.findAllProperties(adjustments) engine.block.setInt(pixelize, property = "pixelize/horizontalPixelSize", value = 20) engine.block.setFloat(adjustments, property = "effect/adjustments/brightness", value = 0.2F) engine.block.setEffectEnabled(pixelize, enabled = false) engine.block.setEffectEnabled(pixelize, !engine.block.isEffectEnabled(pixelize)) engine.stop() } ``` Some [design blocks](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) in CE.SDK such as pages and graphic blocks allow you to add effects to them. An effect can modify the visual output of a block's [fill](https://img.ly/docs/cesdk/android/fills-402ddc/). CreativeEditor SDK supports many different types of effects, such as adjustments, LUT filters, pixelization, glow, vignette and more. Similarly to blocks, each effect instance has a numeric id which can be used to query and [modify its properties](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/). We create a scene containing a graphic block with an image fill and want to apply effects to this image. ```kotlin highlight-setup val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 100F) engine.block.setPositionY(block, value = 50F) engine.block.setWidth(block, value = 300F) engine.block.setHeight(block, value = 300F) engine.block.appendChild(parent = page, child = block) val fill = engine.block.createFill(FillType.Image) engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) engine.block.setFill(block, fill = fill) ``` ## Accessing Effects Not all types of design blocks support effects, so you should always first call the `fun supportsEffects(block: DesignBlock): Boolean` API before accessing any of the following APIs. ```kotlin highlight-supportsEffects engine.block.supportsEffects(scene) // Returns false engine.block.supportsEffects(block) // Returns true ``` ## Creating an Effect In order to add effects to our block, we first have to create a new effect instance, which we can do by calling `fun createEffect(type: EffectType): DesignBlock` and passing it the type of effect that we want. In this example, we create a pixelization and an adjustment effect. Please refer to [API Docs](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) for a complete list of supported effect types. ```kotlin highlight-createEffect val pixelize = engine.block.createEffect(type = EffectType.Pixelize) val adjustments = engine.block.createEffect(type = EffectType.Adjustments) ``` ## Adding Effects Now we have two effects but the output of our scene looks exactly the same as before. That is because we still need to append these effects to the graphic design block's list of effects, which we can do by calling `fun appendEffect(block: DesignBlock, effectBlock: DesignBlock)`. We can also insert or remove effects from specific indices of a block's effect list using the `fun insertEffect(block: DesignBlock, effectBlock: DesignBlock, index: Int)` and `fun removeEffect(block: DesignBlock, index: Int)` APIs. Effects will be applied to the block in the order they are placed in the block's effects list. If the same effect appears multiple times in the list, it will also be applied multiple times. In our case, the adjustments effect will be applied to the image first, before the result of that is then pixelated. ```kotlin highlight-addEffect engine.block.appendEffect(block, effectBlock = pixelize) engine.block.insertEffect(block, effectBlock = adjustments, index = 0) // engine.block.removeEffect(rect, index = 0) ``` ## Querying Effects Use the `fun getEffects(block: DesignBlock): List` API to query the ordered list of effect ids of a block. ```kotlin highlight-getEffects // This will return [adjustments, pixelize] val effectsList = engine.block.getEffects(block) ``` ## Destroying Effects If we created an effect that we don't want anymore, we have to make sure to destroy it using the same `fun destroy(block: DesignBlock)` API that we also call for design blocks. Effects that are attached to a design block will be automatically destroyed when the design block is destroyed. ```kotlin highlight-destroyEffect val unusedEffect = engine.block.createEffect(type = EffectType.HalfTone) engine.block.destroy(unusedEffect) ``` ## Effect Properties Just like design blocks, effects with different types have different properties that you can query and modify via the API. Use `fun findAllProperties(block: DesignBlock): List` in order to get a list of all properties of a given effect. Please refer to the [API Docs](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/) for a complete list of all available properties for each type of effect. ```kotlin highlight-getProperties val allPixelizeProperties = engine.block.findAllProperties(pixelize) val allAdjustmentProperties = engine.block.findAllProperties(adjustments) ``` Once we know the property keys of an effect, we can use the same APIs as for design blocks in order to [modify those properties](https://img.ly/docs/cesdk/android/concepts/blocks-90241e/). Our adjustment effect here for example will not modify the output unless we at least change one of its adjustment properties - such as the brightness - to not be zero. ```kotlin highlight-modifyProperties engine.block.setInt(pixelize, property = "pixelize/horizontalPixelSize", value = 20) engine.block.setFloat(adjustments, property = "effect/adjustments/brightness", value = 0.2F) ``` ## Disabling Effects You can temporarily disable and enable the individual effects using the `fun setEffectEnabled(effectBlock: DesignBlock, enabled: Boolean)` API. When the effects are applied to a block, all disabled effects are simply skipped. Whether an effect is currently enabled or disabled can be queried with `fun isEffectEnabled(effectBlock: DesignBlock): Boolean`. ```kotlin highlight-disableEffect engine.block.setEffectEnabled(pixelize, enabled = false) engine.block.setEffectEnabled(pixelize, !engine.block.isEffectEnabled(pixelize)) ``` ## Full Code Here's the full code: ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.EffectType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun usingEffects( license: String, userId: String, ) = CoroutineScope(Dispatchers.Main).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 800F) engine.block.setHeight(page, value = 600F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( page, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val block = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(block, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setPositionX(block, value = 100F) engine.block.setPositionY(block, value = 50F) engine.block.setWidth(block, value = 300F) engine.block.setHeight(block, value = 300F) engine.block.appendChild(parent = page, child = block) val fill = engine.block.createFill(FillType.Image) engine.block.setString( block = fill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) engine.block.setFill(block, fill = fill) engine.block.supportsEffects(scene) // Returns false engine.block.supportsEffects(block) // Returns true val pixelize = engine.block.createEffect(type = EffectType.Pixelize) val adjustments = engine.block.createEffect(type = EffectType.Adjustments) engine.block.appendEffect(block, effectBlock = pixelize) engine.block.insertEffect(block, effectBlock = adjustments, index = 0) // engine.block.removeEffect(rect, index = 0) // This will return [adjustments, pixelize] val effectsList = engine.block.getEffects(block) val unusedEffect = engine.block.createEffect(type = EffectType.HalfTone) engine.block.destroy(unusedEffect) val allPixelizeProperties = engine.block.findAllProperties(pixelize) val allAdjustmentProperties = engine.block.findAllProperties(adjustments) engine.block.setInt(pixelize, property = "pixelize/horizontalPixelSize", value = 20) engine.block.setFloat(adjustments, property = "effect/adjustments/brightness", value = 0.2F) engine.block.setEffectEnabled(pixelize, enabled = false) engine.block.setEffectEnabled(pixelize, !engine.block.isEffectEnabled(pixelize)) engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Create a Custom LUT Filter" description: "Create and apply custom LUT filters to achieve consistent, brand-aligned visual styles." platform: android url: "https://img.ly/docs/cesdk/android/filters-and-effects/create-custom-lut-filter-6e3f49/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Filters and Effects](https://img.ly/docs/cesdk/android/filters-and-effects-6f88ac/) > [Apply Custom LUT Filter](https://img.ly/docs/cesdk/android/filters-and-effects/create-custom-lut-filter-6e3f49/) --- ```kotlin file=@cesdk_android_examples/engine-guides-custom-lut-filter/CustomLUTFilter.kt reference-only import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.EffectType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun customLUTFilter( license: String?, // pass null or empty for evaluation mode with watermark userId: String, ) = CoroutineScope( Dispatchers.Main, ).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 1080, height = 1920) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 100F) engine.block.setHeight(page, value = 100F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( scene, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val rect = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(rect, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setWidth(rect, value = 100F) engine.block.setHeight(rect, value = 100F) engine.block.appendChild(parent = page, child = rect) val imageFill = engine.block.createFill(FillType.Image) engine.block.setString( imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) val lutFilter = engine.block.createEffect(EffectType.LutFilter) engine.block.setBoolean(lutFilter, property = "effect/enabled", value = true) engine.block.setFloat(lutFilter, property = "effect/lut_filter/intensity", value = 0.9F) @Suppress("ktlint:standard:max-line-length") val lutUri = "https://cdn.img.ly/packages/imgly/cesdk-js/1.70.0/assets/extensions/ly.img.cesdk.filters.lut/LUTs/imgly_lut_ad1920_5_5_128.png" engine.block.setString( lutFilter, property = "effect/lut_filter/lutFileURI", value = lutUri, ) engine.block.setInt(lutFilter, property = "effect/lut_filter/verticalTileCount", value = 5) engine.block.setInt(lutFilter, property = "effect/lut_filter/horizontalTileCount", value = 5) engine.block.appendEffect(rect, effectBlock = lutFilter) engine.block.setFill(rect, fill = imageFill) engine.stop() } ``` We use a technology called Lookup Tables (LUTs) in order to add new filters to our SDK. The main idea is that colors respond to operations that are carried out during the filtering process. We 'record' that very response by applying the filter to the identity image shown below. Identity LUT The resulting image can be used within our SDK and the recorded changes can then be applied to any image by looking up the transformed colors in the modified LUT. If you want to create a new filter, you'll need to [download](content-assets/6e3f49/imgly_lut_ad1920_5_5_128.png) the identity LUT shown above, load it into an image editing software of your choice, apply your operations, save it and add it to your app. > **WARNING:** As any compression artifacts in the edited LUT could lead to distorted results when applying the filter, you need to save your LUT as a PNG file. ## Using Custom Filters In this example, we will use a hosted CDN LUT filter file. First we will load one of our demo scenes and change the first image to use LUT filter we will provide. We will also configure the necessary setting based on the file. LUT file we will use: Color grading LUT showcasing a grid of color variations used for applying a specific visual style to images. ## Load Scene After the setup, we create a new scene. Within this scene, we create a page, set its dimensions, and append it to the scene. Lastly, we adjust the zoom level to properly fit the page into the view. ```kotlin highlight-load-scene val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 100F) engine.block.setHeight(page, value = 100F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( scene, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) ``` ## Create Rectangle Next, we create a rectangle with defined dimensions and append it to the page. We will apply our LUT filter to this rectangle. ```kotlin highlight-create-rect val rect = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(rect, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setWidth(rect, value = 100F) engine.block.setHeight(rect, value = 100F) engine.block.appendChild(parent = page, child = rect) ``` ## Load Image After creating the rectangle, we create an image fill with a specified URL. This will load the image as a fill for the rectangle to which we will apply the LUT filter. ```kotlin highlight-create-image-fill val imageFill = engine.block.createFill(FillType.Image) engine.block.setString( imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) ``` ## Create LUT Filter Now, we create a Look-Up Table (LUT) filter effect. We enable the filter, set its intensity, and provide a URL for the LUT file. We also define the tile count for the filter. The LUT filter effect is then applied to the rectangle and image should appear black and white. ```kotlin highlight-create-lut-filter val lutFilter = engine.block.createEffect(EffectType.LutFilter) engine.block.setBoolean(lutFilter, property = "effect/enabled", value = true) engine.block.setFloat(lutFilter, property = "effect/lut_filter/intensity", value = 0.9F) @Suppress("ktlint:standard:max-line-length") val lutUri = "https://cdn.img.ly/packages/imgly/cesdk-js/1.70.0/assets/extensions/ly.img.cesdk.filters.lut/LUTs/imgly_lut_ad1920_5_5_128.png" engine.block.setString( lutFilter, property = "effect/lut_filter/lutFileURI", value = lutUri, ) engine.block.setInt(lutFilter, property = "effect/lut_filter/verticalTileCount", value = 5) engine.block.setInt(lutFilter, property = "effect/lut_filter/horizontalTileCount", value = 5) ``` ## Apply LUT Filter Finally, we apply the LUT filter effect to the rectangle, and set the image fill to the rectangle. Before setting an image fill, we destroy the default rectangle fill. ```kotlin highlight-apply-lut-filter engine.block.appendEffect(rect, effectBlock = lutFilter) engine.block.setFill(rect, fill = imageFill) ``` ## Full Code Here's the full code: ```kotlin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ly.img.engine.DesignBlockType import ly.img.engine.EffectType import ly.img.engine.Engine import ly.img.engine.FillType import ly.img.engine.ShapeType fun customLUTFilter( license: String, userId: String, ) = CoroutineScope( Dispatchers.Main, ).launch { val engine = Engine.getInstance(id = "ly.img.engine.example") engine.start(license = license, userId = userId) engine.bindOffscreen(width = 100, height = 100) val scene = engine.scene.create() val page = engine.block.create(DesignBlockType.Page) engine.block.setWidth(page, value = 100F) engine.block.setHeight(page, value = 100F) engine.block.appendChild(parent = scene, child = page) engine.scene.zoomToBlock( scene, paddingLeft = 40F, paddingTop = 40F, paddingRight = 40F, paddingBottom = 40F, ) val rect = engine.block.create(DesignBlockType.Graphic) engine.block.setShape(rect, shape = engine.block.createShape(ShapeType.Rect)) engine.block.setWidth(rect, value = 100F) engine.block.setHeight(rect, value = 100F) engine.block.appendChild(parent = page, child = rect) val imageFill = engine.block.createFill(FillType.Image) engine.block.setString( imageFill, property = "fill/image/imageFileURI", value = "https://img.ly/static/ubq_samples/sample_1.jpg", ) val lutFilter = engine.block.createEffect(EffectType.LutFilter) engine.block.setBoolean(lutFilter, property = "effect/enabled", value = true) engine.block.setFloat(lutFilter, property = "effect/lut_filter/intensity", value = 0.9F) @Suppress("ktlint:standard:max-line-length") val lutUri = "https://cdn.img.ly/packages/imgly/cesdk-js/$UBQ_VERSION$/assets/extensions/ly.img.cesdk.filters.lut/LUTs/imgly_lut_ad1920_5_5_128.png" engine.block.setString( lutFilter, property = "effect/lut_filter/lutFileURI", value = lutUri, ) engine.block.setInt(lutFilter, property = "effect/lut_filter/verticalTileCount", value = 5) engine.block.setInt(lutFilter, property = "effect/lut_filter/horizontalTileCount", value = 5) engine.block.appendEffect(rect, effectBlock = lutFilter) engine.block.setFill(rect, fill = imageFill) engine.stop() } ``` --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Android Filters & Effects SDK" description: "Enhance visual elements with filters and effects such as blur, duotone, LUTs, and chroma keying." platform: android url: "https://img.ly/docs/cesdk/android/filters-and-effects/overview-299b15/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Guides](https://img.ly/docs/cesdk/android/guides-8d8b00/) > [Filters and Effects](https://img.ly/docs/cesdk/android/filters-and-effects-6f88ac/) > [Overview](https://img.ly/docs/cesdk/android/filters-and-effects/overview-299b15/) --- In CreativeEditor SDK (CE.SDK), *filters* and *effects* refer to visual modifications that enhance or transform the appearance of design elements. Filters typically adjust an element’s overall color or tone, while effects add specific visual treatments like blur, sharpness, or distortion. You can apply both filters and effects through the user interface or programmatically using the CE.SDK API. They allow you to refine the look of images, videos, and graphic elements in your designs with precision and flexibility. [Explore Demos](https://img.ly/showcases/cesdk?tags=android) [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Clone GitHub Project" description: "Using CE.SDK with a cloned Android GitHub project" platform: android url: "https://img.ly/docs/cesdk/android/get-started/clone-github-project-f9890l/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) > [Quickstart Jetpack Compose](https://img.ly/docs/cesdk/android/get-started/new-jetpack-compose-project-c6567i/) --- This guide will walk you through cloning an existing sample project with the CE.SDK editor already set up ## Pre-requisites - Android Studio installed on your machine - A valid **CE.SDK license key** ([Get a free trial](https://img.ly/forms/free-trial)), use `null` or an empty string to run in evaluation mode with watermark. ## Clone the GitHub Repository Launch Android Studio and select `File -> New -> Project from version control` Next, add the following URL: ``` https://github.com/imgly/cesdk-android-examples.git ``` ![Android Studio Clone dialog](assets/CloneDialog.png) Click "Clone" and wait for the project to be downloaded and set up. ## Run the Project In the `local.properties` file, add your CE.SDK license key (or leave it empty to run in evaluation mode with watermark) ``` license=MY_LICENSE_GOES_HERE ``` You may have to create this file, if Android Studio did not generate it for you. Finally, run the app on your device or android emulator. ## Common Errors Here are some common errors you may encounter through this guide, and how to solve them. #### Invalid License | ![Invalid license dialog](assets/InvalidLicense.png) | ![Missing license dialog](assets/MissingLicense.png) | | ---------------------------------------------------- | ---------------------------------------------------- | | | | **Solution** -> Check whether you have supplied a valid license #### No Internet ![No internet dialog](assets/NoInternet.png) **Solution** -> Check your internet connection --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "Existing Project Setup" description: "Learn how to integrate the CreativeEditor SDK into your existing Android Jetpack Compose project" platform: android url: "https://img.ly/docs/cesdk/android/get-started/existing-project-g0901m/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) > [Quickstart Jetpack Compose](https://img.ly/docs/cesdk/android/get-started/new-jetpack-compose-project-c6567i/) --- This guide shows you how to integrate the CreativeEditor SDK into your existing Android Jetpack Compose project. Follow these steps to learn how to: - **add** the necessary dependencies. - **configure** the editor. - **test** the integration. ## Who Is This Guide For? This guide is for developers who: - Have an existing Android Jetpack Compose project - Want to add CreativeEditor SDK features to their app - Need to understand common integration patterns - Want to test available editing capabilities and workflows ## What You'll Achieve By following this guide, you'll perform the following tasks: - **Project Integration**: Add CreativeEditor SDK to your existing Android project - **Editor Implementation**: Implement the editor with proper lifecycle management - **Testing**: Verify the integration works correctly - **Customization**: Learn how to customize the editor for your use case [View Android Examples](https://github.com/imgly/cesdk-android-examples) [Android Documentation](https://img.ly/docs/cesdk/android) ## Prerequisites ### Development Environment - Android Studio (latest version) - Android SDK (API level 24 or higher) - Kotlin 1.9.10 or higher - Gradle 8.4 or later - Jetpack Compose BOM 2023.05.01 or higher ### Platform Requirements - **Android**: API level 24 (Android 7.0) or higher - **Supported ABIs**: arm64-v8a, armeabi-v7a, x86\_64, x86 ### License - A valid **CE.SDK license key** ([Get a free trial](https://img.ly/forms/free-trial)), use `null` or an empty string to run in evaluation mode with watermark. ## Verify Your Setup Before starting, verify your Android development environment: ```bash gradle --version ``` This command checks your Gradle installation and reports any issues to resolve before proceeding. > **Note:** You can customize the CreativeEditor SDK for Android exclusively through > native code (Kotlin), as described in the > [configuration overview section](https://img.ly/docs/cesdk/android/user-interface/customization-72b2f8/). ## Step 1: Add Dependencies to Your Existing Project ### 1.1 Add IMG.LY Repository Update your `settings.gradle.kts` file to include the IMG.LY repository: ```kotlin title="settings.gradle.kts" dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { name = "IMG.LY Artifactory" url = uri("https://artifactory.img.ly/artifactory/maven") mavenContent { includeGroup("ly.img") } } } } ``` ### 1.2 Add Editor Dependencies Update your `app/build.gradle.kts` file to include the CreativeEditor SDK: ```kotlin title="build.gradle.kts" dependencies { // CreativeEditor SDK implementation("ly.img:editor:1.57.0") // Jetpack Compose BOM implementation(platform("androidx.compose:compose-bom:2025.04.01")) implementation("androidx.activity:activity-compose") implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") implementation("androidx.compose.runtime:runtime") implementation("androidx.compose.foundation:foundation") } ``` ### 1.3 Configure Android Settings Ensure your `app/build.gradle.kts` has the correct Android configuration: ```kotlin title="build.gradle.kts" android { compileSdk = 34 defaultConfig { minSdk = 24 targetSdk = 34 } buildFeatures { compose = true } composeOptions { kotlinCompilerExtensionVersion = "1.5.15" } kotlinOptions { jvmTarget = "17" } packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } } ``` ### 1.4 Sync Project After adding the dependencies, sync your project: In Android Studio, click **Sync Project with Gradle Files** to download and configure all dependencies. This downloads and installs the CreativeEditor SDK and its dependencies automatically through Gradle. ## Step 2: Implement the Editor Integration ### 2.1 Create Editor Composable Create a new Kotlin file (for example, `EditorComposable.kt`) in your project: ```kotlin file=@cesdk_android_examples/editor-guides-quickstart/EditorComposable.kt import androidx.compose.runtime.Composable import ly.img.editor.DesignEditor import ly.img.editor.EngineConfiguration import ly.img.editor.rememberForDesign @Composable fun EditorComposable() { val engineConfiguration = EngineConfiguration.rememberForDesign( // Get your license from https://img.ly/forms/free-trial // pass null or empty for evaluation mode with watermark license = "", userId = "", // A unique string to identify your user/session ) DesignEditor( engineConfiguration = engineConfiguration, onClose = { // Close the editor here // If using a navigation library, call pop() or navigateUp() here }, ) } ``` ### 2.2 Update Your MainActivity Modify your existing `MainActivity.kt` to include the editor: ```kotlin title="MainActivity.kt" import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Modifier class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { EditorComposable() } } } } } ``` ### 2.3 Add Required Permissions Update your `AndroidManifest.xml` to include necessary permissions: ```xml title="AndroidManifest.xml" ``` ## Step 3: Test Your Editor Integration ### 3.1 Build and Run Build your Android project using Gradle: ### 3.2 Test on Android Device ```bash ./gradlew installInternalDebug ``` ### 3.3 Verify Features After launching your app, verify these features work correctly: - **Editor Launch**: The CreativeEditor SDK opens without errors - **Template Selection**: You can browse and select templates - **Editing Tools**: All editing tools are accessible and functional - **Export**: You can export edited content successfully - **Navigation**: The editor closes properly when finished ### 3.4 Common Test Scenarios - **Photo Editing**: Import and edit photos - **Text Addition**: Add and customize text elements - **Template Usage**: Apply and customize templates - **Export Options**: Test different export formats - **Performance**: Verify smooth operation on target devices ## Step 4: Customize for Your Use Case ### 4.1 Editor Configuration Options The CreativeEditor SDK offers different presets for common use cases: #### Available Editor Types Summary | Editor Type | Parameters | Use Case | | --------------------- | ----------------------------------------------------------- | ----------------------- | | `rememberForDesign` | license, userId, baseUri, sceneUri, renderTarget | General design creation | | `rememberForPhoto` | license, imageUri, imageSize, userId, baseUri, renderTarget | Photo editing | | `rememberForVideo` | license, userId, baseUri, sceneUri, renderTarget | Video editing | | `rememberForApparel` | license, userId, baseUri, sceneUri, renderTarget | T-shirt/apparel design | | `rememberForPostcard` | license, userId, baseUri, sceneUri, renderTarget | Postcard creation | | Editor Type | Use Case | Kotlin Implementation | | ---------------- | ----------------------------- | ------------------------- | | **PhotoEditor** | Photo editing and enhancement | `PhotoEditor` composable | | **DesignEditor** | Graphic design and templates | `DesignEditor` composable | | **VideoEditor** | Video editing and effects | `VideoEditor` composable | ### 4.2 Custom Configuration You can customize the editor behavior: ```kotlin title="EditorComposable.kt" @Composable fun CustomEditorComposable() { val engineConfiguration = EngineConfiguration.rememberForDesign( license = "", userId = "", // Add custom configuration here ) DesignEditor( engineConfiguration = engineConfiguration, onClose = { /* Handle close */ }, onExport = { result -> /* Handle export */ } ) } ``` ## Step 5: Troubleshooting ### Common Issues and Solutions #### Gradle Sync Issues - **Problem**: Repository not found - **Solution**: Verify the IMG.LY repository URL in `settings.gradle.kts` #### Compilation Errors - **Problem**: Missing dependencies - **Solution**: Ensure you've installed all Jetpack Compose dependencies. #### Runtime Errors - **Problem**: Invalid license - **Solution**: Verify your license key is correct and valid (or pass `null` for evaluation mode with watermark) #### Performance Issues - **Problem**: Slow editor loading - **Solution**: Check device specifications and memory usage #### Integration Issues - **Problem**: Editor not displaying - **Solution**: Verify the composable is properly called in your activity ## Step 6: Next Steps ### Advanced Features - Implement custom asset sources - Add custom filters and effects - Integrate with your backend services - Implement user authentication ### Other Integrations - Explore camera integration - Add video editing capabilities - Implement batch processing - Add cloud storage integration ### Production Considerations - Optimize for performance - Implement proper error handling - Add analytics and monitoring - Test on different device configurations ## Additional Resources - [CreativeEditor SDK Documentation](https://img.ly/docs/cesdk/android) - [Android Examples Repository](https://github.com/imgly/cesdk-android-examples) - [Jetpack Compose Documentation](https://developer.android.com/jetpack/compose) - [Android Development Guide](https://developer.android.com/guide) --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "MCP Server" description: "Connect AI assistants to CE.SDK documentation using the Model Context Protocol (MCP) server." platform: android url: "https://img.ly/docs/cesdk/android/get-started/mcp-server-fde71c/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) > [MCP Server](https://img.ly/docs/cesdk/android/get-started/mcp-server-fde71c/) --- The CE.SDK MCP server provides a standardized interface that allows any compatible AI assistant to search and access our documentation. This enables AI tools like Claude, Cursor, and VS Code Copilot to provide more accurate, context-aware help when working with CE.SDK. ## What is MCP? The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard that enables AI assistants to securely connect to external data sources. By connecting your AI tools to our MCP server, you get: - **Accurate answers**: AI assistants can search and retrieve the latest CE.SDK documentation - **Context-aware help**: Get platform-specific guidance for your development environment - **Up-to-date information**: Always access current documentation without relying on training data ## Available Tools The MCP server exposes two tools: | Tool | Description | |------|-------------| | `search` | Search documentation by query string | | `fetch` | Retrieve the full content of a document by ID | ## Server Endpoint | URL | Transport | |-----|-----------| | `https://mcp.img.ly/mcp` | Streamable HTTP | No authentication is required. ## Setup Instructions ### Claude Code Add the MCP server with a single command: ```bash claude mcp add --transport http imgly_docs https://mcp.img.ly/mcp ``` ### Claude Desktop 1. Open Claude Desktop and go to **Settings** (click your profile icon) 2. Navigate to **Connectors** in the sidebar 3. Click **Add custom connector** 4. Enter the URL: `https://mcp.img.ly/mcp` 5. Click **Add** to connect ### Cursor Add the following to your Cursor MCP configuration. You can use either: - **Project-specific**: `.cursor/mcp.json` in your project root - **Global**: `~/.cursor/mcp.json` ```json { "mcpServers": { "imgly_docs": { "url": "https://mcp.img.ly/mcp" } } } ``` ### VS Code Add to your workspace configuration at `.vscode/mcp.json`: ```json { "servers": { "imgly_docs": { "type": "http", "url": "https://mcp.img.ly/mcp" } } } ``` ### Windsurf Add the following to your Windsurf MCP configuration at `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "imgly_docs": { "serverUrl": "https://mcp.img.ly/mcp" } } } ``` ### Other Clients For other MCP-compatible clients, use the endpoint `https://mcp.img.ly/mcp` with HTTP transport. Refer to your client's documentation for the specific configuration format. ## Usage Once configured, your AI assistant will automatically have access to CE.SDK documentation. You can ask questions like: - "How do I add a text block in CE.SDK?" - "Show me how to export a design as PNG" - "What are the available blend modes?" The AI will search our documentation and provide answers based on the latest CE.SDK guides and API references. --- ## More Resources - **[Android Documentation Index](https://img.ly/docs/cesdk/android.md)** - Browse all Android documentation - **[Complete Documentation](https://img.ly/docs/cesdk/android/llms-full.txt)** - Full documentation in one file (for LLMs) - **[Web Documentation](https://img.ly/docs/cesdk/android/)** - Interactive documentation with examples - **[Support](mailto:support@img.ly)** - Contact IMG.LY support --- --- title: "New Project Setup" description: "Learn how to integrate the CreativeEditor SDK into a new Android Activity-based project" platform: android url: "https://img.ly/docs/cesdk/android/get-started/new-activity-based-ui-project-d7678j/" --- > This is one page of the CE.SDK Android documentation. For a complete overview, see the [Android Documentation Index](https://img.ly/docs/cesdk/android.md). For all docs in one file, see [llms-full.txt](https://img.ly/docs/cesdk/android/llms-full.txt). **Navigation:** [Get Started](https://img.ly/docs/cesdk/android/get-started/overview-e18f40/) > [Quickstart Activity](https://img.ly/docs/cesdk/android/get-started/new-activity-based-ui-project-d7678j/) --- This guide shows you how to integrate the CreativeEditor SDK into a **new Android Activity-based project**. Learn how to: - **create** a new project. - **add** the necessary dependencies. - **configure** the editor. - **test** the integration. ## Who Is This Guide For? This guide is for developers who: - Have experience with Android development and Kotlin - Want to create a new Android Activity-based project with integrated creative editing capabilities - Need to implement user-friendly editing interfaces - Want to add professional-grade image editing, design creation, and video editing to their Android apps - Prefer using traditional Android Views with Activity-based architecture ## What You'll Achieve By following this guide, you: - Create a new Android Activity-based project with CreativeEditor SDK integration - Configure platform-specific requirements for Android - Implement a functional editor that you can launch from your app - Test and verify the integration works correctly [Explore Android Demos](https://img.ly/showcases/cesdk/?tags=android) [View on GitHub](https://github.com/imgly/cesdk-android-examples) ## Prerequisites Before you begin, ensure you have the following requirements: ### Development Environment - **Android Studio**: Latest version (Hedgehog or later) - **Kotlin**: 1.9.10 or later - **Gradle**: 8.4 or later - **Git CLI** for version control ### Platform Requirements - **Android**: 7.0+ (API level 24+) - **Minimum SDK**: 24 - **Target SDK**: Latest stable version ### License - A valid **CE.SDK license key** ([Get a free trial](https://img.ly/forms/free-trial)), use `null` or an empty string to run in evaluation mode with watermark. ### Verify Your Setup Run the following command to verify your Android development environment: ```bash gradle --version ``` This command checks your Gradle installation and reports any issues to resolve before proceeding. > **Note:** You can customize the CreativeEditor SDK for Android through **native code > (Kotlin) only**, as described in the > [configuration overview section](https://img.ly/docs/cesdk/android/user-interface/customization-72b2f8/). ## Step 1: Create a New Android Activity-Based Project First, verify your Android Studio installation and create a new project: 1. **Open Android Studio** and select "New Project" 2. **Choose "Empty Views Activity"** template 3. **Configure your project**: - Name: `cesdk_android_activity_app` - Package name: `com.example.cesdkactivityapp` - Language: **Kotlin** - Minimum SDK: **API 24 (Android 7.0)** - Build configuration language: **Kotlin DSL** ### Project Structure Your new project should have this structure: ``` cesdk_android_activity_app/ ├── app/ # Main application module │ ├── src/main/ │ │ ├── java/ # Kotlin source files │ │ ├── res/ # Resources │ │ └── AndroidManifest.xml │ ├── build.gradle.kts # App-level build configuration │ └── proguard-rules.pro # ProGuard rules ├── gradle/ # Gradle wrapper ├── build.gradle.kts # Project-level build configuration ├── settings.gradle.kts # Project settings └── gradle.properties # Gradle properties ``` ## Step 2: Add the CreativeEditor SDK Dependency Add the CreativeEditor SDK to your project by updating the build configuration: ### 2.1 Add IMG.LY Repository Update your `settings.gradle.kts` to include the IMG.LY repository: ```kotlin title="settings.gradle.kts" dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() // Add CE.SDK dependency here maven { name = "IMG.LY Artifactory" url = uri("https://artifactory.img.ly/artifactory/maven") mavenContent { includeGroup("ly.img") } } } } ``` ### 2.2 Add Editor Dependency Update your `app/build.gradle.kts` to include the editor dependency: ```kotlin title="build.gradle.kts" dependencies { implementation("ly.img:editor:1.57.0") // Required dependencies for CE.SDK (includes Compose internally) implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.activity:activity-compose:1.8.2") implementation("androidx.compose.ui:ui:1.5.4") implementation("androidx.compose.material3:material3:1.1.2") // Other Android dependencies implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") implementation("com.google.android.material:material:1.11.0") } ``` ### 2.3 Configure Android Settings Ensure your `app/build.gradle.kts` has the correct Android configuration: ```kotlin title="build.gradle.kts" android { compileSdk = 34 defaultConfig { minSdk = 24 targetSdk = 34 } kotlinOptions { jvmTarget = "17" } packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } } ``` ### 2.4 Sync Project After adding the dependency, sync your project to download the CreativeEditor SDK: In Android Studio, click **Sync Project with Gradle Files**. This downloads and installs the CreativeEditor SDK and its dependencies automatically through Gradle. ## Step 3: Implement the Editor Integration ### 3.1 Create Editor Activity Create a new Kotlin file called `EditorActivity.kt` in your app module's main source set (typically `app/src/main/java/com/yourpackage/`): ```kotlin file=@cesdk_android_examples/editor-guides-quickstart/EditorActivity.kt import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import ly.img.editor.DesignEditor import ly.img.editor.EngineConfiguration import ly.img.editor.rememberForDesign class EditorActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val engineConfiguration = EngineConfiguration.rememberForDesign( // Get your license from https://img.ly/forms/free-trial // pass null or empty for evaluation mode with watermark license = "", userId = "", // A unique string to identify your user/session ) DesignEditor( engineConfiguration = engineConfiguration, onClose = { // Close the editor here finish() }, ) } } } ``` ### 3.2 Update MainActivity Modify your existing `MainActivity.kt` file (located in `app/src/main/java/com/yourpackage/`) to launch the editor: ```kotlin title="MainActivity.kt" import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.example.cesdkactivityapp.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.launchEditorButton.setOnClickListener { val intent = Intent(this, EditorActivity::class.java) startActivity(intent) } } } ``` ### 3.3 Update Layout File Update your `activity_main.xml` to include a button: ```xml title="activity_main.xml"