CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-compose-components--components-resources

Resource management library for Compose Multiplatform applications providing type-safe access to images, strings, fonts, and drawable assets across all platforms.

Pending
Overview
Eval results
Files

image-drawable-resources.mddocs/

Image and Drawable Resources

Image and drawable resources provide type-safe access to visual assets including raster images, vector graphics, and SVG files. The library automatically handles format detection, density-aware scaling, and platform-specific optimizations.

Core Types

class DrawableResource(id: String, items: Set<ResourceItem>) : Resource(id, items)

A drawable resource represents any visual asset that can be rendered as an image, vector, or painter. This includes PNG, JPEG, WebP, XML vectors, and SVG files.

Composable Functions

Automatic Painter Creation

@Composable
fun painterResource(resource: DrawableResource): Painter

Creates a Painter from a drawable resource with automatic format detection. The function selects the appropriate painter type based on the file extension:

  • .xml files → Vector painter
  • .svg files → SVG painter
  • Other formats → Bitmap painter

Usage:

@Composable
fun AppIcon() {
    val icon = painterResource(Res.drawable.app_icon)
    Image(
        painter = icon,
        contentDescription = "Application Icon",
        modifier = Modifier.size(48.dp)
    )
}

Bitmap Image Loading

@Composable
fun imageResource(resource: DrawableResource): ImageBitmap

Loads a raster image as an ImageBitmap with automatic density scaling. The function selects the best density variant and scales appropriately for the current screen.

Usage:

@Composable
fun ProfilePhoto(user: User) {
    val photo = imageResource(Res.drawable.default_profile)
    Image(
        bitmap = photo,
        contentDescription = "Profile Photo",
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .size(80.dp)
            .clip(CircleShape)
    )
}

Density Handling: The library automatically selects the best image density variant:

  1. Exact or higher density match (preferred)
  2. Lower density with upscaling
  3. Default (MDPI) with scaling
  4. LDPI as last resort

Vector Graphics Loading

@Composable
fun vectorResource(resource: DrawableResource): ImageVector

Loads XML vector graphics as ImageVector objects. Supports Android Vector Drawable format with full path, group, and gradient support.

Usage:

@Composable
fun NavigationIcon(isSelected: Boolean) {
    val icon = vectorResource(
        if (isSelected) Res.drawable.ic_home_filled 
        else Res.drawable.ic_home_outline
    )
    Icon(
        imageVector = icon,
        contentDescription = "Home",
        tint = if (isSelected) MaterialTheme.colors.primary 
               else MaterialTheme.colors.onSurface
    )
}

Suspend Functions

Raw Resource Bytes

suspend fun getDrawableResourceBytes(
    environment: ResourceEnvironment,
    resource: DrawableResource
): ByteArray

Retrieves the raw byte content of a drawable resource. Useful for custom processing, external libraries, or passing to platform-specific APIs.

Parameters:

  • environment - Resource environment for variant selection
  • resource - The drawable resource to load

Usage:

suspend fun uploadImageToServer(imageResource: DrawableResource) {
    val environment = getSystemResourceEnvironment()
    val imageBytes = getDrawableResourceBytes(environment, imageResource)
    
    // Upload bytes to server
    uploadService.uploadImage(imageBytes)
}

suspend fun saveImageToFile(imageResource: DrawableResource, outputPath: String) {
    val environment = getSystemResourceEnvironment()
    val bytes = getDrawableResourceBytes(environment, imageResource)
    File(outputPath).writeBytes(bytes)
}

Resource Variants and Selection

Density Qualifiers

The library supports multiple density variants for optimal display quality:

drawable/               # Default (MDPI - 160dpi)
drawable-ldpi/         # Low density (120dpi)
drawable-hdpi/         # High density (240dpi)  
drawable-xhdpi/        # Extra high density (320dpi)
drawable-xxhdpi/       # Extra extra high density (480dpi)
drawable-xxxhdpi/      # Extra extra extra high density (640dpi)

Selection Algorithm:

  1. Find exact or next higher density
  2. Scale down if necessary (preferred)
  3. Fall back to lower density with upscaling
  4. Use default (MDPI) if no specific density available

Theme Variants

Support for light and dark theme variants:

drawable/              # Default/light theme
drawable-night/        # Dark theme variants

Usage:

@Composable
fun ThemedIcon() {
    // Automatically selects light/dark variant based on system theme
    val icon = painterResource(Res.drawable.themed_icon)
    Icon(imageVector = icon, contentDescription = "Themed Icon")  
}

Supported Formats

Raster Images

  • PNG - Full transparency support, recommended for icons
  • JPEG - Efficient for photos, no transparency
  • WebP - Modern format with excellent compression

Vector Graphics

  • XML Vector Drawable - Android's vector format with paths, groups, gradients
  • SVG - Scalable Vector Graphics with broad compatibility

Format Detection

The library automatically detects format based on file extension:

  • .png, .jpg, .jpeg, .webp → Bitmap handling
  • .xml → Vector drawable parsing
  • .svg → SVG parsing and rendering

Performance Considerations

Image Caching

The library includes automatic caching to improve performance:

// Images are cached by path and density
// Cache key format: "path-{density}dpi"
val cachedImage = imageResource(Res.drawable.large_image) // Cached on first load

Memory Management

