Discussion:
Bitwise puzzle in com.sun.glass.ui.monocle.Framebuffer
John Neffenger
2018-11-05 18:20:13 UTC
Permalink
I am completely puzzled by the bitwise operations in the Framebuffer
class (package com.sun.glass.ui.monocle). I'm overriding its methods to
support a "Y8" 8-bit grayscale frame buffer, yet I have no idea why it's
doing what it's doing. Can anyone shed light on its complexity?

The Framebuffer class converts a buffer with 32-bit pixels in ARGB32
format into a buffer with 16-bit pixels in RGB565 format, as illustrated
below for one pixel.

aaaa aaaa rrrr rrrr gggg gggg bbbb bbbb
--> rrrr rggg gggb bbbb

I would convert the pixels with a method like the following:

void copyNextPixel(IntBuffer source, ShortBuffer target) {
int pixel32 = source.get();
int r = (pixel32 >> 8) & 0xF800;
int g = (pixel32 >> 5) & 0x07E0;
int b = (pixel32 >> 3) & 0x001F;
int pixel16 = r | g | b;
target.put((short) pixel16);
}

The JavaFX Framebuffer class converts the pixels like this:

void copyNextPixelOriginal(IntBuffer source, ShortBuffer target) {
int pixel32 = source.get();
int r = ((((pixel32 >> 19) & 31) * 539219) >> 8) & (31 << 11);
int g = ((((pixel32 >> 10) & 63) * 265395) >> 13) & (63 << 5);
int b = (((pixel32 >> 3) & 31) * 539219) >> 19;
int pixel16 = r | g | b;
target.put((short) pixel16);
}

Both of these methods produce identical results when tested against all
16,777,216 possible 24-bit RGB values. To understand what is happening,
I show each step in the creation and positioning of the 5-bit red color
value for both algorithms below (best viewed in a monospaced font).

My version
int r = (pixel32 >> 8) & 0xF800;

____ ____ xxxx xxxx ____ ____ ____ ____ pixel32 (x = red)
____ ____ ____ ____ xxxx xxxx ____ ____ >> 8
0000 0000 0000 0000 xxxx x000 0000 0000 & 0xF800

JavaFX version
int r = ((((pixel32 >> 19) & 31) * 539219) >> 8) & (31 << 11);

____ ____ xxxx xxxx ____ ____ ____ ____ pixel32 (x = red)
____ ____ ____ ____ ____ ____ ___x xxxx >> 19
0000 0000 0000 0000 0000 0000 000x xxxx & 31 = 0x1F
0000 0000 xxxx x___ ____ ____ ____ ____ * 539219 = 2^19 + 14931
0000 0000 0000 0000 xxxx x___ ____ ____ >> 8
0000 0000 0000 0000 xxxx x000 0000 0000 & (31 << 11) = 0xF800

Why all the back and forth? Why introduce varying bits to the right of
the red color value (* 539219), only to clear those bits later? The last
three steps could be replaced with a single logical shift left operation
(<< 11).

The Framebuffer class was added to the repository on January 21, 2014,
by Daniel Blaukopf.

Thank you,
John
Nir Lisker
2018-11-17 13:40:43 UTC
Permalink
Hi John,

I agree with your math. I guess you could submit an enhancement request to
have it evaluated.

- Nir
Post by John Neffenger
I am completely puzzled by the bitwise operations in the Framebuffer
class (package com.sun.glass.ui.monocle). I'm overriding its methods to
support a "Y8" 8-bit grayscale frame buffer, yet I have no idea why it's
doing what it's doing. Can anyone shed light on its complexity?
The Framebuffer class converts a buffer with 32-bit pixels in ARGB32
format into a buffer with 16-bit pixels in RGB565 format, as illustrated
below for one pixel.
aaaa aaaa rrrr rrrr gggg gggg bbbb bbbb
--> rrrr rggg gggb bbbb
void copyNextPixel(IntBuffer source, ShortBuffer target) {
int pixel32 = source.get();
int r = (pixel32 >> 8) & 0xF800;
int g = (pixel32 >> 5) & 0x07E0;
int b = (pixel32 >> 3) & 0x001F;
int pixel16 = r | g | b;
target.put((short) pixel16);
}
void copyNextPixelOriginal(IntBuffer source, ShortBuffer target) {
int pixel32 = source.get();
int r = ((((pixel32 >> 19) & 31) * 539219) >> 8) & (31 << 11);
int g = ((((pixel32 >> 10) & 63) * 265395) >> 13) & (63 << 5);
int b = (((pixel32 >> 3) & 31) * 539219) >> 19;
int pixel16 = r | g | b;
target.put((short) pixel16);
}
Both of these methods produce identical results when tested against all
16,777,216 possible 24-bit RGB values. To understand what is happening,
I show each step in the creation and positioning of the 5-bit red color
value for both algorithms below (best viewed in a monospaced font).
My version
int r = (pixel32 >> 8) & 0xF800;
____ ____ xxxx xxxx ____ ____ ____ ____ pixel32 (x = red)
____ ____ ____ ____ xxxx xxxx ____ ____ >> 8
0000 0000 0000 0000 xxxx x000 0000 0000 & 0xF800
JavaFX version
int r = ((((pixel32 >> 19) & 31) * 539219) >> 8) & (31 << 11);
____ ____ xxxx xxxx ____ ____ ____ ____ pixel32 (x = red)
____ ____ ____ ____ ____ ____ ___x xxxx >> 19
0000 0000 0000 0000 0000 0000 000x xxxx & 31 = 0x1F
0000 0000 xxxx x___ ____ ____ ____ ____ * 539219 = 2^19 + 14931
0000 0000 0000 0000 xxxx x___ ____ ____ >> 8
0000 0000 0000 0000 xxxx x000 0000 0000 & (31 << 11) = 0xF800
Why all the back and forth? Why introduce varying bits to the right of
the red color value (* 539219), only to clear those bits later? The last
three steps could be replaced with a single logical shift left operation
(<< 11).
The Framebuffer class was added to the repository on January 21, 2014,
by Daniel Blaukopf.
Thank you,
John
John Neffenger
2018-11-19 02:03:18 UTC
Permalink
Post by Nir Lisker
I agree with your math. I guess you could submit an enhancement request
to have it evaluated.
Thank you for looking into it, Nir. I plan to include the simplified
RGB565 conversion along with my Y8 grayscale support, as it's all done
in the same two Framebuffer methods.

I'm still hoping to solve the puzzle, though. Maybe it was just meant to
remain a mystery. :-)

John

Loading...