DecodeFormat PREFER_RGB_565 doesn't work
- Dominant language
- Java
- Stars
- 35k
- Forks
- 6.2k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 8
Description
**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
```java
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() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target 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
```
With code list above,issue can be reproduce.
## Stack trace / LogCat
```ruby
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 call `getDimensions` to get bitmap's width and height,and then call `calculateConfig` to fill options.inPreferCofig filed,and then call `setInBitmap` to fill options.inBitmap
* When call `getDimensions`,options.inPreferCofig is null
```java
private 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`
```java
@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 of `Downampler` at all. Is this api totally useless?
Contributor guide
Assessment
This issue has not been assessed yet.