Best Practices:

  1. Use appropriate image densities to avoid unnecessary memory usage
  2. Consider WebP format for better compression
  3. Use vector graphics for simple icons and illustrations
  4. Lazy load images in lists and grids

Cache Control

// For testing or memory management, cache can be cleared
// Note: This is internal API
internal fun dropImageCache()

Platform-Specific Behavior

Android

  • Native bitmap decoding with hardware acceleration
  • Automatic density selection based on screen density
  • Vector drawable native support

Desktop (JVM)

  • Skia-based rendering for consistent appearance
  • Density simulation for high-DPI displays
  • SVG support through integrated parser

iOS

  • Core Graphics integration for optimal performance
  • Retina display support with automatic scaling
  • Memory-efficient image loading

Web (JS/Wasm)

  • Canvas-based rendering for vectors
  • Efficient image loading with browser caching
  • Progressive loading for better user experience

Error Handling

Common Exceptions:

  • MissingResourceException - Resource file not found
  • IllegalArgumentException - Invalid resource ID
  • Rendering errors for corrupted or unsupported image formats

Example Error Handling:

@Composable
fun SafeImageLoad(resourceId: DrawableResource) {
    try {
        val image = painterResource(resourceId)
        Image(painter = image, contentDescription = null)
    } catch (e: MissingResourceException) {
        // Show placeholder or error state
        Icon(Icons.Default.ImageNotSupported, contentDescription = "Image not found")
    }
}

Best Practices

  1. Provide multiple density variants for raster images:

    drawable-mdpi/icon.png    (48x48)
    drawable-hdpi/icon.png    (72x72) 
    drawable-xhdpi/icon.png   (96x96)
    drawable-xxhdpi/icon.png  (144x144)
  2. Use vector drawables for simple graphics:

    • Icons, simple illustrations
    • Graphics that need to scale without quality loss
    • UI elements with solid colors
  3. Use raster images for complex graphics:

    • Photographs
    • Complex illustrations with gradients
    • Images with many colors and details
  4. Optimize image sizes for target platforms:

    • Consider platform-specific limitations
    • Use appropriate compression settings
    • Test on actual devices for performance
  5. Provide theme variants when appropriate:

    // Use same resource ID, library selects theme variant automatically
    val icon = painterResource(Res.drawable.app_icon) // Auto light/dark

Image Decoder Extensions

The library provides extension functions for decoding raw image bytes into Compose graphics objects.

Bitmap Decoder

fun ByteArray.decodeToImageBitmap(): ImageBitmap

Decodes a byte array of a bitmap to an ImageBitmap. Supports JPEG, PNG, BMP, WEBP formats. Different platforms may support additional formats.

Usage:

suspend fun loadExternalImage(imageUrl: String): ImageBitmap {
    val imageBytes = httpClient.get(imageUrl).readBytes()
    return imageBytes.decodeToImageBitmap()
}

suspend fun processImageResource(): ImageBitmap {
    val environment = getSystemResourceEnvironment()
    val rawBytes = getDrawableResourceBytes(environment, Res.drawable.photo)
    return rawBytes.decodeToImageBitmap()
}

Vector Decoder

fun ByteArray.decodeToImageVector(density: Density): ImageVector

Decodes a byte array of a vector XML file to an ImageVector with the specified density for unit conversion.

Parameters:

  • density - Density to apply during converting the source units to ImageVector units

Usage:

@Composable
fun CustomVectorProcessor() {
    val density = LocalDensity.current
    
    LaunchedEffect(Unit) {
        val environment = getSystemResourceEnvironment()
        val vectorBytes = getDrawableResourceBytes(environment, Res.drawable.vector_icon)
        val processedVector = vectorBytes.decodeToImageVector(density)
        
        // Further processing of the ImageVector
        customVectorProcessor.process(processedVector)
    }
}

suspend fun convertVectorResource(
    resource: DrawableResource,
    targetDensity: Density
): ImageVector {
    val environment = getSystemResourceEnvironment()
    val bytes = getDrawableResourceBytes(environment, resource)
    return bytes.decodeToImageVector(targetDensity)
}

Decoder Use Cases

Custom Image Processing:

suspend fun applyImageFilters(resource: DrawableResource): ImageBitmap {
    val environment = getSystemResourceEnvironment()
    val rawBytes = getDrawableResourceBytes(environment, resource)
    val originalBitmap = rawBytes.decodeToImageBitmap()
    
    return imageProcessor.applyFilters(originalBitmap)
}

Dynamic Vector Manipulation:

@Composable
fun DynamicVectorIcon(resource: DrawableResource, color: Color) {
    val density = LocalDensity.current
    var processedVector by remember { mutableStateOf<ImageVector?>(null) }
    
    LaunchedEffect(resource, color) {
        val environment = getSystemResourceEnvironment()
        val bytes = getDrawableResourceBytes(environment, resource)
        val vector = bytes.decodeToImageVector(density)
        
        // Apply dynamic color changes
        processedVector = vectorColorProcessor.changeColor(vector, color)
    }
    
    processedVector?.let { vector ->
        Icon(imageVector = vector, contentDescription = null)
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-compose-components--components-resources

docs

font-resources.md

image-drawable-resources.md

index.md

plural-string-resources.md

resource-environment.md

string-array-resources.md

string-resources.md

tile.json