agentsclimarketplace

Bitmap and image optimization

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

Agent skills for profiling and optimizing mobile app performance (startup, memory, frame-rate, network).

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.

What its author says it does

Copied from the file, not written here

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

SKILL.md

5.8 KB, 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.

Keep looking

Skills are one crate of 328,083. 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.