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
|
package epd7in3e
import (
"image"
"image/draw"
)
// Quantise maps img to a *image.Paletted on the panel's Palette. With
// dither it applies Floyd-Steinberg error diffusion; otherwise each
// pixel becomes its nearest palette colour.
func Quantise(img image.Image, dither bool) *image.Paletted {
if dither {
return floydSteinberg(img)
}
r := img.Bounds()
p := image.NewPaletted(r, Palette)
// Drawing into a Paletted quantises each pixel to the nearest
// palette colour.
draw.Draw(p, r, img, r.Min, draw.Src)
return p
}
// floydSteinberg dithers img to the panel's Palette using
// Floyd-Steinberg error diffusion.
func floydSteinberg(img image.Image) *image.Paletted {
r := img.Bounds()
w, h := r.Dx(), r.Dy()
// Work on a plain NRGBA copy so the loop reads raw bytes.
src := image.NewNRGBA(r)
draw.Draw(src, r, img, r.Min, draw.Src)
// Float copy so diffused error may overshoot 0..255.
f := make([]float32, w*h*3)
for y := 0; y < h; y++ {
i := (y-r.Min.Y)*src.Stride + 4*r.Min.X
for x := 0; x < w; x++ {
k := (y*w + x) * 3
f[k] = float32(src.Pix[i])
f[k+1] = float32(src.Pix[i+1])
f[k+2] = float32(src.Pix[i+2])
i += 4
}
}
p := image.NewPaletted(r, Palette)
for y := 0; y < h; y++ {
i := (y-r.Min.Y)*p.Stride + r.Min.X
for x := 0; x < w; x++ {
k := (y*w + x) * 3
cr, cg, cb := f[k], f[k+1], f[k+2]
idx := nearestIndex(cr, cg, cb)
p.Pix[i+x] = idx
pp := paletteRGB[idx]
er := cr - float32(pp[0])
eg := cg - float32(pp[1])
eb := cb - float32(pp[2])
if x+1 < w { // right: 7/16
j := k + 3
f[j] += er * 7 / 16
f[j+1] += eg * 7 / 16
f[j+2] += eb * 7 / 16
}
if y+1 < h {
jb := k + 3*w // below: 5/16
f[jb] += er * 5 / 16
f[jb+1] += eg * 5 / 16
f[jb+2] += eb * 5 / 16
if x > 0 { // bottom-left: 3/16
j := jb - 3
f[j] += er * 3 / 16
f[j+1] += eg * 3 / 16
f[j+2] += eb * 3 / 16
}
if x+1 < w { // bottom-right: 1/16
j := jb + 3
f[j] += er / 16
f[j+1] += eg / 16
f[j+2] += eb / 16
}
}
}
}
return p
}
// nearestIndex returns the Palette index whose RGB value is closest
// to (r, g, b).
func nearestIndex(r, g, b float32) uint8 {
var best uint8
bestD := float32(1 << 30)
for i := range paletteRGB {
p := paletteRGB[i]
dr := r - float32(p[0])
dg := g - float32(p[1])
db := b - float32(p[2])
if d := dr*dr + dg*dg + db*db; d < bestD {
best, bestD = uint8(i), d
}
}
return best
}
|