-
Notifications
You must be signed in to change notification settings - Fork 0
/
image.go
132 lines (108 loc) · 2.67 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
122
123
124
125
126
127
128
129
130
131
132
package waveform
import (
"image"
"image/color"
"sync"
)
var (
DefaultFillColor = color.RGBAModel.Convert(color.Black)
DefaultBGColor = color.RGBAModel.Convert(color.White)
)
type Pair struct {
Max, Min float32
}
type WaveForm struct {
// Protects writes, this can be locked by user to avoid
// a write happening while reading out the entire image
sync.Mutex
FillColor color.Color
BackgroundColor color.Color
Width, Height int
// img is a slice of min/max pairs
img []Pair
// state to keep track of for AddPair
// startPos is the x the image starts in
startPos int
// full determines if img is full and we need to wrap around
full bool
// imgPos is the next position to write to in img
imgPos int
}
// NewWaveForm returns an initialized empty WaveForm image
//
// The width and height are the dimensions of the image returned.
func NewWaveForm(width, height int) *WaveForm {
return &WaveForm{
FillColor: DefaultFillColor,
BackgroundColor: DefaultBGColor,
img: make([]Pair, width),
Width: width,
Height: height,
}
}
func (wf *WaveForm) Resize(width, height int) {
wf.Lock()
defer wf.Unlock()
// We only need to do actual work if the width changes
if width == wf.Width {
wf.Height = height
return
}
nimg := make([]Pair, width)
copy(nimg, wf.img[wf.startPos:])
copy(nimg[len(wf.img)-wf.startPos:], wf.img[:wf.startPos])
wf.img = nimg
wf.Width = width
wf.Height = height
}
// ColorModel implements the image.Image ColorModel method.
func (wf *WaveForm) ColorModel() color.Model {
return color.RGBAModel
}
// Bounds implements the image.Image Bounds method.
func (wf *WaveForm) Bounds() image.Rectangle {
return image.Rectangle{
image.Point{0, 0},
image.Point{int(wf.Width), int(wf.Height)},
}
}
// At implements the image.Image At method.
func (wf *WaveForm) At(x, y int) color.Color {
x = x + wf.startPos
if x > wf.Width {
x = x % wf.Width
}
p := wf.img[x]
mid := wf.Height / 2
if y == mid {
return wf.FillColor
}
if y < mid && mid+int(float32(mid)*p.Min) < y {
return wf.FillColor
}
if y > mid && mid+int(float32(mid)*p.Max) > y {
return wf.FillColor
}
return wf.BackgroundColor
}
// AddPair adds a pair of min and max points, each pair is
// a single pixel in the resulting image.
//
// AddPair can be called wf.width times before it gets rid
// of previous pairs added.
func (wf *WaveForm) AddPair(p Pair) {
wf.Lock()
x := wf.imgPos
wf.imgPos++
// wrap around our x if needed
if wf.imgPos >= len(wf.img) {
wf.full = true
wf.imgPos = 0
}
// if our buffer is already full we will need to adjust things
if wf.full {
wf.startPos = wf.imgPos
}
wf.img[x] = p
wf.Unlock()
}