Skip to content

Instantly share code, notes, and snippets.

@frel
Last active August 15, 2021 10:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frel/1cbb5404f33db69e0b91fda18feb45a2 to your computer and use it in GitHub Desktop.
Save frel/1cbb5404f33db69e0b91fda18feb45a2 to your computer and use it in GitHub Desktop.
AirBrush Medium article example
class TinyThumbDecoder(
private var context: Context,
private val bitmapPool: BitmapPool,
private val blurProvider: (source: Bitmap, options: BlurOptions?) -> Bitmap
) : ResourceDecoder<TinyThumb, BitmapDrawable> {
override fun handles(source: TinyThumb, options: Options) = source.base64.isNotEmpty()
override fun decode(
source: TinyThumb,
width: Int,
height: Int,
options: Options
): Resource<BitmapDrawable>? {
// Down sample the blurred bitmap to save resources.
// The later hardware upscale will look almost identical anyway.
val sampleSize = source.options?.sampleSize?.coerceAtLeast(1) ?: 6
val bitmap = generateBitmap(source, width / sampleSize, height / sampleSize)
val bitmapResource = BitmapResource.obtain(bitmap, bitmapPool)
return LazyBitmapDrawableResource.obtain(context.resources, bitmapResource)
}
private fun generateBitmap(thumb: TinyThumb, width: Int, height: Int): Bitmap {
// This is application specific and must match how it was encoded
val decodeFlag = Base64.URL_SAFE
val bytes = Base64.decode(thumb.base64, decodeFlag)
val options = BitmapFactory.Options().apply {
// RGB565 will take less memory, but the resulting gradient is horrible.
inPreferredConfig = Bitmap.Config.ARGB_8888
}
// Convert the byte array into a bitmap.
val source = requireNotNull(BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options)) {
"Failed to decode byte array. This can happen if the decode flag does not match the flag the data was encoded with."
}
// If the thumb is square or landscape, center crop it. Otherwise leave it as is.
val centerCrop = crop(source, width, height, 0.5f, 1f)
return blurProvider(centerCrop, thumb.options).also { centerCrop.recycle() }
}
/**
* Returns a new Bitmap (from the pool) with a crop effect depending on the crop anchor given.
* 0.5f is like [android.widget.ImageView.ScaleType.CENTER_CROP]. The crop anchor will be be nudged
* so the entire cropped bitmap will fit inside the src. May return the input bitmap if no
* scaling is necessary.
*/
private fun crop(
source: Bitmap,
width: Int,
height: Int,
horizontalCenterPercent: Float,
verticalCenterPercent: Float
): Bitmap = TODO("Beyond the scope of this article")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment