glReadPixels GL_UNSIGNED_SHORT_5_6_5


int pixel_size = 2;
int stride = SCREEN_WIDTH * pixel_size;
unsigned char* frame_buffer = malloc(SCREEN_HEIGHT * stride);

while(!terminate)
{
update_model(state);
redraw_scene(state);

glReadPixels(
		0, 0,
		480, 272,
		GL_RGB,
		GL_UNSIGNED_SHORT_5_6_5,
		frame_buffer
		);

glGetError();
}

I am attempting to extract pixel data in RGB 565 format to write to an LCD.

The above code returns GL_INVALID_VALUE. According to the documentation this error is only supposed to happen if the width or height are negative. I would have expected GL_INVALID_OPERATION for not being one of the two possible type/format pairs. Further, GL_RGB/GL_RGBA and GL_UNSIGNED_BYTE works fine and does not cause this error.

The documentation states that the only valid type/format pairs are GL_RGBA and GL_UNSIGNED_BYTE, and glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES) and glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES). In my case they also returned GL_RGBA and GL_UNSIGNED_BYTE. So the only combination that should work is GL_RGBA and GL_UNSIGNED_BYTE. However, GL_RGB and GL_UNSIGNED_BYTE also works (only ones tried).

The documentation also incorrectly leaves the “_OES” off the end of the constants.

There seems to be several errors in the documentation, or am I looking at the wrong one:
khronos.org/opengles/sdk/docs/man/xhtml/glReadPixels.xml

I am pretty new to OpenGL.
I am running this on a Raspberry Pi which has include directories /opt/vc/include/EGL, GLES, and GLES2.

If there is anymore information needed about my setup, please let me know.

EDIT:
Forgot to include full source: hello_trinagle_lcd.c - Pastebin.com

The documentation also incorrectly leaves the “_OES” off the end of the constants.

That’s because there is no “_OES” on the end of those constants. Those are core OpenGL ES enumerators, not extension enumerators. So they don’t get a suffix.

There seems to be several errors in the documentation, or am I looking at the wrong one

Or the Raspberry Pi implementation is bad.

Hmm, so you are saying that Raspberry Pi has its own implementation of OpenGL ES? I thought there was a single universal library called OpenGL ES, not a specific implementation for each device.

Looking again I find that GL_IMPLEMENTATION_COLOR_READ_FORMAT is defined in GLES2/gl2.h as 0x8B9B and GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES is defined in GLES/gl.h also as 0x8B9B.

Here is a link to the OpenGL headers that Raspberry Pi provides (EGL, GLES, GLES2):

I was not trying to be offensive or blame the documentation, I was just trying to note that there were discrepancies between what I was seeing and what the documentation was stating… maybe that would have been a better word than error. My apologizes.

EDIT:
I guess my real question is: How do I change GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE to be GL_RGB and GL_UNSIGNED_SHORT_5_6_5? I assume this means changing the “implementation color read”?

The documentation states that the only valid type/format pairs are GL_RGBA and GL_UNSIGNED_BYTE, and glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE_OE S) and glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_ OES). In my case they also returned GL_RGBA and GL_UNSIGNED_BYTE. So the only combination that should work is GL_RGBA and GL_UNSIGNED_BYTE. However, GL_RGB and GL_UNSIGNED_BYTE also works (only ones tried).

Well, the way I read it your implementation says you can only use GL_RGBA/GL_UNSIGNED_BYTE. It then happens to also accept GL_RGB/GL_UNSIGNED_BYTE, which strictly it does not have to and could (should?) reject, but using anything else is a (programmer) error. BTW, another common name for OpenGL (ES) implementation is “graphics driver” (well, the driver is a bit more than just the OpenGL implementation, but you get the idea), so there are indeed a number of different ones (of varying quality too :slight_smile: ).

I guess my real question is: How do I change GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE to be GL_RGB and GL_UNSIGNED_SHORT_5_6_5?

You can not (unless you have access to the source of your OpenGL implementation). You’ll have to do the format conversion yourself after obtaining the data.

OK I tried my hand at manually convert the format, but the colors don’t look right… Any ideas?


int i, j;
unsigned short r, g, b;
for (i = 0; i < SCREEN_HEIGHT; i++)
{
	for (j = 0; j < SCREEN_WIDTH; j++)
	{
		r = *(frame_buffer_888 + i * RGB_888_STRIDE + j * RGB_888_PIXEL_SIZE);
		g = *(frame_buffer_888 + i * RGB_888_STRIDE + j * RGB_888_PIXEL_SIZE + 1);
		b = *(frame_buffer_888 + i * RGB_888_STRIDE + j * RGB_888_PIXEL_SIZE + 2);
		
		r <<= 8;
		r &= 0xF800; // 1111100000000000
		g <<= 3;
		g &= 0x07E0; // 0000011111100000
		b >>= 3;
		b &= 0x001F; // 0000000000011111

		unsigned short x = r | g | b;
		
		if (i == 0 && j == 0)
		{
			printf("
%i
", x);
			printf("%i
", (unsigned char)(x >> 8));
			printf("%i
", (unsigned char)(x & 0x00FF));
		}
		
		*(frame_buffer + i * RGB_565_STRIDE + j * RGB_565_PIXEL_SIZE) = (unsigned char)(x >> 8);
		*(frame_buffer + i * RGB_565_STRIDE + j * RGB_565_PIXEL_SIZE + 1) = (unsigned char)(x & 0x00FF);
	}
}

I did some testing my manually setting the first and second byte of each pixel and by observing the output of my LCD it seems that the color format is BRG 853??

11111111|00000000 is full blue (although I did not see a noticeable difference from 11111110|00000000)
00000000|11111000 is full red
00000000|00000111 is full green

Does this make sense to anyone?

I don’t see where you are remapping the 8 bit color ranges for r,g,b in the source format to the 5 or 6 bit range in the destination format. You probably map black to black and white to white, but I don’t think 50% grey (127, 127, 127) gets mapped to the correct value.

r = *(frame_buffer_888 + i * RGB_888_STRIDE + j * RGB_888_PIXEL_SIZE); // gets the red value (0-255)
r <<= 8; // left shift red 8 times so that the first 8 bits are now red's bits
r &= 0xF800; // remove the last 3 bits of the red by doing a bit-wise AND with 1111100000000000. This is equivalent to dividing by 8 and puts our red value in the first 5 bits and in the correct range of 0-31.

Please see comments above.