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