Мне нужно загрузить очень длинное (около 15K px) черно-белое растровое изображение с шириной экрана.
Я пробовал несколько подходов, и лучший, кажется, использует Glide
:
private fun loadGlideScreenWideCompress(context: Context, imageView: AppCompatImageView) {
imageView.adjustViewBounds = true
val params = LayoutParams(MATCH_PARENT, WRAP_CONTENT)
imageView.layoutParams = params
GlideApp.with(context)
.load(imageRes)
.encodeFormat(Bitmap.CompressFormat.WEBP)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView)
}
Проблема в том, что качество изображения теряется. Изображение размыто и текст не читается.
Я пытался использовать BitmapRegionDecoder
. Я не видел потери качества или проблем с памятью. Тем не менее, он декодирует только часть изображения. Я не очень понимаю, как его использовать: декодировать следующую часть на событие прокрутки? Это было бы трудно осуществить. Измерить высоту рисования и передать полную высоту до BitmapRegionDecoder
, интуитивно кажется неправильным, потому что это декодер только для регионов.
Другая проблема заключается в том, что изображение большое, и мне нужно, чтобы оно работало на всех размерах экрана. Если я возьму максимально возможный размер и затем масштабирую его, мне потребуется выполнить дорогостоящие операции по созданию растрового изображения и потенциально заблокировать основной поток.
Обычный подход не работает и дает исключения OOM:
val bitmap = BitmapFactory.Options().run {
inJustDecodeBounds = true
inPreferredConfig = Bitmap.Config.ALPHA_8
inDensity = displayMetrics.densityDpi
BitmapFactory.decodeResource(context.resources, imageRes, this)
}
imageView.scaleType = ImageView.ScaleType.FIT_CENTER
imageView.setImageBitmap(bitmap)
Код с уменьшением:
val res = context.resources
val display = res.displayMetrics
val dr = res.getDrawable(imageRes!!)
val original = (dr as BitmapDrawable).bitmap
val scale = original.width / display.widthPixels
val scaledBitmap = BitmapDrawable(res, Bitmap.createScaledBitmap(
original,
display.widthPixels,
original.height / scale,
true
))
imageView.adjustViewBounds = true
val bos = ByteArrayOutputStream()
scaledBitmap.bitmap.compress(CompressFormat.WEBP, 100, bos)
val decoder = BitmapRegionDecoder.newInstance(
ByteArrayInputStream(bos.toByteArray()),
false
)
val rect = Rect(
0,
0,
scaledBitmap.intrinsicWidth,
scaledBitmap.intrinsicHeight
)
val bitmapFactoryOptions = BitmapFactory.Options()
bitmapFactoryOptions.inPreferredConfig = Bitmap.Config.ALPHA_8;
bitmapFactoryOptions.inDensity = display.densityDpi;
val bmp = decoder.decodeRegion(rect, bitmapFactoryOptions);
imageView.setImageBitmap(bmp)
Итак, вопрос в том, что было бы лучшим подходом в этой ситуации и как правильно использовать BitmapRegionDecoder
?