curious problem implementing RGB565 image

180 views
Skip to first unread message

Dan Kortschak

unread,
Jun 13, 2016, 7:10:46 AM6/13/16
to golang-nuts, Nigel Tao
As part of my LEGO robotics project, I'm implementing image handling for
various boards' frame buffers (EVB and EV3 in the first instance).

Both are working as far as manual testing of rendered images goes, but
automated testing has shown up a weird problem. This is best illustrated
by looking at the input and golden images for the tests.

(small images for debugging - golden images are generated via[1] - png
encoding an RGB565 image).

https://github.com/ev3go/evb/blob/master/testdata/corner.png
https://github.com/ev3go/evb/blob/master/testdata/corner-565.png

There are clear defects in the corner-565.png.

Zooming you see the same defects.

https://github.com/ev3go/evb/blob/master/testdata/black.png
https://github.com/ev3go/evb/blob/master/testdata/black-565.png

Though doing the direct round trip of an image through an RGB565 gets
back the pixels in a state that you would expect, shown here in the
playground snippet:

https://play.golang.org/p/UCwE4YJk21


Here you can see that this image.RGBA converted to an RGB565 and then
png encoded renders correctly: https://play.golang.org/p/bopuF5cLFl

But if the same image is read in via a png decode, it renders
incorrectly (it shows up white here):
https://play.golang.org/p/8Z429vCUIM


Any suggestions about what I'm missing here?


[1]https://github.com/ev3go/evb/blob/master/lcd_test.go#L49

Nigel Tao

unread,
Jun 13, 2016, 9:16:14 PM6/13/16
to Dan Kortschak, golang-nuts
On Mon, Jun 13, 2016 at 9:10 PM, Dan Kortschak
<dan.ko...@adelaide.edu.au> wrote:
> Though doing the direct round trip of an image through an RGB565 gets
> back the pixels in a state that you would expect, shown here in the
> playground snippet:
>
> https://play.golang.org/p/UCwE4YJk21

Here's your color model function:

func rgb565Model(c color.Color) color.Color {
if _, ok := c.(Pixel565); ok {
return c
}
r, g, b, _ := c.RGBA()
r >>= (bytewid - rwid)
g >>= (bytewid - gwid)
b >>= (bytewid - bwid)
return Pixel565((r&rmask)<<roff | (g&gmask)<<goff | b&bmask)
}

The
r, g, b, _ := c.RGBA()
line gives a 16-bit red value, in the range [0x0000, 0xffff]. The
value's type is uint32, but the effective range is 16 bits. (See the
"three important subtleties" paragraph at
https://blog.golang.org/go-image-package). The next line is:
r >>= (bytewid - rwid)
or equivalently,
r >>= (8 - 5)
so that r is effectively an 13 bit value. The final line says:
r&rmask
but rmask is 0x1f, so you're masking off all but the low 5 bytes,
instead of the high 5 bytes. I suspect that it suffices to replace
r >>= (bytewid - rwid)
with
r >>= (8 + bytewid - rwid)
and the same for g and b, obviously.

Here's a quick test:
fmt.Printf("0x%04x\n", RGB565Model.Convert(color.RGBA64{
R: 0x0000,
G: 0x0000,
B: 0x8000,
A: 0xffff,
}))
should print a Pixel565 value that's 50-ish% blue: 0x0010 instead of 0x0000.

Dan Kortschak

unread,
Jun 13, 2016, 10:15:25 PM6/13/16
to Nigel Tao, golang-nuts
Yes, that explains it.

The simple summary is that I forgot the r,g,b,a are 16bit and was
working with them as 8bit. This is why the image.RGBA worked, but the
png decoded image did not - image.RGBA returned c|c<<8 for each channel,
so the error was masked by having the low byte mimic the high byte.

thanks
--
Omnes mundum facimus.

Dan Kortschak <dan.ko...@adelaide.edu.au>
F9B3 3810 C4DD E214 347C B8DA D879 B7A7 EECC 5A40
10C7 EEF4 A467 89C9 CA00 70DF C18F 3421 A744 607C

Reply all
Reply to author
Forward
0 new messages