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

ev3dev/fb: add support for XRGB #78

Merged
merged 2 commits into from
Apr 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added fb/testdata/black-xrgb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fb/testdata/corner-xrgb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fb/testdata/gopherbrick-xrgb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 109 additions & 0 deletions fb/xrgb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright ©2016 The ev3go Authors. All rights reserved.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2018? Also in the test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As attested by the horrific level of copy-paste errorism, the code was originally the 565 code.

// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fb

import (
"errors"
"image"
"image/color"
"image/draw"
)

// NewXRGB returns a new XRGB image with the given bounds.
func NewXRGB(r image.Rectangle) *XRGB {
w, h := r.Dx(), r.Dy()
stride := 4 * w
pix := make([]uint8, stride*h)
return &XRGB{Pix: pix, Stride: stride, Rect: r}
}

// NewXRGBWith returns a new XRGB image with the given bounds,
// backed by the []byte, pix. If stride is zero, a working stride
// is computed. If the length of pix is less than stride*h, an
// error is returned.
func NewXRGBWith(pix []byte, r image.Rectangle, stride int) (draw.Image, error) {
w, h := r.Dx(), r.Dy()
if stride == 0 {
stride = 4 * w
}
if len(pix) < stride*h {
return nil, errors.New("ev3dev: bad pixel buffer length")
}
return &XRGB{Pix: pix, Stride: stride, Rect: r}, nil
}

// XRGB is an in-memory image whose At method returns PixelXRGB values.
type XRGB struct {
// Pix holds the image's pixels, as 32 bit XRGB
// values stored in little-endian order.
// The pixel at (x, y) is the four bytes at
// Pix[4*(x-Rect.Min.X) + (y-Rect.Min.Y)*Stride].
Pix []uint8
// Stride is the Pix stride (in bytes) between
// vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect image.Rectangle
}

// ColorModel returns the XRGB color model.
func (p *XRGB) ColorModel() color.Model { return RGB565Model }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is RGB565Model correct?

Copy link
Member Author

@kortschak kortschak Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, The only difference between an XRGB and a color.RGBA is the absence of the A channel. The storage in the image's Pix field` is independent of the colour's representation here.


// Bounds returns the bounding rectangle for the image.
func (p *XRGB) Bounds() image.Rectangle { return p.Rect }

// At returns the color of the pixel at (x, y).
func (p *XRGB) At(x, y int) color.Color {
if !(image.Point{x, y}.In(p.Rect)) {
return color.RGBA{}
}
i := p.pixOffset(x, y)
return PixelXRGB{R: p.Pix[i+2], G: p.Pix[i+1], B: p.Pix[i]}
}

// Set sets the color of the pixel at (x, y) to c.
func (p *XRGB) Set(x, y int, c color.Color) {
if !(image.Point{x, y}.In(p.Rect)) {
return
}
i := p.pixOffset(x, y)
xrgb := XRGBModel.Convert(c).(PixelXRGB)
p.Pix[i+2] = xrgb.R
p.Pix[i+1] = xrgb.G
p.Pix[i] = xrgb.B
}

// pixOffset returns the index into p.Pix for the first byte
// containing the pixel at (x, y).
func (p *XRGB) pixOffset(x, y int) int {
return 4*(x-p.Rect.Min.X) + (y-p.Rect.Min.Y)*p.Stride
}

// PixelXRGB is an XRGB pixel.
type PixelXRGB struct {
R, G, B byte
}

// RGBA returns the RGBA values for the receiver.
func (c PixelXRGB) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
return r, g, b, 0xffff
}

// XRGBModel is the color model for XRGB images.
var XRGBModel color.Model = color.ModelFunc(xrgbModel)

func xrgbModel(c color.Color) color.Color {
if _, ok := c.(PixelXRGB); ok {
return c
}
r, g, b, _ := c.RGBA()
return PixelXRGB{R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8)}
}
52 changes: 52 additions & 0 deletions fb/xrgb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright ©2016 The ev3go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fb

import (
"image/draw"
"image/png"
"os"
"path/filepath"
"reflect"
"testing"
)

func TestXRGB(t *testing.T) {
for _, test := range testImages {
golden := filepath.FromSlash("testdata/" + test + "-xrgb.png")

src, err := decodeImage(filepath.FromSlash("testdata/" + test + ".png"))
if err != nil {
t.Fatalf("failed to read src image file %v.png: %v", test, err)
}

got := NewXRGB(src.Bounds())
draw.Draw(got, got.Bounds(), src, src.Bounds().Min, draw.Src)

if *genGolden {
f, err := os.Create(golden)
if err != nil {
t.Fatalf("failed to create golden image file %v-xrgb.png: %v", test, err)
}
defer f.Close()
err = png.Encode(f, got)
if err != nil {
t.Fatalf("failed to encode golden image %v-xrgb.png: %v", test, err)
}
continue
}

gol, err := decodeImage(golden)
if err != nil {
t.Fatalf("failed to read golden image file %v-xrgb.png: %v", test, err)
}
want := NewXRGB(gol.Bounds())
draw.Draw(want, want.Bounds(), gol, gol.Bounds().Min, draw.Src)

if !reflect.DeepEqual(got, want) {
t.Errorf("XRGB from source does not match expected image for %v test", test)
}
}
}