|
@@ -0,0 +1,142 @@
|
|
1
|
+/*
|
|
2
|
+Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>
|
|
3
|
+
|
|
4
|
+Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
5
|
+with or without fee is hereby granted, provided that the above copyright notice
|
|
6
|
+and this permission notice appear in all copies.
|
|
7
|
+
|
|
8
|
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
9
|
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
10
|
+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
11
|
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
12
|
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
13
|
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
14
|
+THIS SOFTWARE.
|
|
15
|
+*/
|
|
16
|
+
|
|
17
|
+package resize
|
|
18
|
+
|
|
19
|
+import (
|
|
20
|
+ "image"
|
|
21
|
+ "image/color"
|
|
22
|
+ "math"
|
|
23
|
+)
|
|
24
|
+
|
|
25
|
+// color.RGBA64 as array
|
|
26
|
+type RGBA [4]uint16
|
|
27
|
+
|
|
28
|
+// build RGBA from an arbitrary color
|
|
29
|
+func toRGBA(c color.Color) RGBA {
|
|
30
|
+ n := color.RGBA64Model.Convert(c).(color.RGBA64)
|
|
31
|
+ return RGBA{n.R, n.G, n.B, n.A}
|
|
32
|
+}
|
|
33
|
+
|
|
34
|
+func clampToUint16(x float32) (y uint16) {
|
|
35
|
+ y = uint16(x)
|
|
36
|
+ if x < 0 {
|
|
37
|
+ y = 0
|
|
38
|
+ } else if x > float32(0xffff) {
|
|
39
|
+ y = 0xffff
|
|
40
|
+ }
|
|
41
|
+ return
|
|
42
|
+}
|
|
43
|
+
|
|
44
|
+// Nearest-neighbor interpolation.
|
|
45
|
+// Approximates a value by returning the value of the nearest point.
|
|
46
|
+func NearestNeighbor(x, y float32, img image.Image) color.RGBA64 {
|
|
47
|
+ xn, yn := int(x), int(y)
|
|
48
|
+ c := toRGBA(img.At(xn, yn))
|
|
49
|
+ return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
50
|
+}
|
|
51
|
+
|
|
52
|
+// Linear interpolation.
|
|
53
|
+func linearInterp(x float32, p *[2]RGBA) (c RGBA) {
|
|
54
|
+ x -= float32(math.Floor(float64(x)))
|
|
55
|
+ for i := range c {
|
|
56
|
+ c[i] = clampToUint16(float32(p[0][i])*(1.0-x) + x*float32(p[1][i]))
|
|
57
|
+ }
|
|
58
|
+ return
|
|
59
|
+}
|
|
60
|
+
|
|
61
|
+// Bilinear interpolation.
|
|
62
|
+func Bilinear(x, y float32, img image.Image) color.RGBA64 {
|
|
63
|
+ xf, yf := int(math.Floor(float64(x))), int(math.Floor(float64(y)))
|
|
64
|
+
|
|
65
|
+ var row [2]RGBA
|
|
66
|
+ var col [2]RGBA
|
|
67
|
+ row = [2]RGBA{toRGBA(img.At(xf, yf)), toRGBA(img.At(xf+1, yf))}
|
|
68
|
+ col[0] = linearInterp(x, &row)
|
|
69
|
+ row = [2]RGBA{toRGBA(img.At(xf, yf+1)), toRGBA(img.At(xf+1, yf+1))}
|
|
70
|
+ col[1] = linearInterp(x, &row)
|
|
71
|
+
|
|
72
|
+ c := linearInterp(y, &col)
|
|
73
|
+ return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
74
|
+}
|
|
75
|
+
|
|
76
|
+// cubic interpolation
|
|
77
|
+func cubicInterp(x float32, p *[4]RGBA) (c RGBA) {
|
|
78
|
+ x -= float32(math.Floor(float64(x)))
|
|
79
|
+ for i := range c {
|
|
80
|
+ c[i] = clampToUint16(float32(p[1][i]) + 0.5*x*(float32(p[2][i])-float32(p[0][i])+x*(2.0*float32(p[0][i])-5.0*float32(p[1][i])+4.0*float32(p[2][i])-float32(p[3][i])+x*(3.0*(float32(p[1][i])-float32(p[2][i]))+float32(p[3][i])-float32(p[0][i])))))
|
|
81
|
+ }
|
|
82
|
+ return
|
|
83
|
+}
|
|
84
|
+
|
|
85
|
+// Bicubic interpolation.
|
|
86
|
+func Bicubic(x, y float32, img image.Image) color.RGBA64 {
|
|
87
|
+ xf, yf := int(math.Floor(float64(x))), int(math.Floor(float64(y)))
|
|
88
|
+
|
|
89
|
+ var row [4]RGBA
|
|
90
|
+ var col [4]RGBA
|
|
91
|
+ row = [4]RGBA{toRGBA(img.At(xf-1, yf-1)), toRGBA(img.At(xf, yf-1)), toRGBA(img.At(xf+1, yf-1)), toRGBA(img.At(xf+2, yf-1))}
|
|
92
|
+ col[0] = cubicInterp(x, &row)
|
|
93
|
+ row = [4]RGBA{toRGBA(img.At(xf-1, yf)), toRGBA(img.At(xf, yf)), toRGBA(img.At(xf+1, yf)), toRGBA(img.At(xf+2, yf))}
|
|
94
|
+ col[1] = cubicInterp(x, &row)
|
|
95
|
+ row = [4]RGBA{toRGBA(img.At(xf-1, yf+1)), toRGBA(img.At(xf, yf+1)), toRGBA(img.At(xf+1, yf+1)), toRGBA(img.At(xf+2, yf+1))}
|
|
96
|
+ col[2] = cubicInterp(x, &row)
|
|
97
|
+ row = [4]RGBA{toRGBA(img.At(xf-1, yf+2)), toRGBA(img.At(xf, yf+2)), toRGBA(img.At(xf+1, yf+2)), toRGBA(img.At(xf+2, yf+2))}
|
|
98
|
+ col[3] = cubicInterp(x, &row)
|
|
99
|
+
|
|
100
|
+ c := cubicInterp(y, &col)
|
|
101
|
+ return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
102
|
+}
|
|
103
|
+
|
|
104
|
+// 1-d convolution with windowed sinc for a=3.
|
|
105
|
+func lanczos_x(x float32, p *[6]RGBA) (c RGBA) {
|
|
106
|
+ x -= float32(math.Floor(float64(x)))
|
|
107
|
+ var v float32
|
|
108
|
+ l := [4]float32{0.0, 0.0, 0.0, 0.0}
|
|
109
|
+ for j := range p {
|
|
110
|
+ v = float32(Sinc(float64(x-float32(j-2)))) * float32(Sinc(float64((x-float32(j-2))/3.0)))
|
|
111
|
+ for i := range c {
|
|
112
|
+ l[i] += float32(p[j][i]) * v
|
|
113
|
+ }
|
|
114
|
+ }
|
|
115
|
+ for i := range c {
|
|
116
|
+ c[i] = clampToUint16(l[i])
|
|
117
|
+ }
|
|
118
|
+ return
|
|
119
|
+}
|
|
120
|
+
|
|
121
|
+// Lanczos interpolation (a=3).
|
|
122
|
+func Lanczos3(x, y float32, img image.Image) color.RGBA64 {
|
|
123
|
+ xf, yf := int(math.Floor(float64(x))), int(math.Floor(float64(y)))
|
|
124
|
+
|
|
125
|
+ var row [6]RGBA
|
|
126
|
+ var col [6]RGBA
|
|
127
|
+ row = [6]RGBA{toRGBA(img.At(xf-2, yf-2)), toRGBA(img.At(xf-1, yf-2)), toRGBA(img.At(xf, yf-2)), toRGBA(img.At(xf+1, yf-2)), toRGBA(img.At(xf+2, yf-2)), toRGBA(img.At(xf+3, yf-2))}
|
|
128
|
+ col[0] = lanczos_x(x, &row)
|
|
129
|
+ row = [6]RGBA{toRGBA(img.At(xf-2, yf-1)), toRGBA(img.At(xf-1, yf-1)), toRGBA(img.At(xf, yf-1)), toRGBA(img.At(xf+1, yf-1)), toRGBA(img.At(xf+2, yf-1)), toRGBA(img.At(xf+3, yf-1))}
|
|
130
|
+ col[1] = lanczos_x(x, &row)
|
|
131
|
+ row = [6]RGBA{toRGBA(img.At(xf-2, yf)), toRGBA(img.At(xf-1, yf)), toRGBA(img.At(xf, yf)), toRGBA(img.At(xf+1, yf)), toRGBA(img.At(xf+2, yf)), toRGBA(img.At(xf+3, yf))}
|
|
132
|
+ col[2] = lanczos_x(x, &row)
|
|
133
|
+ row = [6]RGBA{toRGBA(img.At(xf-2, yf+1)), toRGBA(img.At(xf-1, yf+1)), toRGBA(img.At(xf, yf+1)), toRGBA(img.At(xf+1, yf+1)), toRGBA(img.At(xf+2, yf+1)), toRGBA(img.At(xf+3, yf+1))}
|
|
134
|
+ col[3] = lanczos_x(x, &row)
|
|
135
|
+ row = [6]RGBA{toRGBA(img.At(xf-2, yf+2)), toRGBA(img.At(xf-1, yf+2)), toRGBA(img.At(xf, yf+2)), toRGBA(img.At(xf+1, yf+2)), toRGBA(img.At(xf+2, yf+2)), toRGBA(img.At(xf+3, yf+2))}
|
|
136
|
+ col[4] = lanczos_x(x, &row)
|
|
137
|
+ row = [6]RGBA{toRGBA(img.At(xf-2, yf+3)), toRGBA(img.At(xf-1, yf+3)), toRGBA(img.At(xf, yf+3)), toRGBA(img.At(xf+1, yf+3)), toRGBA(img.At(xf+2, yf+3)), toRGBA(img.At(xf+3, yf+3))}
|
|
138
|
+ col[5] = lanczos_x(x, &row)
|
|
139
|
+
|
|
140
|
+ c := lanczos_x(y, &col)
|
|
141
|
+ return color.RGBA64{c[0], c[1], c[2], c[3]}
|
|
142
|
+}
|