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}
319