How to display HDR picture in Android?
Asked Answered
B

1

6

I'm doing an android app to decompress, decode and display HDR pictures.
These HDR pictures use 2 bytes per component (A,R,G,B) so one pixel is represented by a 8 bytes value that can only fit with the long type.

I'm using android's Bitmap to display picture as they have a constructor allowing to do HDR by using Bitmap.Config.RGBA_F16:

int width = 1;
int height = 1;
Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.RGBA_F16);

Unfortunately I can't find any way to fill a pixel of the Bitmap. I use the recommended formula but it cannot be used in the setPixel(x,y,color) method of Bitmap because color has to be a int:

long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff);
image.setPixel(0,0,color); //Argument type error

.
I have also tried with Color (which has a HDR compatible method), Paint and Canvas but no Bitmap method accepts them to set only one pixel.

Thanks for any help!

Boating answered 1/4, 2019 at 15:25 Comment(10)
If you're doing image editing, you shouldn't use setPixel anyway. Get the bytes of the image and edit the image directly. Repeated calls to setPixel is incredibly inefficient.Comate
@GabeSechan thanks for your answer but it doesn't help me. My main problem is displaying a 8bytes per pixel picture in Android (if possible with Bitmap). Do you know any way to display HDR content (like a .hdr file or a bytestream) on Android ?Boating
The entire architecture on Android is for ARGB, with support for smaller formats (565, grayscale, etc). You're going to have to downscale. Which is what I bet that constructor you found is doing. But if it makes you feel any better it would have to happen at some point anyway- the hardware isnt going to support 16 bit output per channel on the screen.Comate
Then I have trouble understanding why HDR10 video (10bits per component) is supported by Android on my Galaxy Note 8 but not HDR pictures. The android documentation is really unclear about this unfortunately...Boating
Completely different subsystems. Video uses OpenGL to directly write the video buffers. Not really comparable.You may be able to use openGL directly so long as you do everything via a custom view or SurfaceView, but I'm not sure its worth the effort. Either way you wouldn't be using a Bitmap object most likely.Comate
Well I think that I will have to get my hands dirty with OpenGL then. Thanks for your time and answers, you cleared a big misunderstanding I had!Boating
@Boating have you found the sollution. I am facing the same problem - I have only found info how to displayer hdr video content on android: source.android.com/devices/tech/display/hdrOpportunity
I have found also this little information: developer.android.com/reference/android/graphics/Bitmap.ConfigOpportunity
@Boating Did you tried using glide or Picasso image loader instead yourself your decompressing image?Piselli
@Opportunity unfortunately we haven't found any solution :( As an alternative we ended up using tone mapping to reduce the pixel space to a basic 8 bits per component (using opencv tonemappers).Boating
L
1

If you need to open an HDR file, such as 16-bit png, and then display it, you can use ImageDecoder.setTargetColorSpace to create bitmap with format Bitmap.Config.RGBA_F16 like so:

File file = new File(...);
ImageDecoder.Source source = ImageDecoder.createSource(file);
Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
    decoder.setTargetColorSpace(ColorSpace.Named.EXTENDED_SRGB);
});

If you need to display HDR image that is stored in memory you can use Bitmap.copyPixelsFromBuffer, as this method allows to set pixels of the bitmap without conversion of color space the way Bitmap.setPixel does. In this case you need to pack 4 channels represented by Half values into long for each pixel, then write these long values into a Buffer and finally copy pixels from the buffer to the bitmap.

LongBuffer buffer = LongBuffer.allocate(width * height);
for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
        // fill pixels values as needed
        float r = (float)y / height;
        float g = (float)y / height;
        float b = (float)y / height;
        float a = 1f;
        long rBits = Half.halfToShortBits(Half.toHalf(r));
        long gBits = Half.halfToShortBits(Half.toHalf(g));
        long bBits = Half.halfToShortBits(Half.toHalf(b));
        long aBits = Half.halfToShortBits(Half.toHalf(a));
        long color = aBits << 48 | bBits << 32 | gBits << 16 | rBits;
        buffer.put(color);
    }
}
buffer.rewind();

bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGBA_F16);
bitmap.copyPixelsFromBuffer(buffer);

Lias answered 22/12, 2020 at 4:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.