### Image Format Enumeration and Properties Source: https://context7.com/t8rin/imagetoolbox/llms.txt Shows how to use the `ImageFormat` sealed class to represent supported output formats. Includes examples of selecting formats by MIME type, checking lossless properties, and accessing all supported formats. ```kotlin // Select format from MIME type string val format: ImageFormat = ImageFormat["image/avif"] // -> ImageFormat.Avif.Lossless // Check if format is lossless println(ImageFormat.Png.Lossless.isLossless) // true println(ImageFormat.Jxl.Lossy.isLossless) // false // Supported formats (all entries) val allFormats: List = ImageFormat.entries // [Jpg, Jpeg, MozJpeg, Jpegli, Png.Lossless, Png.Lossy, OxiPNG, // Bmp, Webp.Lossless, Webp.Lossy, Avif.Lossless, Avif.Lossy, // Heic.Lossless, Heic.Lossy, Heif.Lossless, Heif.Lossy, // Jxl.Lossless, Jxl.Lossy, Tif, Tiff, Jp2, J2k, Qoi, Ico, Gif] ``` -------------------------------- ### Clone ImageToolbox Repository Source: https://github.com/t8rin/imagetoolbox/blob/master/README.md Use this command to clone the ImageToolbox repository from GitHub. Ensure you have Git installed. ```bash git clone https://github.com/yourusername/ImageToolbox.git ``` -------------------------------- ### HashingType: Registering and Using Custom Hash Algorithms Source: https://context7.com/t8rin/imagetoolbox/llms.txt Shows how to register custom hashing providers and use built-in HashingType enums for file checksum computation. Custom providers must be registered once at process start. ```kotlin // Register custom providers once at app startup HashingType.registerSecurityMessageDigests( Security.getAlgorithms("MessageDigest").toList() ) // Use built-in types directly val md5 = HashingType.MD5 // digest = "MD5" val sha256 = HashingType.SHA_256 // digest = "SHA-256" // Retrieve all available algorithms val all: List = HashingType.entries // [MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, …(BouncyCastle algorithms)] // Look up by digest string val found: HashingType? = HashingType.fromString("SHA-512") // Compute a hash (actual digest logic lives in the data layer) // The ChecksumTools feature uses HashingType to calculate file checksums // via FileController.readBytes() and MessageDigest.getInstance(type.digest) ``` -------------------------------- ### Create and Manage Template Filters Source: https://context7.com/t8rin/imagetoolbox/llms.txt Defines how to create reusable `TemplateFilter` objects, which bundle multiple `Filter` instances. Includes examples of checking equality and saving templates. ```kotlin // Create a custom "Retro Film" template val retroFilm = TemplateFilter( name = "Retro Film", filters = listOf( SepiaFilterImpl(), SaturationFilterImpl(0.8f), GrainFilterImpl(value = 0.15f), VignetteFilterImpl(value = Triple(0.4f, 0.7f, ColorModel(0xFF000000))), ) ) // Check equality (order-independent, compares by class name + value) val another = TemplateFilter(name = "Retro Film", filters = listOf(SepiaFilterImpl())) println(retroFilm == another) // false — different filter count ``` ```kotlin // Use FilterParamsInteractor to persist/load templates // (injected interface backed by DataStore) suspend fun saveTemplate( interactor: FilterParamsInteractor, template: TemplateFilter ) { interactor.saveTemplateFilter(template) } ``` -------------------------------- ### Update Local Copy with Upstream Changes Source: https://github.com/t8rin/imagetoolbox/wiki/Contributing-Guidelines Update your local repository copy with the latest changes from the upstream repository before starting new work. Ensure you are on the correct branch. ```bash git remote update git checkout git rebase upstream/ ``` -------------------------------- ### Set Upstream Remote and Update Local Copy Source: https://github.com/t8rin/imagetoolbox/blob/master/CONTRIBUTING.md Add the original repository as an 'upstream' remote and update your local copy before starting work. This ensures you have the latest changes from the main project. ```bash cd git remote add upstream https://github.com// git remote -v # To the check the remotes for this repository ``` ```bash git remote update git checkout git rebase upstream/ ``` -------------------------------- ### SettingsProvider: Observing App Settings Source: https://context7.com/t8rin/imagetoolbox/llms.txt Demonstrates how to observe the current app settings using SettingsProvider. This is useful for reacting to changes in user preferences, such as theme or dynamic color settings. ```kotlin // Observe current settings (via SettingsProvider → Flow) settingsProvider.getSettingsState().collect { state: SettingsState -> println("Theme: ${state.nightMode}") // NightMode.System / Light / Dark println("Dynamic colors: ${state.isDynamicColors}") println("AMOLED: ${state.isAmoledMode}") println("Font: ${state.font}") println("Default format: ${state.defaultImageFormat}") println("Default scale: ${state.defaultImageScaleMode}") println("Save folder: ${state.saveFolderUri}") println("Presets: ${state.presets}") } ``` -------------------------------- ### Create and Switch to a New Branch Source: https://github.com/t8rin/imagetoolbox/wiki/Contributing-Guidelines Create a new branch for your work and switch to it. Name the branch descriptively to identify the issue it addresses. ```bash # It will create a new branch with name branch_name and switch to that branch git checkout -b branch_name ``` -------------------------------- ### Build ImageToolbox Project Source: https://github.com/t8rin/imagetoolbox/blob/master/README.md Build the ImageToolbox project using the Gradle wrapper. This command compiles the project and prepares it for execution. ```bash ./gradlew build ``` -------------------------------- ### Configure Image Compression Quality Source: https://context7.com/t8rin/imagetoolbox/llms.txt Illustrates how to configure specific quality settings for different image formats like JXL, AVIF, PNG, and TIFF. Demonstrates using `Quality` objects and coercing values into valid ranges. ```kotlin // Format-specific quality configuration val jxlQuality = Quality.Jxl( qualityValue = 85, // 1–100 effort = 5, // 1–10 (encoding speed vs. compression) speed = 2, // 0–4 channels = Quality.Channels.RGBA ) val avifQuality = Quality.Avif(qualityValue = 70, effort = 6) val pngLossy = Quality.PngLossy(maxColors = 256, compressionLevel = 7) val tiffQuality = Quality.Tiff(compressionScheme = 5) // LZW // Coerce quality values into valid ranges for the chosen format val coerced = jxlQuality.coerceIn(ImageFormat.Jxl.Lossy) ``` -------------------------------- ### Resize Images with ImageScaler Source: https://context7.com/t8rin/imagetoolbox/llms.txt Illustrates resizing images using the ImageScaler interface with various modes and algorithms. Supports explicit dimensions, flexible resizing while maintaining aspect ratio, center-cropping, and scaling down to safe display sizes. Requires an instance of ImageScaler. ```kotlin suspend fun resizeImage( imageScaler: ImageScaler, bitmap: Bitmap ): Bitmap { // Explicit resize to 1920×1080 using Lanczos3 in Oklab color space val resized = imageScaler.scaleImage( image = bitmap, width = 1920, height = 1080, resizeType = ResizeType.Explicit, imageScaleMode = ImageScaleMode.Lanczos3( scaleColorSpace = ScaleColorSpace.OklabSRGB ) ) // Flexible resize — keeps aspect ratio, fits within given bounds val flexible = imageScaler.scaleImage( image = bitmap, width = 800, height = 600, resizeType = ResizeType.Flexible(resizeAnchor = ResizeAnchor.Default) ) // Center-crop with blurred background fill val cropped = imageScaler.scaleImage( image = bitmap, width = 1080, height = 1080, resizeType = ResizeType.CenterCrop( canvasColor = 0xFF000000.toInt(), blurRadius = 35, scaleFactor = 1f, position = Position.Center ) ) // Scale down until the image can be displayed in memory safely return imageScaler.scaleUntilCanShow(bitmap) ?: bitmap } ``` -------------------------------- ### Clone the ImageToolbox Repository Source: https://github.com/t8rin/imagetoolbox/wiki/Contributing-Guidelines Clone your forked repository to your local machine. Replace `` and `` with your GitHub username and the repository name. ```bash git clone https://github.com// ``` -------------------------------- ### Run ImageToolbox Application Source: https://github.com/t8rin/imagetoolbox/blob/master/README.md Execute the ImageToolbox application after building it. This command uses the Gradle wrapper to run the application. ```bash ./gradlew run ``` -------------------------------- ### Add Upstream Remote and Check Remotes Source: https://github.com/t8rin/imagetoolbox/wiki/Contributing-Guidelines Add a reference to the original project as 'upstream' and verify the configured remotes. Replace `` and `` with the original owner's username and the repository name. ```bash cd git remote add upstream https://github.com// git remote -v # To the check the remotes for this repository ``` -------------------------------- ### Create and Use Resize Presets Source: https://context7.com/t8rin/imagetoolbox/llms.txt Define and use named presets for common image resizing operations. Presets can be serialized to strings for persistence and parsed back into lists. ```kotlin // Percentage-based scaling (relative to original) val half = Preset.Percentage(50) val quarter = Preset.Percentage(25) val original = Preset.Original // Percentage(100) // Fixed aspect ratio preset val widescreen = Preset.AspectRatio(ratio = 16f / 9f, isFit = false) // Telegram sticker preset (512×512 max, lossless PNG) val telegram = Preset.Telegram // Serialize to string for DataStore persistence val str = half.asString() // "50%" val ratioStr = widescreen.asString() // "ratio(1.7777778)" // Parse list from stored settings string val presets: List? = Preset.createListFromInts("25*50*75*100") // -> [Percentage(25), Percentage(50), Percentage(75), Percentage(100)] // Apply preset via ImageTransformer suspend fun applyPreset( transformer: ImageTransformer, bitmap: Bitmap, currentInfo: ImageInfo ): ImageInfo { return transformer.applyPresetBy( image = bitmap, preset = Preset.Percentage(50), currentInfo = currentInfo ) } ``` -------------------------------- ### Load Images with ImageGetter Source: https://context7.com/t8rin/imagetoolbox/llms.txt Demonstrates loading images from various sources using the ImageGetter interface. Supports full-resolution loading, thumbnail generation, and applying transformations during decoding. Ensure ImageGetter is injected via Hilt. ```kotlin class MyComponent @AssistedInject constructor( private val imageGetter: ImageGetter ) { // Load full-resolution image with metadata suspend fun loadImage(uri: String): ImageData? { return imageGetter.getImage( uri = uri, originalSize = true, onFailure = { throwable -> Log.e("IMG", "Load failed", throwable) } ) // Returns ImageData containing: // .image -> Bitmap // .imageInfo -> ImageInfo (width, height, format, quality, …) // .metadata -> Exif Metadata? } // Load a thumbnail (down-sampled to fit within 512 px) suspend fun loadThumbnail(uri: String): Bitmap? { return imageGetter.getImage(data = uri, size = 512) } // Load with applied filter transformations suspend fun loadWithFilters( uri: String, filters: List> ): ImageData? { return imageGetter.getImageWithTransformations( uri = uri, transformations = filters, originalSize = false ) } } ``` -------------------------------- ### Share and Cache Images with ImageShareProvider Source: https://context7.com/t8rin/imagetoolbox/llms.txt Utilize ImageShareProvider to share single images via the system share sheet, cache images to temporary directories for previews, and share multiple URIs with progress tracking. Provide an imageLoader for loading images from URIs when sharing batches. ```kotlin suspend fun shareImage( shareProvider: ImageShareProvider, bitmap: Bitmap, info: ImageInfo ) { // Share a single image via the system share sheet shareProvider.shareImage( imageInfo = info, image = bitmap, onComplete = { println("Share dialog dismissed") } ) // Cache an image and get its content URI for use in other apps val cachedUri: String? = shareProvider.cacheImage( image = bitmap, imageInfo = info, filename = "preview_output" ) // cachedUri -> "content://com.t8rin.imagetoolbox.fileprovider/cache/preview_output.jpg" // Share a list of URIs with progress tracking shareProvider.shareImages( uris = listOf("content://uri1", "content://uri2", "content://uri3"), imageLoader = { uri -> val data = imageGetter.getImage(uri) data?.let { Pair(it.image, it.imageInfo) } }, onProgressChange = { progress -> println("Sharing: $progress%") } ) } ``` -------------------------------- ### PdfManager: Comprehensive PDF Manipulation Source: https://context7.com/t8rin/imagetoolbox/llms.txt Demonstrates various PDF operations using the PdfManager API, including creation from images, page extraction, merging, rotation, protection, compression, watermarking, creating searchable PDFs, and metadata editing. Ensure PdfManager is properly initialized before use. ```kotlin suspend fun workWithPdfs(pdfManager: PdfManager) { // Convert images to PDF val pdfUri: String = pdfManager.createPdf( imageUris = listOf("content://uri1.jpg", "content://uri2.png"), params = PdfCreationParams(quality = 90) ) // Extract all pages as images (streaming) pdfManager.extractPages( uri = pdfUri, params = PdfExtractPagesParams(pages = null) // null = all pages ).collect { when (it) { is ExtractPagesAction.Complete -> println("Done: ${it.resultUri}") is ExtractPagesAction.Page -> println("Page ${it.pageIndex}: ${it.uri}") } } // Merge multiple PDFs into one val merged: String = pdfManager.mergePdfs( uris = listOf("content://doc1.pdf", "content://doc2.pdf") ) // Rotate specific pages (indices 0-based, degrees: 0,90,180,270) val rotated: String = pdfManager.rotatePdf( uri = pdfUri, rotations = listOf(0, 90, 0, 270) // one angle per page ) // Password-protect a PDF val protected: String = pdfManager.protectPdf(uri = pdfUri, password = "s3cr3t") // Compress PDF (quality 0.0–1.0) val compressed: String = pdfManager.compressPdf(uri = pdfUri, quality = 0.7f) // Add watermark val watermarked: String = pdfManager.addWatermark( uri = pdfUri, params = PdfWatermarkParams(/* text, opacity, position, … */) ) // OCR: create searchable PDF from recognized-text pages val searchable: String = pdfManager.createSearchablePdf( pages = listOf( SearchablePdfPage(imageUri = "content://scan.jpg", recognizedText = "Hello World") ) ) // Get and update metadata val meta: PdfMetadata = pdfManager.getPdfMetadata(uri = pdfUri) pdfManager.changePdfMetadata( uri = pdfUri, metadata = meta.copy(author = "T8RIN", title = "My Document") ) } ``` -------------------------------- ### Define Resize Strategies with ResizeType Source: https://context7.com/t8rin/imagetoolbox/llms.txt Configure image resizing behavior using ResizeType. Choose from exact pixel dimensions, fitting within bounds while preserving aspect ratio, center-cropping with customizable backgrounds, or fitting with letterboxing. These types integrate with ImageScaler. ```kotlin // Exact pixel dimensions (ignores aspect ratio) val explicit = ResizeType.Explicit ``` ```kotlin // Fit within bounds while preserving aspect ratio val flexible = ResizeType.Flexible(resizeAnchor = ResizeAnchor.Default) ``` ```kotlin // Center-crop to exact output size with configurable background val centerCrop = ResizeType.CenterCrop( canvasColor = null, // null = transparent, 0xFF… = solid fill blurRadius = 40, // blur radius for background fill originalSize = IntegerSize(4000, 3000), scaleFactor = 1.05f, // slight overscale before crop position = Position.Center ) ``` ```kotlin // Fit image inside canvas with letterboxing/pillarboxing val fit = ResizeType.Fit( canvasColor = 0xFFFFFFFF.toInt(), blurRadius = 0, position = Position.Center ) ``` ```kotlin // Preserve original size during crop operations val withOriginal = centerCrop.withOriginalSizeIfCrop(IntegerSize(4000, 3000)) ``` -------------------------------- ### Implementations of Image Filters Source: https://context7.com/t8rin/imagetoolbox/llms.txt Demonstrates various implementations of the `Filter` interface for different image manipulation tasks. Each filter has a specific `Value` type and `isVisible` property. ```kotlin class SepiaFilterImpl : Filter.Sepia { override val value: Unit = Unit override val isVisible: Boolean = true } ``` ```kotlin class SaturationFilterImpl(saturation: Float = 1.0f) : Filter.Saturation { override val value: Float = saturation override val isVisible: Boolean = true } ``` ```kotlin class GaussianBlurFilterImpl : Filter.GaussianBlur { override val value: Triple = Triple(10f, 2.0f, BlurEdgeMode.Clamp) override val isVisible: Boolean = true } ``` ```kotlin class ClaheFilterImpl : Filter.Clahe { override val value: Triple = Triple(3.0f, 8f, 8f) // clipLimit, gridW, gridH override val isVisible: Boolean = true } ``` ```kotlin class GlitchFilterImpl : Filter.EnhancedGlitch { override val value: GlitchParams = GlitchParams( channelsShiftX = -0.08f, channelsShiftY = -0.08f, corruptionSize = 0.01f, corruptionCount = 60, corruptionShiftX = -0.05f, corruptionShiftY = 0f ) override val isVisible: Boolean = true } ``` ```kotlin class ToneCurvesFilterImpl : Filter.ToneCurves { override val value: ToneCurvesParams = ToneCurvesParams.Default override val isVisible: Boolean = true } ``` -------------------------------- ### Stage Changes for Commit Source: https://github.com/t8rin/imagetoolbox/wiki/Contributing-Guidelines Add modified files to the staging area. Use `git add .` to add all changes or `git add ` to add specific files. ```bash # To add all new files to branch Branch_Name git add . # To add only a few files to Branch_Name git add ``` -------------------------------- ### ImageScaler Interface Source: https://context7.com/t8rin/imagetoolbox/llms.txt The ImageScaler interface provides functionality to resize images with fine-grained control over algorithms and color spaces. It supports explicit resizing, flexible resizing that maintains aspect ratio, and center-cropping. ```APIDOC ## ImageScaler - Resize Images ### Description Scales images to exact or adaptive dimensions using a wide range of resampling algorithms and color spaces. Supports explicit dimensions, flexible fitting, and center-cropping. ### Methods - `scaleImage(image: Bitmap, width: Int, height: Int, resizeType: ResizeType, imageScaleMode: ImageScaleMode): Bitmap` - `scaleUntilCanShow(bitmap: Bitmap): Bitmap?` ### Parameters #### `ResizeType` Enum - `Explicit(width: Int, height: Int)`: Resizes to exact dimensions. - `Flexible(resizeAnchor: ResizeAnchor)`: Resizes while maintaining aspect ratio to fit within bounds. - `CenterCrop(canvasColor: Int, blurRadius: Int, scaleFactor: Float, position: Position)`: Crops the image to a square and fills the background. #### `ImageScaleMode` Enum - `Lanczos3(scaleColorSpace: ScaleColorSpace)`: Uses Lanczos3 resampling algorithm. ### Usage Example ```kotlin suspend fun resizeImage( imageScaler: ImageScaler, bitmap: Bitmap ): Bitmap { // Explicit resize to 1920×1080 using Lanczos3 in Oklab color space val resized = imageScaler.scaleImage( image = bitmap, width = 1920, height = 1080, resizeType = ResizeType.Explicit, imageScaleMode = ImageScaleMode.Lanczos3( scaleColorSpace = ScaleColorSpace.OklabSRGB ) ) // Flexible resize — keeps aspect ratio, fits within given bounds val flexible = imageScaler.scaleImage( image = bitmap, width = 800, height = 600, resizeType = ResizeType.Flexible(resizeAnchor = ResizeAnchor.Default) ) // Center-crop with blurred background fill val cropped = imageScaler.scaleImage( image = bitmap, width = 1080, height = 1080, resizeType = ResizeType.CenterCrop( canvasColor = 0xFF000000.toInt(), blurRadius = 35, scaleFactor = 1f, position = Position.Center ) ) // Scale down until the image can be displayed in memory safely return imageScaler.scaleUntilCanShow(bitmap) ?: bitmap } ``` ``` -------------------------------- ### Apply Filters to Bitmap Source: https://context7.com/t8rin/imagetoolbox/llms.txt A utility function to apply a list of filters to a bitmap using a `FilterProvider`. Requires a `FilterProvider` and a list of `Filter` objects. ```kotlin fun applyFilters( filterProvider: FilterProvider, filters: List> ): List> { return filters.map { filterProvider.filterToTransformation(it) } } ``` -------------------------------- ### ImageGetter Interface Source: https://context7.com/t8rin/imagetoolbox/llms.txt The ImageGetter interface is used for decoding images from various sources like URIs or assets into a usable Bitmap format. It supports loading full-resolution images, thumbnails, and images with applied transformations. ```APIDOC ## ImageGetter - Load Images ### Description Loads images from any URI or data source into a typed image representation (typically Bitmap). Supports synchronous and asynchronous loading, optional transformations at decode time, and size constraints. ### Methods - `getImage(uri: String, originalSize: Boolean, onFailure: (Throwable) -> Unit): ImageData?` - `getImage(data: String, size: Int): Bitmap?` - `getImageWithTransformations(uri: String, transformations: List>, originalSize: Boolean): ImageData?` ### Usage Example ```kotlin // Injected via Hilt into any component class MyComponent @AssistedInject constructor( private val imageGetter: ImageGetter ) { // Load full-resolution image with metadata suspend fun loadImage(uri: String): ImageData? { return imageGetter.getImage( uri = uri, originalSize = true, onFailure = { throwable -> Log.e("IMG", "Load failed", throwable) } ) // Returns ImageData containing: // .image -> Bitmap // .imageInfo -> ImageInfo (width, height, format, quality, …) // .metadata -> Exif Metadata? } // Load a thumbnail (down-sampled to fit within 512 px) suspend fun loadThumbnail(uri: String): Bitmap? { return imageGetter.getImage(data = uri, size = 512) } // Load with applied filter transformations suspend fun loadWithFilters( uri: String, filters: List> ): ImageData? { return imageGetter.getImageWithTransformations( uri = uri, transformations = filters, originalSize = false ) } } ``` ``` -------------------------------- ### Configure Drawing Modes and Path Modes Source: https://context7.com/t8rin/imagetoolbox/llms.txt Select active brush tool types using DrawMode and geometric or freehand styles with DrawPathMode. DrawLineStyle can be used to decorate stroke rendering. ```kotlin // Available DrawMode types val pen = DrawMode.Pen val neon = DrawMode.Neon val highlighter = DrawMode.Highlighter val privacyBlur = DrawMode.PathEffect.PrivacyBlur(blurRadius = 25) val pixelation = DrawMode.PathEffect.Pixelation(pixelSize = 40f) val filterBrush = DrawMode.PathEffect.Custom(filter = saturationFilter) val textMode = DrawMode.Text( text = "© 2024", isRepeated = true, repeatingInterval = Pt(30f) ) val imageBrush = DrawMode.Image( imageData = "content://media/images/stamp.png", repeatingInterval = Pt(50f) ) val spotHeal = DrawMode.SpotHeal(mode = SpotHealMode.OpenCV) val warp = DrawMode.Warp(warpMode = WarpMode.MOVE, strength = 0.3f, hardness = 0.6f) // DrawPathMode selects path geometry val freeDraw = DrawPathMode.Free val line = DrawPathMode.Line val arrow = DrawPathMode.PointingArrow(sizeScale = 3f, angle = 150f) val rect = DrawPathMode.OutlinedRect( rotationDegrees = 45, cornerRadius = 12f, fillColor = ColorModel(0x4400FF00) // semi-transparent green fill ) val star = DrawPathMode.OutlinedStar( vertices = 5, innerRadiusRatio = 0.45f, fillColor = null // outline only ) val floodFill = DrawPathMode.FloodFill(tolerance = 0.15f) val spray = DrawPathMode.Spray(density = 80, pixelSize = 2f, isSquareShaped = false) // DrawLineStyle decorates stroke rendering val dashed = DrawLineStyle.Dashed(size = 10.pt, gap = 20.pt) val zigzag = DrawLineStyle.ZigZag(heightRatio = 4f) val dotDashed = DrawLineStyle.DotDashed ``` -------------------------------- ### SettingsInteractor: Modifying App Settings Source: https://context7.com/t8rin/imagetoolbox/llms.txt Shows how to modify persistent app settings using the SettingsInteractor. Ensure the interactor is properly initialized and that DataStore is available for persistence. ```kotlin suspend fun configureApp(interactor: SettingsInteractor) { interactor.setNightMode(NightMode.Dark) interactor.toggleAmoledMode() interactor.setPresets(listOf(25, 50, 75, 100)) interactor.setSaveFolderUri("/storage/emulated/0/ImageToolbox") interactor.setFilenamePrefix("edited_") interactor.toggleAddSequenceNumber() interactor.setBorderWidth(2f) interactor.setDefaultImageScaleMode(ImageScaleMode.Lanczos3()) interactor.setDefaultImageFormat(ImageFormat.Png.Lossless) interactor.toggleDynamicColors() interactor.setColorTuple("FF6200EE*FF03DAC5*FFFFFFFF*FF000000") } ``` -------------------------------- ### Save and Manage Files with FileController Source: https://context7.com/t8rin/imagetoolbox/llms.txt Use FileController to save processed images and arbitrary bytes to the Android file system, read cache, manage output directories, and copy EXIF metadata. Ensure proper error handling for permission issues and exceptions. ```kotlin suspend fun saveProcessedImage( fileController: FileController, bitmap: Bitmap, imageInfo: ImageInfo, compressor: ImageCompressor ) { // Encode bitmap val bytes = compressor.compressAndTransform(bitmap, imageInfo) // Build a save target val saveTarget = ImageSaveTarget( imageInfo = imageInfo, originalUri = "content://media/external/images/1234", sequenceNumber = 1, data = bytes ) // Save with EXIF preservation val result: SaveResult = fileController.save( saveTarget = saveTarget, keepOriginalMetadata = true, oneTimeSaveLocationUri = null // null → use app default folder ) when (result) { is SaveResult.Success -> println("Saved to ${result.savingPath}") is SaveResult.Error.MissingPermissions -> println("Grant storage permission") is SaveResult.Error.Exception -> println("Error: ${result.throwable.message}") is SaveResult.Skipped -> println("Skipped (duplicate)") } // Read raw bytes back val raw: ByteArray = fileController.readBytes("content://…/saved.avif") // Cache management val cacheBytes: Long = fileController.getCacheSize() fileController.clearCache { freed -> println("Freed ${freed / 1024} KB") } } ```