github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/images/resize/resize.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package resize resizes images. 6 package resize 7 8 import ( 9 "image" 10 "image/color" 11 "image/draw" 12 ) 13 14 // Resize returns a scaled copy of the image slice r of m. 15 // The returned image has width w and height h. 16 func Resize(m image.Image, r image.Rectangle, w, h int) image.Image { 17 if w < 0 || h < 0 { 18 return nil 19 } 20 if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 { 21 return image.NewRGBA64(image.Rect(0, 0, w, h)) 22 } 23 switch m := m.(type) { 24 case *image.RGBA: 25 return resizeRGBA(m, r, w, h) 26 case *image.YCbCr: 27 if m, ok := resizeYCbCr(m, r, w, h); ok { 28 return m 29 } 30 } 31 ww, hh := uint64(w), uint64(h) 32 dx, dy := uint64(r.Dx()), uint64(r.Dy()) 33 // The scaling algorithm is to nearest-neighbor magnify the dx * dy source 34 // to a (ww*dx) * (hh*dy) intermediate image and then minify the intermediate 35 // image back down to a ww * hh destination with a simple box filter. 36 // The intermediate image is implied, we do not physically allocate a slice 37 // of length ww*dx*hh*dy. 38 // For example, consider a 4*3 source image. Label its pixels from a-l: 39 // abcd 40 // efgh 41 // ijkl 42 // To resize this to a 3*2 destination image, the intermediate is 12*6. 43 // Whitespace has been added to delineate the destination pixels: 44 // aaab bbcc cddd 45 // aaab bbcc cddd 46 // eeef ffgg ghhh 47 // 48 // eeef ffgg ghhh 49 // iiij jjkk klll 50 // iiij jjkk klll 51 // Thus, the 'b' source pixel contributes one third of its value to the 52 // (0, 0) destination pixel and two thirds to (1, 0). 53 // The implementation is a two-step process. First, the source pixels are 54 // iterated over and each source pixel's contribution to 1 or more 55 // destination pixels are summed. Second, the sums are divided by a scaling 56 // factor to yield the destination pixels. 57 // TODO: By interleaving the two steps, instead of doing all of 58 // step 1 first and all of step 2 second, we could allocate a smaller sum 59 // slice of length 4*w*2 instead of 4*w*h, although the resultant code 60 // would become more complicated. 61 n, sum := dx*dy, make([]uint64, 4*w*h) 62 for y := r.Min.Y; y < r.Max.Y; y++ { 63 for x := r.Min.X; x < r.Max.X; x++ { 64 // Get the source pixel. 65 r32, g32, b32, a32 := m.At(x, y).RGBA() 66 r64 := uint64(r32) 67 g64 := uint64(g32) 68 b64 := uint64(b32) 69 a64 := uint64(a32) 70 // Spread the source pixel over 1 or more destination rows. 71 py := uint64(y-r.Min.Y) * hh 72 for remy := hh; remy > 0; { 73 qy := dy - (py % dy) 74 if qy > remy { 75 qy = remy 76 } 77 // Spread the source pixel over 1 or more destination columns. 78 px := uint64(x-r.Min.X) * ww 79 index := 4 * ((py/dy)*ww + (px / dx)) 80 for remx := ww; remx > 0; { 81 qx := dx - (px % dx) 82 if qx > remx { 83 qx = remx 84 } 85 sum[index+0] += r64 * qx * qy 86 sum[index+1] += g64 * qx * qy 87 sum[index+2] += b64 * qx * qy 88 sum[index+3] += a64 * qx * qy 89 index += 4 90 px += qx 91 remx -= qx 92 } 93 py += qy 94 remy -= qy 95 } 96 } 97 } 98 return average(sum, w, h, n*0x0101) 99 } 100 101 // average convert the sums to averages and returns the result. 102 func average(sum []uint64, w, h int, n uint64) image.Image { 103 ret := image.NewRGBA(image.Rect(0, 0, w, h)) 104 for y := 0; y < h; y++ { 105 for x := 0; x < w; x++ { 106 i := y*ret.Stride + x*4 107 j := 4 * (y*w + x) 108 ret.Pix[i+0] = uint8(sum[j+0] / n) 109 ret.Pix[i+1] = uint8(sum[j+1] / n) 110 ret.Pix[i+2] = uint8(sum[j+2] / n) 111 ret.Pix[i+3] = uint8(sum[j+3] / n) 112 } 113 } 114 return ret 115 } 116 117 // resizeYCbCr returns a scaled copy of the YCbCr image slice r of m. 118 // The returned image has width w and height h. 119 func resizeYCbCr(m *image.YCbCr, r image.Rectangle, w, h int) (image.Image, bool) { 120 var verticalRes int 121 switch m.SubsampleRatio { 122 case image.YCbCrSubsampleRatio420: 123 verticalRes = 2 124 case image.YCbCrSubsampleRatio422: 125 verticalRes = 1 126 default: 127 return nil, false 128 } 129 ww, hh := uint64(w), uint64(h) 130 dx, dy := uint64(r.Dx()), uint64(r.Dy()) 131 // See comment in Resize. 132 n, sum := dx*dy, make([]uint64, 4*w*h) 133 for y := r.Min.Y; y < r.Max.Y; y++ { 134 Y := m.Y[y*m.YStride:] 135 Cb := m.Cb[y/verticalRes*m.CStride:] 136 Cr := m.Cr[y/verticalRes*m.CStride:] 137 for x := r.Min.X; x < r.Max.X; x++ { 138 // Get the source pixel. 139 r8, g8, b8 := color.YCbCrToRGB(Y[x], Cb[x/2], Cr[x/2]) 140 r64 := uint64(r8) 141 g64 := uint64(g8) 142 b64 := uint64(b8) 143 // Spread the source pixel over 1 or more destination rows. 144 py := uint64(y-r.Min.Y) * hh 145 for remy := hh; remy > 0; { 146 qy := dy - (py % dy) 147 if qy > remy { 148 qy = remy 149 } 150 // Spread the source pixel over 1 or more destination columns. 151 px := uint64(x-r.Min.X) * ww 152 index := 4 * ((py/dy)*ww + (px / dx)) 153 for remx := ww; remx > 0; { 154 qx := dx - (px % dx) 155 if qx > remx { 156 qx = remx 157 } 158 qxy := qx * qy 159 sum[index+0] += r64 * qxy 160 sum[index+1] += g64 * qxy 161 sum[index+2] += b64 * qxy 162 sum[index+3] += 0xFFFF * qxy 163 index += 4 164 px += qx 165 remx -= qx 166 } 167 py += qy 168 remy -= qy 169 } 170 } 171 } 172 return average(sum, w, h, n), true 173 } 174 175 // resizeRGBA returns a scaled copy of the RGBA image slice r of m. 176 // The returned image has width w and height h. 177 func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image { 178 ww, hh := uint64(w), uint64(h) 179 dx, dy := uint64(r.Dx()), uint64(r.Dy()) 180 // See comment in Resize. 181 n, sum := dx*dy, make([]uint64, 4*w*h) 182 for y := r.Min.Y; y < r.Max.Y; y++ { 183 pix := m.Pix[(y-m.Rect.Min.Y)*m.Stride:] 184 for x := r.Min.X; x < r.Max.X; x++ { 185 // Get the source pixel. 186 p := pix[(x-m.Rect.Min.X)*4:] 187 r64 := uint64(p[0]) 188 g64 := uint64(p[1]) 189 b64 := uint64(p[2]) 190 a64 := uint64(p[3]) 191 // Spread the source pixel over 1 or more destination rows. 192 py := uint64(y-r.Min.Y) * hh 193 for remy := hh; remy > 0; { 194 qy := dy - (py % dy) 195 if qy > remy { 196 qy = remy 197 } 198 // Spread the source pixel over 1 or more destination columns. 199 px := uint64(x-r.Min.X) * ww 200 index := 4 * ((py/dy)*ww + (px / dx)) 201 for remx := ww; remx > 0; { 202 qx := dx - (px % dx) 203 if qx > remx { 204 qx = remx 205 } 206 qxy := qx * qy 207 sum[index+0] += r64 * qxy 208 sum[index+1] += g64 * qxy 209 sum[index+2] += b64 * qxy 210 sum[index+3] += a64 * qxy 211 index += 4 212 px += qx 213 remx -= qx 214 } 215 py += qy 216 remy -= qy 217 } 218 } 219 } 220 return average(sum, w, h, n) 221 } 222 223 // HalveInplace downsamples the image by 50% using averaging interpolation. 224 func HalveInplace(m image.Image) image.Image { 225 b := m.Bounds() 226 switch m := m.(type) { 227 case *image.YCbCr: 228 for y := b.Min.Y; y < b.Max.Y/2; y++ { 229 for x := b.Min.X; x < b.Max.X/2; x++ { 230 y00 := uint32(m.Y[m.YOffset(2*x, 2*y)]) 231 y10 := uint32(m.Y[m.YOffset(2*x+1, 2*y)]) 232 y01 := uint32(m.Y[m.YOffset(2*x, 2*y+1)]) 233 y11 := uint32(m.Y[m.YOffset(2*x+1, 2*y+1)]) 234 // Add before divide with uint32 or we get errors in the least 235 // significant bits. 236 m.Y[m.YOffset(x, y)] = uint8((y00 + y10 + y01 + y11) >> 2) 237 238 cb00 := uint32(m.Cb[m.COffset(2*x, 2*y)]) 239 cb10 := uint32(m.Cb[m.COffset(2*x+1, 2*y)]) 240 cb01 := uint32(m.Cb[m.COffset(2*x, 2*y+1)]) 241 cb11 := uint32(m.Cb[m.COffset(2*x+1, 2*y+1)]) 242 m.Cb[m.COffset(x, y)] = uint8((cb00 + cb10 + cb01 + cb11) >> 2) 243 244 cr00 := uint32(m.Cr[m.COffset(2*x, 2*y)]) 245 cr10 := uint32(m.Cr[m.COffset(2*x+1, 2*y)]) 246 cr01 := uint32(m.Cr[m.COffset(2*x, 2*y+1)]) 247 cr11 := uint32(m.Cr[m.COffset(2*x+1, 2*y+1)]) 248 m.Cr[m.COffset(x, y)] = uint8((cr00 + cr10 + cr01 + cr11) >> 2) 249 } 250 } 251 b.Max = b.Min.Add(b.Size().Div(2)) 252 return subImage(m, b) 253 case draw.Image: 254 for y := b.Min.Y; y < b.Max.Y/2; y++ { 255 for x := b.Min.X; x < b.Max.X/2; x++ { 256 r00, g00, b00, a00 := m.At(2*x, 2*y).RGBA() 257 r10, g10, b10, a10 := m.At(2*x+1, 2*y).RGBA() 258 r01, g01, b01, a01 := m.At(2*x, 2*y+1).RGBA() 259 r11, g11, b11, a11 := m.At(2*x+1, 2*y+1).RGBA() 260 261 // Add before divide with uint32 or we get errors in the least 262 // significant bits. 263 r := (r00 + r10 + r01 + r11) >> 2 264 g := (g00 + g10 + g01 + g11) >> 2 265 b := (b00 + b10 + b01 + b11) >> 2 266 a := (a00 + a10 + a01 + a11) >> 2 267 268 m.Set(x, y, color.RGBA{ 269 R: uint8(r >> 8), 270 G: uint8(g >> 8), 271 B: uint8(b >> 8), 272 A: uint8(a >> 8), 273 }) 274 } 275 } 276 b.Max = b.Min.Add(b.Size().Div(2)) 277 return subImage(m, b) 278 default: 279 // TODO(wathiede): fallback to generic Resample somehow? 280 panic("Unhandled image type") 281 } 282 } 283 284 // ResampleInplace will resample m inplace, overwritting existing pixel data, 285 // and return a subimage of m sized to w and h. 286 func ResampleInplace(m image.Image, r image.Rectangle, w, h int) image.Image { 287 // We don't support scaling up. 288 if r.Dx() < w || r.Dy() < h { 289 return m 290 } 291 292 switch m := m.(type) { 293 case *image.YCbCr: 294 xStep := float64(r.Dx()) / float64(w) 295 yStep := float64(r.Dy()) / float64(h) 296 for y := r.Min.Y; y < r.Min.Y+h; y++ { 297 for x := r.Min.X; x < r.Min.X+w; x++ { 298 xSrc := int(float64(x) * xStep) 299 ySrc := int(float64(y) * yStep) 300 cSrc := m.COffset(xSrc, ySrc) 301 cDst := m.COffset(x, y) 302 m.Y[m.YOffset(x, y)] = m.Y[m.YOffset(xSrc, ySrc)] 303 m.Cb[cDst] = m.Cb[cSrc] 304 m.Cr[cDst] = m.Cr[cSrc] 305 } 306 } 307 case draw.Image: 308 xStep := float64(r.Dx()) / float64(w) 309 yStep := float64(r.Dy()) / float64(h) 310 for y := r.Min.Y; y < r.Min.Y+h; y++ { 311 for x := r.Min.X; x < r.Min.X+w; x++ { 312 xSrc := int(float64(x) * xStep) 313 ySrc := int(float64(y) * yStep) 314 r, g, b, a := m.At(xSrc, ySrc).RGBA() 315 m.Set(x, y, color.RGBA{ 316 R: uint8(r >> 8), 317 G: uint8(g >> 8), 318 B: uint8(b >> 8), 319 A: uint8(a >> 8), 320 }) 321 } 322 } 323 default: 324 // TODO fallback to generic Resample somehow? 325 panic("Unhandled image type") 326 } 327 r.Max.X = r.Min.X + w 328 r.Max.Y = r.Min.Y + h 329 return subImage(m, r) 330 } 331 332 func subImage(m image.Image, r image.Rectangle) image.Image { 333 type subImager interface { 334 SubImage(image.Rectangle) image.Image 335 } 336 if si, ok := m.(subImager); ok { 337 return si.SubImage(r) 338 } 339 panic("Image type doesn't support SubImage") 340 } 341 342 // Resample returns a resampled copy of the image slice r of m. 343 // The returned image has width w and height h. 344 func Resample(m image.Image, r image.Rectangle, w, h int) image.Image { 345 if w < 0 || h < 0 { 346 return nil 347 } 348 if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 { 349 return image.NewRGBA64(image.Rect(0, 0, w, h)) 350 } 351 img := image.NewRGBA(image.Rect(0, 0, w, h)) 352 xStep := float64(r.Dx()) / float64(w) 353 yStep := float64(r.Dy()) / float64(h) 354 for y := 0; y < h; y++ { 355 for x := 0; x < w; x++ { 356 xSrc := int(float64(r.Min.X) + float64(x)*xStep) 357 ySrc := int(float64(r.Min.Y) + float64(y)*yStep) 358 r, g, b, a := m.At(xSrc, ySrc).RGBA() 359 img.SetRGBA(x, y, color.RGBA{ 360 R: uint8(r >> 8), 361 G: uint8(g >> 8), 362 B: uint8(b >> 8), 363 A: uint8(a >> 8), 364 }) 365 } 366 } 367 return img 368 }