agentsclimarketplace

Bitmap and image optimization

Skill almasumdev/awesome-mobile-performance-agent-skills/.github/skills/memory/bitmap-and-image-optimization

Downsample, cache, and choose the right image format (AVIF, HEIC, WebP) to cut memory, network, and decode time on iOS and Android.From its SKILL.md

Install
npx -y skills add almasumdev/awesome-mobile-performance-agent-skills --skill bitmap-and-image-optimization

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.8 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it

Bitmap and Image Optimization

Instructions

Images dominate mobile memory and bandwidth. A single 4000×3000 JPEG decoded naively takes 48 MB in RAM (4000 × 3000 × 4 bytes). This skill covers sizing, caching, and format selection.

1. Size Images to the Display Target, Not the Source

Decode to the on-screen pixel size, not the full resolution. The screen does not care about extra pixels.

Android (Glide):

Glide.with(view)
    .load(url)
    .override(view.width, view.height)     // decode to target size
    .format(DecodeFormat.PREFER_RGB_565)    // for opaque images
    .into(view)

Android (Coil):

imageView.load(url) {
    size(ViewSizeResolver(imageView))       // matches the view
    allowRgb565(true)
}

iOS (SDWebImage):

imageView.sd_setImage(
    with: URL(string: url),
    placeholderImage: placeholder,
    context: [.imageThumbnailPixelSize: CGSize(width: 512, height: 512)]
)

iOS native UIImage downsample:

func downsample(url: URL, to size: CGSize, scale: CGFloat) -> UIImage? {
    let opts: [CFString: Any] = [kCGImageSourceShouldCache: false]
    guard let src = CGImageSourceCreateWithURL(url as CFURL, opts as CFDictionary) else { return nil }
    let maxDim = max(size.width, size.height) * scale
    let thumbOpts: [CFString: Any] = [
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceShouldCacheImmediately: true,
        kCGImageSourceCreateThumbnailWithTransform: true,
        kCGImageSourceThumbnailMaxPixelSize: maxDim,
    ]
    return CGImageSourceCreateThumbnailAtIndex(src, 0, thumbOpts as CFDictionary).map { UIImage(cgImage: $0) }
}

Flutter:

CachedNetworkImage(
  imageUrl: url,
  memCacheWidth: 512,     // decode at target size
  memCacheHeight: 512,
  placeholder: (c, _) => const ColoredBox(color: Color(0xffeeeeee)),
)

React Native — prefer FastImage or Expo Image; they downsample natively. Set resizeMode and explicit dimensions.

2. Format Selection (2026 defaults)

Use caseBest formatWhy
Photos over networkAVIF30–50% smaller than JPEG; wide OS support (Android 12+, iOS 16+).
Fallback for AVIFWebPUniversal, better than JPEG/PNG, lossy or lossless.
Apple-first pipelinesHEICNative to iOS; smaller than JPEG.
Illustrations / iconsSVG or vector drawableScale for free; typically tiny.
Flat UI with transparencyWebP lossless or PNG-8Smaller than PNG-24.
AnimationsAVIF sequence / AVIS or animated WebPReplace GIFs.

Content-Negotiation from your CDN:

Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8

Return AVIF when Accept contains image/avif. Emit Vary: Accept.

3. Bitmap Config (Android)

  • Use RGB_565 (2 bytes/px) for opaque photos on low-end devices — halves RAM vs ARGB_8888.
  • Use HARDWARE bitmaps on API 26+ — zero-copy to GPU, but cannot be read back on CPU.
BitmapFactory.Options().apply {
    inPreferredConfig = Bitmap.Config.HARDWARE  // API 26+
    inSampleSize = calculateInSampleSize(width, height, targetW, targetH)
}

4. Caching

Every serious loader has a memory cache + disk cache. Size them generously:

  • Memory cache: 20–25% of Runtime.getRuntime().maxMemory() (Android) or 50 MB cap (iOS).
  • Disk cache: 128–256 MB; keyed by final transformed URL so variant decoding is hit, not recomputed.

Coil example:

ImageLoader.Builder(context)
    .memoryCache { MemoryCache.Builder(context).maxSizePercent(0.25).build() }
    .diskCache { DiskCache.Builder().directory(context.cacheDir.resolve("img")).maxSizeBytes(256L * 1024 * 1024).build() }
    .respectCacheHeaders(true)
    .build()

5. Preload Critical Images

For hero images on the next screen, preload before navigation:

imageLoader.enqueue(ImageRequest.Builder(ctx).data(heroUrl).build())

In SwiftUI use ImageRenderer or AsyncImage with .task { await prefetch() }. In Flutter use precacheImage.

6. Avoid Common Footguns

  • Do not decode images on the UI/main thread. All major loaders do this for you; do not reimplement.
  • Do not store bitmaps in StateFlow / @State. Store identifiers; let the loader return bitmaps per view.
  • Do not apply BackdropFilter / blur over large images; blur once into a smaller buffer, then upscale.
  • Placeholder-shimmer components should be cheap — Modifier.background or a single gradient.

7. Verify

  • Android: adb shell dumpsys meminfo --package <pkg> | findstr Graphics shows GPU texture memory.
  • iOS: Debug → Rendering → Color Misaligned Images (a misaligned image reports a yellow tint = slow path).
  • Flutter: DevTools → Memory → filter by Image and ImageCache.

8. Production Metrics

Log per image: URL, source (cache/network), decoded width/height, decode time. A p95 > 40 ms or source=network > 50% on warm scrolls is a regression.

Checklist

  • Images decoded at view/display size, not source size.
  • Opaque photos use RGB_565 (Android) / thumbnail downsample (iOS).
  • AVIF or WebP served with Accept content negotiation; JPEG/PNG only as fallback.
  • Memory cache sized to 20–25% of heap; disk cache 128–256 MB.
  • Hero images preloaded before navigation.
  • No image decoding on main/UI thread.
  • CI dashboard tracks p95 image decode time and bytes/screen.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.