Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loaded bitmap is "pixelated" #23

Closed
SammyVimes opened this issue Oct 7, 2014 · 14 comments
Closed

Loaded bitmap is "pixelated" #23

SammyVimes opened this issue Oct 7, 2014 · 14 comments
Labels

Comments

@SammyVimes
Copy link

Some images are loading in very poor quality. It's always the same images, but I can't figure out what they have in common. Here is the example of image and a screenshot how it looks like on a device. (btw, I use subsampling view). The issue persists on every device I tried and also on genymotion emulator, so I think it's a bug
sun-ken-rock-v01-c03---20
image

@davemorrissey
Copy link
Owner

It seems that BitmapRegionDecoder's handling of grayscale images is very badly broken. The last issue #22 reported a similar but different rendering problem with a grayscale JPEG, and I've isolated this problem to BitmapRegionDecoder as well. Your image displayed properly on my phone (Android 4.1.1) but not on my tablet (4.4.2). If I changed the image to PNG, or color JPEG, it was displayed properly.

I will have to raise another bug with Google but it may not be fixed for years. If you can convert the image to another format, that's your best option. I have tried changing all the options for the decodeRegion call but none of them fix this issue, so unfortunately I don't have any other workaround.

@SammyVimes
Copy link
Author

Thanks @davemorrissey , I made some research and it looks like it's a bug in skia library, which is used natively by BitmapRegionDecoder. I wonder if you know: is it possible to convert image programmatically on the phone to display it properly?

@davemorrissey
Copy link
Owner

That's a good find, the skia library didn't come up in my research. If you've found reports of this bug could you send me a link?

It is possible to convert the image by loading it as a bitmap (it appears other Android classes are unaffected), and saving it again. This is slow. However, I don't know how you can tell if the image is grayscale before doing this, and SubsamplingScaleImageView is designed for displaying bitmaps that are too large to load as a complete bitmap, so I don't have a good solution.

@SammyVimes
Copy link
Author

Sadly, I didn't find the report itself, only some mentions of it. I suspect this:
https://github.com/android/platform_external_skia/blob/ecd936fe6ed3c2dba29a06074894cd8a5e832d76/src/images/SkImageDecoder_libpng.cpp

^ this looks like a fixed source and this (https://github.com/android/platform_external_skia/blob/80bacfeb4bda06541e8695bd502229727bccfeab/src/images/SkImageDecoder_libpng.cpp) is bugged. Note that I bad at computer graphics, so this might be the wrong track, BUT I had similar issue (pixelated image) with this image: http://hc.readmanga.ru/gto_shonan_14_days/v1ch1/02.jpg_res.jpg that is not a grayscale image but has a lot of red color in it and in the same time, in the comments of libpng there is a mention of red being > 255.

This is that comment:

/* We apply the mask because in a very small
number of corrupt PNGs, (transpColor->red >
255) and (bitDepth == 8), for certain versions
of libpng. For safety we assume the same could
happen with a grayscale PNG. */

@davemorrissey
Copy link
Owner

This is beyond my experience! Are you sure this libpng class is used for JPEGs as well? So far I've had no reported problems with PNGs, only JPEGs.

@davemorrissey
Copy link
Owner

Closing issue as wontfix because it's a flaw in Android I can't work around.

@SammyVimes
Copy link
Author

I fixed this using https://github.com/suckgamony/RapidDecoder

@davemorrissey
Copy link
Owner

Thanks, that looks like a good solution. If you have time, could you post a couple of notes and a code sample showing how you use this library with SubsamplingScaleImageView, so I can post it on the readme?

@SammyVimes
Copy link
Author

Hmm, okay. I did something like this:

    @Override
    protected Bitmap doInBackground(Void... params) {
        try {
            if (decoderRef != null && tileRef != null && viewRef != null) {
                final BitmapRegionDecoder decoder = decoderRef.get();
                final Object decoderLock = decoderLockRef.get();
                final Tile tile = tileRef.get();
                final SubsamplingScaleImageView view = viewRef.get();
                if (decoder != null && decoderLock != null && tile != null && view != null && !decoder.isRecycled()) {
                    synchronized (decoderLock) {
                        BitmapFactory.Options options = new BitmapFactory.Options();
                        options.inSampleSize = tile.sampleSize;
                        options.inPreferredConfig = Config.RGB_565;
                        options.inDither = true;
                        Rect rect = view.fileSRect(tile.sRect);
                        //Bitmap bitmap = decoder.decodeRegion(view.fileSRect(tile.sRect), options);
                        Bitmap bitmap = BitmapDecoder.from(view._source).region(rect).decode();
                        int rotation = view.getRequiredRotation();
                        if (rotation != 0) {
                            Matrix matrix = new Matrix();
                            matrix.postRotate(rotation);
                            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                        }
                        return bitmap;
                    }
                } else if (tile != null) {
                    tile.loading = false;
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Failed to decode tile", e);
        }
        return null;
    }

and added a "_source" field to the SubsamplingScaleImageView class.

@davemorrissey
Copy link
Owner

I assume the _source field is a string?

Because this doesn't use the sample size it will create tiles that are higher resolution than they should be. The RGB_565 option is also necessary to halve memory usage for JPGs at least (it doesn't work for PNGs with an alpha layer). This solution will work for some images, but for larger images on low memory devices you're likely to find this change makes the view crash with OutOfMemoryError.

@SammyVimes
Copy link
Author

Yes, I know that. I just didn't have enough time yet, to rewrite it.

@SammyVimes
Copy link
Author

I suppose, I need to call scale method there, as the RapidDecoder 'documentation' states that "All of the scaling operations automatically decode bounds of bitmaps and calculate inSampleSize, so you don't need to consider about them at all.". Still, I don't know how to set RGB_565 option.

@SammyVimes
Copy link
Author

Okay, I found how to set RGB_565. It's just calling "config" method with RGB_565 as a parameter.

@davemorrissey
Copy link
Owner

I have extracted the bitmap decoding into an interface to allow an alternative decoder to be easily substituted in. A basic implementation built on RapidDecoder is included in the sample library, and it displays this image properly. See the updated README for more details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants