1/*
2Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
3
4Permission to use, copy, modify, and/or distribute this software for any purpose
5with or without fee is hereby granted, provided that the above copyright notice
6and this permission notice appear in all copies.
7
8THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
10FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
12OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
13TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
14THIS SOFTWARE.
15*/
16
17package resize
18
19import "image"
20
21func floatToUint8(x float32) uint8 {
22 // Nearest-neighbor values are always
23 // positive no need to check lower-bound.
24 if x > 0xfe {
25 return 0xff
26 }
27 return uint8(x)
28}
29
30func floatToUint16(x float32) uint16 {
31 if x > 0xfffe {
32 return 0xffff
33 }
34 return uint16(x)
35}
36
37func nearestGeneric(in image.Image, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
38 newBounds := out.Bounds()
39 maxX := in.Bounds().Dx() - 1
40
41 for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
42 for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
43 var rgba [4]float32
44 var sum float32
45 start := offset[y]
46 ci := y * filterLength
47 for i := 0; i < filterLength; i++ {
48 if coeffs[ci+i] {
49 xi := start + i
50 switch {
51 case xi < 0:
52 xi = 0
53 case xi >= maxX:
54 xi = maxX
55 }
56 r, g, b, a := in.At(xi+in.Bounds().Min.X, x+in.Bounds().Min.Y).RGBA()
57 rgba[0] += float32(r)
58 rgba[1] += float32(g)
59 rgba[2] += float32(b)
60 rgba[3] += float32(a)
61 sum++
62 }
63 }
64
65 offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
66 value := floatToUint16(rgba[0] / sum)
67 out.Pix[offset+0] = uint8(value >> 8)
68 out.Pix[offset+1] = uint8(value)
69 value = floatToUint16(rgba[1] / sum)
70 out.Pix[offset+2] = uint8(value >> 8)
71 out.Pix[offset+3] = uint8(value)
72 value = floatToUint16(rgba[2] / sum)
73 out.Pix[offset+4] = uint8(value >> 8)
74 out.Pix[offset+5] = uint8(value)
75 value = floatToUint16(rgba[3] / sum)
76 out.Pix[offset+6] = uint8(value >> 8)
77 out.Pix[offset+7] = uint8(value)
78 }
79 }
80}
81
82func nearestRGBA(in *image.RGBA, out *image.RGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
83 newBounds := out.Bounds()
84 maxX := in.Bounds().Dx() - 1
85
86 for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
87 row := in.Pix[x*in.Stride:]
88 for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
89 var rgba [4]float32
90 var sum float32
91 start := offset[y]
92 ci := y * filterLength
93 for i := 0; i < filterLength; i++ {
94 if coeffs[ci+i] {
95 xi := start + i
96 switch {
97 case uint(xi) < uint(maxX):
98 xi *= 4
99 case xi >= maxX:
100 xi = 4 * maxX
101 default:
102 xi = 0
103 }
104 rgba[0] += float32(row[xi+0])
105 rgba[1] += float32(row[xi+1])
106 rgba[2] += float32(row[xi+2])
107 rgba[3] += float32(row[xi+3])
108 sum++
109 }
110 }
111
112 xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
113 out.Pix[xo+0] = floatToUint8(rgba[0] / sum)
114 out.Pix[xo+1] = floatToUint8(rgba[1] / sum)
115 out.Pix[xo+2] = floatToUint8(rgba[2] / sum)
116 out.Pix[xo+3] = floatToUint8(rgba[3] / sum)
117 }
118 }
119}
120
121func nearestNRGBA(in *image.NRGBA, out *image.NRGBA, scale float64, coeffs []bool, offset []int, filterLength int) {
122 newBounds := out.Bounds()
123 maxX := in.Bounds().Dx() - 1
124
125 for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
126 row := in.Pix[x*in.Stride:]
127 for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
128 var rgba [4]float32
129 var sum float32
130 start := offset[y]
131 ci := y * filterLength
132 for i := 0; i < filterLength; i++ {
133 if coeffs[ci+i] {
134 xi := start + i
135 switch {
136 case uint(xi) < uint(maxX):
137 xi *= 4
138 case xi >= maxX:
139 xi = 4 * maxX
140 default:
141 xi = 0
142 }
143 rgba[0] += float32(row[xi+0])
144 rgba[1] += float32(row[xi+1])
145 rgba[2] += float32(row[xi+2])
146 rgba[3] += float32(row[xi+3])
147 sum++
148 }
149 }
150
151 xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*4
152 out.Pix[xo+0] = floatToUint8(rgba[0] / sum)
153 out.Pix[xo+1] = floatToUint8(rgba[1] / sum)
154 out.Pix[xo+2] = floatToUint8(rgba[2] / sum)
155 out.Pix[xo+3] = floatToUint8(rgba[3] / sum)
156 }
157 }
158}
159
160func nearestRGBA64(in *image.RGBA64, out *image.RGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
161 newBounds := out.Bounds()
162 maxX := in.Bounds().Dx() - 1
163
164 for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
165 row := in.Pix[x*in.Stride:]
166 for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
167 var rgba [4]float32
168 var sum float32
169 start := offset[y]
170 ci := y * filterLength
171 for i := 0; i < filterLength; i++ {
172 if coeffs[ci+i] {
173 xi := start + i
174 switch {
175 case uint(xi) < uint(maxX):
176 xi *= 8
177 case xi >= maxX:
178 xi = 8 * maxX
179 default:
180 xi = 0
181 }
182 rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
183 rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
184 rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
185 rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
186 sum++
187 }
188 }
189
190 xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
191 value := floatToUint16(rgba[0] / sum)
192 out.Pix[xo+0] = uint8(value >> 8)
193 out.Pix[xo+1] = uint8(value)
194 value = floatToUint16(rgba[1] / sum)
195 out.Pix[xo+2] = uint8(value >> 8)
196 out.Pix[xo+3] = uint8(value)
197 value = floatToUint16(rgba[2] / sum)
198 out.Pix[xo+4] = uint8(value >> 8)
199 out.Pix[xo+5] = uint8(value)
200 value = floatToUint16(rgba[3] / sum)
201 out.Pix[xo+6] = uint8(value >> 8)
202 out.Pix[xo+7] = uint8(value)
203 }
204 }
205}
206
207func nearestNRGBA64(in *image.NRGBA64, out *image.NRGBA64, scale float64, coeffs []bool, offset []int, filterLength int) {
208 newBounds := out.Bounds()
209 maxX := in.Bounds().Dx() - 1
210
211 for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
212 row := in.Pix[x*in.Stride:]
213 for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
214 var rgba [4]float32
215 var sum float32
216 start := offset[y]
217 ci := y * filterLength
218 for i := 0; i < filterLength; i++ {
219 if coeffs[ci+i] {
220 xi := start + i
221 switch {
222 case uint(xi) < uint(maxX):
223 xi *= 8
224 case xi >= maxX:
225 xi = 8 * maxX
226 default:
227 xi = 0
228 }
229 rgba[0] += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
230 rgba[1] += float32(uint16(row[xi+2])<<8 | uint16(row[xi+3]))
231 rgba[2] += float32(uint16(row[xi+4])<<8 | uint16(row[xi+5]))
232 rgba[3] += float32(uint16(row[xi+6])<<8 | uint16(row[xi+7]))
233 sum++
234 }
235 }
236
237 xo := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*8
238 value := floatToUint16(rgba[0] / sum)
239 out.Pix[xo+0] = uint8(value >> 8)
240 out.Pix[xo+1] = uint8(value)
241 value = floatToUint16(rgba[1] / sum)
242 out.Pix[xo+2] = uint8(value >> 8)
243 out.Pix[xo+3] = uint8(value)
244 value = floatToUint16(rgba[2] / sum)
245 out.Pix[xo+4] = uint8(value >> 8)
246 out.Pix[xo+5] = uint8(value)
247 value = floatToUint16(rgba[3] / sum)
248 out.Pix[xo+6] = uint8(value >> 8)
249 out.Pix[xo+7] = uint8(value)
250 }
251 }
252}
253
254func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool, offset []int, filterLength int) {
255 newBounds := out.Bounds()
256 maxX := in.Bounds().Dx() - 1
257
258 for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
259 row := in.Pix[x*in.Stride:]
260 for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
261 var gray float32
262 var sum float32
263 start := offset[y]
264 ci := y * filterLength
265 for i := 0; i < filterLength; i++ {
266 if coeffs[ci+i] {
267 xi := start + i
268 switch {
269 case xi < 0:
270 xi = 0
271 case xi >= maxX:
272 xi = maxX
273 }
274 gray += float32(row[xi])
275 sum++
276 }
277 }
278
279 offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X)
280 out.Pix[offset] = floatToUint8(gray / sum)
281 }
282 }
283}
284
285func nearestGray16(in *image.Gray16, out *image.Gray16, scale float64, coeffs []bool, offset []int, filterLength int) {
286 newBounds := out.Bounds()
287 maxX := in.Bounds().Dx() - 1
288
289 for x := newBounds.Min.X; x < newBounds.Max.X; x++ {
290 row := in.Pix[x*in.Stride:]
291 for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ {
292 var gray float32
293 var sum float32
294 start := offset[y]
295 ci := y * filterLength
296 for i := 0; i < filterLength; i++ {
297 if coeffs[ci+i] {
298 xi := start + i
299 switch {
300 case uint(xi) < uint(maxX):
301 xi *= 2
302 case xi >= maxX:
303 xi = 2 * maxX
304 default:
305 xi = 0
306 }
307 gray += float32(uint16(row[xi+0])<<8 | uint16(row[xi+1]))
308 sum++
309 }
310 }
311
312 offset := (y-newBounds.Min.Y)*out.Stride + (x-newBounds.Min.X)*2
313 value := floatToUint16(gray / sum)
314 out.Pix[offset+0] = uint8(value >> 8)
315 out.Pix[offset+1] = uint8(value)
316 }
317 }
318}