mirror of https://github.com/periph/devices
lepton: Add new image14bit subpackage
This defines a image/draw compatible Gray14 image type and Intensity14 pixel type. These will soon be used to store the frames return by the lepton driver. This is part of the work for #218.pull/1/head
parent
9660271642
commit
66af77a36a
@ -0,0 +1,82 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package image14bit implements 14-bit per pixel images.
|
||||
//
|
||||
// It is compatible with the image/draw package.
|
||||
package image14bit
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
)
|
||||
|
||||
// Gray14 represents an image of 14-bit values.
|
||||
type Gray14 struct {
|
||||
// Pix holds the image's pixels. Each uint16 element represents one 14-bit
|
||||
// pixel.
|
||||
Pix []uint16
|
||||
// Stride is the Pix stride (in pixels) between vertically adjacent pixels.
|
||||
Stride int
|
||||
// Rect is the image's bounds.
|
||||
Rect image.Rectangle
|
||||
}
|
||||
|
||||
// NewGray14 returns an initialized Gray14 instance.
|
||||
func NewGray14(r image.Rectangle) *Gray14 {
|
||||
w, h := r.Dx(), r.Dy()
|
||||
pix := make([]uint16, w*h)
|
||||
return &Gray14{Pix: pix, Stride: w, Rect: r}
|
||||
}
|
||||
|
||||
// ColorModel implements image.Image.
|
||||
func (i *Gray14) ColorModel() color.Model {
|
||||
return Intensity14Model
|
||||
}
|
||||
|
||||
// Bounds implements image.Image.
|
||||
func (i *Gray14) Bounds() image.Rectangle {
|
||||
return i.Rect
|
||||
}
|
||||
|
||||
// Opaque returns whether the image is fully opaque.
|
||||
func (i *Gray14) Opaque() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// At implements image.Image.
|
||||
func (i *Gray14) At(x, y int) color.Color {
|
||||
return i.Intensity14At(x, y)
|
||||
}
|
||||
|
||||
// Intensity14At returns the Intensity14 value at a point.
|
||||
func (i *Gray14) Intensity14At(x, y int) Intensity14 {
|
||||
if !(image.Point{x, y}.In(i.Rect)) {
|
||||
return Intensity14(0)
|
||||
}
|
||||
offset := i.PixOffset(x, y)
|
||||
return Intensity14(i.Pix[offset])
|
||||
}
|
||||
|
||||
// PixOffset returns the index of the element of Pix that
|
||||
// corresponds to the pixel at (x, y).
|
||||
func (i *Gray14) PixOffset(x, y int) int {
|
||||
return (y-i.Rect.Min.Y)*i.Stride + (x - i.Rect.Min.X)
|
||||
}
|
||||
|
||||
// Set implements draw.Image.
|
||||
func (i *Gray14) Set(x, y int, c color.Color) {
|
||||
i.SetIntensity14(x, y, convertIntensity14(c))
|
||||
}
|
||||
|
||||
// SetIntensity14 sets the Intensity14 value for the pixel at (x, y).
|
||||
func (i *Gray14) SetIntensity14(x, y int, c Intensity14) {
|
||||
if !(image.Point{x, y}.In(i.Rect)) {
|
||||
return
|
||||
}
|
||||
i.Pix[i.PixOffset(x, y)] = uint16(c)
|
||||
}
|
||||
|
||||
var _ draw.Image = &Gray14{}
|
||||
@ -0,0 +1,185 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package image14bit
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewGray14(t *testing.T) {
|
||||
data := []struct {
|
||||
r image.Rectangle
|
||||
l int
|
||||
stride int
|
||||
}{
|
||||
// Empty.
|
||||
{
|
||||
image.Rect(0, 0, 0, 0),
|
||||
0,
|
||||
0,
|
||||
},
|
||||
// Empty
|
||||
{
|
||||
image.Rect(0, 0, 1, 0),
|
||||
0,
|
||||
1,
|
||||
},
|
||||
// Empty
|
||||
{
|
||||
image.Rect(0, 0, 0, 1),
|
||||
0,
|
||||
0,
|
||||
},
|
||||
// 1x1
|
||||
{
|
||||
image.Rect(0, 0, 1, 1),
|
||||
1,
|
||||
1,
|
||||
},
|
||||
// Zero-based
|
||||
{
|
||||
image.Rect(0, 0, 9, 17),
|
||||
9 * 17,
|
||||
9,
|
||||
},
|
||||
// Non-zero-based
|
||||
{
|
||||
image.Rect(1, 7, 9, 17),
|
||||
8 * 10,
|
||||
8,
|
||||
},
|
||||
// Negative X
|
||||
{
|
||||
image.Rect(-1, 0, 0, 1),
|
||||
1,
|
||||
1,
|
||||
},
|
||||
// Negative Y.
|
||||
{
|
||||
image.Rect(0, -1, 1, 0),
|
||||
1,
|
||||
1,
|
||||
},
|
||||
}
|
||||
for i, line := range data {
|
||||
img := NewGray14(line.r)
|
||||
if r := img.Bounds(); r != line.r {
|
||||
t.Fatalf("#%d: expected %v; actual %v", i, line.r, r)
|
||||
}
|
||||
if l := len(img.Pix); l != line.l {
|
||||
t.Fatalf("#%d: len(img.Pix) expected %v; actual %v for %v", i, line.l, l, line.r)
|
||||
}
|
||||
if img.Stride != line.stride {
|
||||
t.Fatalf("#%d: img.Stride expected %v; actual %v for %v", i, line.stride, img.Stride, line.r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAt(t *testing.T) {
|
||||
img := NewGray14(image.Rect(0, 0, 1, 1))
|
||||
img.SetIntensity14(0, 0, Intensity14(16383))
|
||||
c := img.At(0, 0)
|
||||
if g, ok := c.(Intensity14); !ok || g != Intensity14(16383) {
|
||||
t.Fatal(c, g)
|
||||
}
|
||||
// Out of bounds.
|
||||
c = img.At(0, 1)
|
||||
if g, ok := c.(Intensity14); !ok || g != Intensity14(0) {
|
||||
t.Fatal(c, g)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntensity14At(t *testing.T) {
|
||||
img := NewGray14(image.Rect(0, 0, 1, 1))
|
||||
img.SetIntensity14(0, 0, Intensity14(16383))
|
||||
if g := img.Intensity14At(0, 0); g != Intensity14(16383) {
|
||||
t.Fatal(g)
|
||||
}
|
||||
// Out of bounds.
|
||||
if g := img.Intensity14At(0, 1); g != Intensity14(0) {
|
||||
t.Fatal(g)
|
||||
}
|
||||
}
|
||||
|
||||
func TestColorModel(t *testing.T) {
|
||||
img := NewGray14(image.Rect(0, 0, 1, 8))
|
||||
if v := img.ColorModel(); v != Intensity14Model {
|
||||
t.Fatalf("%s", v)
|
||||
}
|
||||
if v := img.ColorModel().Convert(color.NRGBA{0x00, 0x00, 0x00, 0xFF}).(Intensity14); v != Intensity14(0) {
|
||||
t.Fatalf("%s", v)
|
||||
}
|
||||
if v := img.ColorModel().Convert(color.NRGBA{0x7F, 0x7F, 0x7F, 0xFF}).(Intensity14); v != Intensity14(8159) {
|
||||
t.Fatalf("%s", v)
|
||||
}
|
||||
if v := img.ColorModel().Convert(color.NRGBA{0xFF, 0xFF, 0xFF, 0xFF}).(Intensity14); v != Intensity14(16383) {
|
||||
t.Fatalf("%s", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpaque(t *testing.T) {
|
||||
if !NewGray14(image.Rect(0, 0, 1, 8)).Opaque() {
|
||||
t.Fatal("image is always opaque")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPixOffset(t *testing.T) {
|
||||
data := []struct {
|
||||
r image.Rectangle
|
||||
x, y int
|
||||
offset int
|
||||
}{
|
||||
{
|
||||
image.Rect(0, 0, 1, 1),
|
||||
0, 0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
image.Rect(0, 0, 1, 8),
|
||||
0, 1,
|
||||
1,
|
||||
},
|
||||
{
|
||||
image.Rect(0, 0, 3, 16),
|
||||
1, 5,
|
||||
16,
|
||||
},
|
||||
{
|
||||
image.Rect(-1, -1, 3, 16),
|
||||
1, 5,
|
||||
26,
|
||||
},
|
||||
}
|
||||
for i, line := range data {
|
||||
img := NewGray14(line.r)
|
||||
offset := img.PixOffset(line.x, line.y)
|
||||
if offset != line.offset {
|
||||
t.Fatalf("#%d: expected offset:%v, actual offset:%v", i, line.offset, offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetIntensity14(t *testing.T) {
|
||||
img := NewGray14(image.Rect(0, 0, 1, 1))
|
||||
if img.Pix[0] != 0 {
|
||||
t.Fatal(img.Pix)
|
||||
}
|
||||
if img.SetIntensity14(0, 0, Intensity14(16383)); img.Pix[0] != 16383 {
|
||||
t.Fatal(img.Pix)
|
||||
}
|
||||
if img.SetIntensity14(0, 0, Intensity14(0)); img.Pix[0] != 0 {
|
||||
t.Fatal(img.Pix)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
img := NewGray14(image.Rect(0, 0, 1, 1))
|
||||
img.Set(0, 0, color.NRGBA{0x80, 0x80, 0x80, 0xFF})
|
||||
if img.Pix[0] != 8224 {
|
||||
t.Fatal(img.Pix)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package image14bit
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Intensity14 is a 14-bit grayscale implementation of color.Color.
|
||||
//
|
||||
// Valid range is between 0 and 16383 (inclusive).
|
||||
type Intensity14 uint16
|
||||
|
||||
// RGBA returns a grayscale result.
|
||||
func (g Intensity14) RGBA() (uint32, uint32, uint32, uint32) {
|
||||
b := uint32(g) & 1
|
||||
i := uint32(g)<<2 | b<<1 | b
|
||||
return i, i, i, 65535
|
||||
}
|
||||
|
||||
func (g Intensity14) String() string {
|
||||
return "Intensity14(" + strconv.Itoa(int(g)) + ")"
|
||||
}
|
||||
|
||||
// Intensity14Model is the color Model for 14-bit grayscale.
|
||||
var Intensity14Model = color.ModelFunc(convert)
|
||||
|
||||
func convert(c color.Color) color.Color {
|
||||
return convertIntensity14(c)
|
||||
}
|
||||
|
||||
func convertIntensity14(c color.Color) Intensity14 {
|
||||
switch t := c.(type) {
|
||||
case Intensity14:
|
||||
return t
|
||||
default:
|
||||
r, g, b, _ := c.RGBA()
|
||||
// Use the same coefficients as color.GrayModel.
|
||||
y := (19595*r + 38470*g + 7471*b + 1<<15) >> 18
|
||||
return Intensity14(y)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package image14bit
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIntensity14(t *testing.T) {
|
||||
if r, g, b, a := Intensity14(16383).RGBA(); r != 65535 || g != r || b != r || a != r {
|
||||
t.Fatal(r, g, b, a)
|
||||
}
|
||||
if r, g, b, a := Intensity14(0).RGBA(); r != 0 || g != r || b != r || a != 65535 {
|
||||
t.Fatal(r, g, b, a)
|
||||
}
|
||||
if Intensity14(16383).String() != "Intensity14(16383)" || Intensity14(0).String() != "Intensity14(0)" {
|
||||
t.Fail()
|
||||
}
|
||||
if Intensity14(8192) != convertIntensity14(Intensity14(8192)) {
|
||||
t.Fatal("failed to convert Intensity14 correctly")
|
||||
}
|
||||
if Intensity14(8224) != convertIntensity14(color.NRGBA{0x80, 0x80, 0x80, 0xFF}) {
|
||||
t.Fatal("failed to convert color.NRGBA correctly")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue