glide
glide copied to clipboard
DecodeFormat PREFER_RGB_565 doesn't work
glide version: 4.11.0 integration library: none model: ANA-AN00 os version: EMUI 11.0.0 base on Android 10
Issue details:
When set DecodeFormat to PREFER_RGB_565,glide apply Config.ARGB_8888 to ANY decoded bitmap,even though when a bitmap doesn't has alpha channel. According to the document:
Bitmaps decoded from image formats that support and/or use alpha (some types of PNGs, GIFs etc) should return {@link android.graphics.Bitmap.Config#ARGB_8888} for {@link android.graphics.Bitmap#getConfig()}. Bitmaps decoded from formats that don't support or use alpha should return {@link android.graphics.Bitmap.Config#RGB_565} for {@link android.graphics.Bitmap#getConfig()}.
it seems something wrong happed.
Or put another way,how can i make sure glide apply RGB_565 config when decoded a bitmap which has no alpha channel?
The reason why I want to use RGB_565 is for memory consider. RGB_565 use less memory compared to ARGB_8888.
Code
MainActivity
public class MainActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView imageView = findViewById(R.id.image);
loadImage(imageView, "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3363295869,2467511306&fm=26&gp=0.jpg");
}
private void loadImage(ImageView imageView, String url) {
Glide.with(imageView)
.load(url)
.transform(UnitTransformation.get()) // for skip centerCrop transformation
.set(Downsampler.DECODE_FORMAT, DecodeFormat.PREFER_RGB_565)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
Bitmap bitmap = ((BitmapDrawable) resource).getBitmap();
Log.d("diagnosis", String.format("config: %s hasAlpha: %b bytePerPixel: %d",
bitmap.getConfig().name(),
bitmap.hasAlpha(),
bitmap.getAllocationByteCount() / bitmap.getHeight() / bitmap.getWidth()));
return false;
}
})
.into(imageView);
}
}
Layout XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:scaleType="centerCrop"
android:layout_width="300dp"
android:layout_height="300dp" />
</FrameLayout>
With code list above,issue can be reproduce.
Stack trace / LogCat
LruBitmapPool: Missing bitmap=[4320000](ARGB_8888)
LruBitmapPool: Get bitmap=[4320000](ARGB_8888)
LruBitmapPool: Hits=0, misses=1, puts=0, evictions=0, currentSize=0, maxSize=9551520
Strategy=SizeConfigStrategy{groupedMap=GroupedLinkedMap( {[4320000](ARGB_8888):0} ), sortedSizes=(null[{}], ARGB_8888[{}], RGBA_F16[{}])}
diagnosis: config: RGB_565 hasAlpha: false bytePerPixel: 4
As log message show,the final bitmap display by imageView's config is has no alpha channel, config is RGB_565. RGB_565 should 2 byte per pixel,but this bitmap is as big as ARGB_8888 actually.
Analysis
And then i dig in glide's source code,find some point:
-
Bitmap is decoded by
Downsampler
.Before a real decoded action,Downsampler callgetDimensions
to get bitmap's width and height,and then callcalculateConfig
to fill options.inPreferCofig filed,and then callsetInBitmap
to fill options.inBitmap -
When call
getDimensions
,options.inPreferCofig is nullprivate static int[] getDimensions( ImageReader imageReader, BitmapFactory.Options options, DecodeCallbacks decodeCallbacks, BitmapPool bitmapPool) throws IOException { options.inJustDecodeBounds = true; decodeStream(imageReader, options, decodeCallbacks, bitmapPool); options.inJustDecodeBounds = false; return new int[] {options.outWidth, options.outHeight}; }
Options.outConfig is ARGB_8888 when this method return. This is because of JNI code default behavior.
-
When call
setInBitmap
@TargetApi(Build.VERSION_CODES.O) private static void setInBitmap( BitmapFactory.Options options, BitmapPool bitmapPool, int width, int height) { @Nullable Bitmap.Config expectedConfig = null; // Avoid short circuiting, it appears to break on some devices. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (options.inPreferredConfig == Config.HARDWARE) { return; } // On API 26 outConfig may be null for some images even if the image is valid, can be decoded // and outWidth/outHeight/outColorSpace are populated (see b/71513049). expectedConfig = options.outConfig; } if (expectedConfig == null) { // We're going to guess that BitmapFactory will return us the config we're requesting. This // isn't always the case, even though our guesses tend to be conservative and prefer configs // of larger sizes so that the Bitmap will fit our image anyway. If we're wrong here and the // config we choose is too small, our initial decode will fail, but we will retry with no // inBitmap which will succeed so if we're wrong here, we're less efficient but still correct. expectedConfig = options.inPreferredConfig; } // BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe. options.inBitmap = bitmapPool.getDirty(width, height, expectedConfig); }
expectedConfig is ARGB_8888 assign from options.outConfig. So there whill create a ARGB_8888 bitmap when decode a non-alpha bitmap.
There is my questions
- Why glide use ARGB_8888 bitmap as inBitmap when decode a bitmap which is known should decode to RGB_565. Is this a bug? Or a feature for reuse consideration?
- It seem's
set(Downsampler.DECODE_FORMAT, DecodeFormat.PREFER_xxxx)
does't effect the actual behavior ofDownampler
at all. Is this api totally useless?
@sjudd any reply is appreciated
I meet this issue too. I guess maybe it is caused by bitmap reuse issue.
Just wanted to add my +1 that this issue exists and makes it hard to optimize an app for low-memory scenarios, given that bitmaps are commonly one of the largest, if not the largest source of memory usage in apps. @sjudd
+1
Highly appreciate if someone can refer to the duplicate links or reference related to this problem
+1