/
image.go
121 lines (109 loc) · 3.41 KB
/
image.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
Package hsvimage implements the image.Image interface with HSV + alpha images.
The code was largely adapted from code in the Go standard library.
*/
package hsvimage
import (
"github.com/spakin/hsvimage/hsvcolor"
"image"
"image/color"
)
// NHSVA is an in-memory image whose At method returns hsvcolor.NHSVA values.
type NHSVA struct {
// Pix holds the image's pixels, in H, S, V, A order. The pixel at
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
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 states that an NHSVA image uses the hsvcolor.NHSVA color model.
func (p *NHSVA) ColorModel() color.Model { return hsvcolor.NHSVAModel }
// Bounds returns the image's bounding rectangle.
func (p *NHSVA) Bounds() image.Rectangle { return p.Rect }
// At returns the color at the given image coordinates.
func (p *NHSVA) At(x, y int) color.Color {
return p.NHSVAAt(x, y)
}
// NHSVAAt returns the color at the given image coordinates as specifically an
// hsvcolor.NHSVA color.
func (p *NHSVA) NHSVAAt(x, y int) hsvcolor.NHSVA {
if !(image.Point{x, y}.In(p.Rect)) {
return hsvcolor.NHSVA{}
}
i := p.PixOffset(x, y)
s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
return hsvcolor.NHSVA{H: s[0], S: s[1], V: s[2], A: s[3]}
}
// PixOffset returns the index of the first element of Pix that corresponds to
// the pixel at (x, y).
func (p *NHSVA) PixOffset(x, y int) int {
return (y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*4
}
// Set assigns an arbitrary color to a given coordinate.
func (p *NHSVA) Set(x, y int, c color.Color) {
if !(image.Point{x, y}.In(p.Rect)) {
return
}
i := p.PixOffset(x, y)
c1 := hsvcolor.NHSVAModel.Convert(c).(hsvcolor.NHSVA)
s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
s[0] = c1.H
s[1] = c1.S
s[2] = c1.V
s[3] = c1.A
}
// SetNHSVA assigns an NHSVA color to a given coordinate.
func (p *NHSVA) SetNHSVA(x, y int, c hsvcolor.NHSVA) {
if !(image.Point{x, y}.In(p.Rect)) {
return
}
i := p.PixOffset(x, y)
s := p.Pix[i : i+4 : i+4] // Small cap improves performance, see https://golang.org/issue/27857
s[0] = c.H
s[1] = c.S
s[2] = c.V
s[3] = c.A
}
// SubImage returns an image representing the portion of the image p visible
// through r. The returned value shares pixels with the original image.
func (p *NHSVA) SubImage(r image.Rectangle) image.Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to
// be inside either r1 or r2 if the intersection is empty. Without
// explicitly checking for this, the Pix[i:] expression below can
// panic.
if r.Empty() {
return &NHSVA{}
}
i := p.PixOffset(r.Min.X, r.Min.Y)
return &NHSVA{
Pix: p.Pix[i:],
Stride: p.Stride,
Rect: r,
}
}
// Opaque scans the entire image and reports whether it is fully opaque.
func (p *NHSVA) Opaque() bool {
if p.Rect.Empty() {
return true
}
i0, i1 := 3, p.Rect.Dx()*4
for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ {
for i := i0; i < i1; i += 4 {
if p.Pix[i] != 0xff {
return false
}
}
i0 += p.Stride
i1 += p.Stride
}
return true
}
// NewNHSVA returns a new NHSVA image with the given bounds.
func NewNHSVA(r image.Rectangle) *NHSVA {
w, h := r.Dx(), r.Dy()
pix := make([]uint8, 4*w*h)
return &NHSVA{pix, 4 * w, r}
}