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.
12 // Resize returns a scaled copy of the image slice r of m.
13 // The returned image has width w and height h.
14 func Resize(m image.Image, r image.Rectangle, w, h int) image.Image {
18 if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
19 return image.NewRGBA64(image.Rect(0, 0, w, h))
21 switch m := m.(type) {
23 return resizeRGBA(m, r, w, h)
25 if m, ok := resizeYCbCr(m, r, w, h); ok {
29 ww, hh := uint64(w), uint64(h)
30 dx, dy := uint64(r.Dx()), uint64(r.Dy())
31 // The scaling algorithm is to nearest-neighbor magnify the dx * dy source
32 // to a (ww*dx) * (hh*dy) intermediate image and then minify the intermediate
33 // image back down to a ww * hh destination with a simple box filter.
34 // The intermediate image is implied, we do not physically allocate a slice
35 // of length ww*dx*hh*dy.
36 // For example, consider a 4*3 source image. Label its pixels from a-l:
40 // To resize this to a 3*2 destination image, the intermediate is 12*6.
41 // Whitespace has been added to delineate the destination pixels:
49 // Thus, the 'b' source pixel contributes one third of its value to the
50 // (0, 0) destination pixel and two thirds to (1, 0).
51 // The implementation is a two-step process. First, the source pixels are
52 // iterated over and each source pixel's contribution to 1 or more
53 // destination pixels are summed. Second, the sums are divided by a scaling
54 // factor to yield the destination pixels.
55 // TODO: By interleaving the two steps, instead of doing all of
56 // step 1 first and all of step 2 second, we could allocate a smaller sum
57 // slice of length 4*w*2 instead of 4*w*h, although the resultant code
58 // would become more complicated.
59 n, sum := dx*dy, make([]uint64, 4*w*h)
60 for y := r.Min.Y; y < r.Max.Y; y++ {
61 for x := r.Min.X; x < r.Max.X; x++ {
62 // Get the source pixel.
63 r32, g32, b32, a32 := m.At(x, y).RGBA()
68 // Spread the source pixel over 1 or more destination rows.
70 for remy := hh; remy > 0; {
75 // Spread the source pixel over 1 or more destination columns.
77 index := 4 * ((py/dy)*ww + (px / dx))
78 for remx := ww; remx > 0; {
83 sum[index+0] += r64 * qx * qy
84 sum[index+1] += g64 * qx * qy
85 sum[index+2] += b64 * qx * qy
86 sum[index+3] += a64 * qx * qy
96 return average(sum, w, h, n*0x0101)
99 // average convert the sums to averages and returns the result.
100 func average(sum []uint64, w, h int, n uint64) image.Image {
101 ret := image.NewRGBA(image.Rect(0, 0, w, h))
102 for y := 0; y < h; y++ {
103 for x := 0; x < w; x++ {
104 index := 4 * (y*w + x)
105 ret.SetRGBA(x, y, color.RGBA{
106 uint8(sum[index+0] / n),
107 uint8(sum[index+1] / n),
108 uint8(sum[index+2] / n),
109 uint8(sum[index+3] / n),
116 // resizeYCbCr returns a scaled copy of the YCbCr image slice r of m.
117 // The returned image has width w and height h.
118 func resizeYCbCr(m *image.YCbCr, r image.Rectangle, w, h int) (image.Image, bool) {
120 switch m.SubsampleRatio {
121 case image.YCbCrSubsampleRatio420:
123 case image.YCbCrSubsampleRatio422:
128 ww, hh := uint64(w), uint64(h)
129 dx, dy := uint64(r.Dx()), uint64(r.Dy())
130 // See comment in Resize.
131 n, sum := dx*dy, make([]uint64, 4*w*h)
132 for y := r.Min.Y; y < r.Max.Y; y++ {
133 Y := m.Y[y*m.YStride:]
134 Cb := m.Cb[y/verticalRes*m.CStride:]
135 Cr := m.Cr[y/verticalRes*m.CStride:]
136 for x := r.Min.X; x < r.Max.X; x++ {
137 // Get the source pixel.
138 r8, g8, b8 := color.YCbCrToRGB(Y[x], Cb[x/2], Cr[x/2])
142 // Spread the source pixel over 1 or more destination rows.
144 for remy := hh; remy > 0; {
149 // Spread the source pixel over 1 or more destination columns.
151 index := 4 * ((py/dy)*ww + (px / dx))
152 for remx := ww; remx > 0; {
158 sum[index+0] += r64 * qxy
159 sum[index+1] += g64 * qxy
160 sum[index+2] += b64 * qxy
161 sum[index+3] += 0xFFFF * qxy
171 return average(sum, w, h, n), true
174 // resizeRGBA returns a scaled copy of the RGBA image slice r of m.
175 // The returned image has width w and height h.
176 func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image {
177 ww, hh := uint64(w), uint64(h)
178 dx, dy := uint64(r.Dx()), uint64(r.Dy())
179 // See comment in Resize.
180 n, sum := dx*dy, make([]uint64, 4*w*h)
181 for y := r.Min.Y; y < r.Max.Y; y++ {
182 pixOffset := m.PixOffset(r.Min.X, y)
183 for x := r.Min.X; x < r.Max.X; x++ {
184 // Get the source pixel.
185 r64 := uint64(m.Pix[pixOffset+0])
186 g64 := uint64(m.Pix[pixOffset+1])
187 b64 := uint64(m.Pix[pixOffset+2])
188 a64 := uint64(m.Pix[pixOffset+3])
190 // Spread the source pixel over 1 or more destination rows.
192 for remy := hh; remy > 0; {
197 // Spread the source pixel over 1 or more destination columns.
199 index := 4 * ((py/dy)*ww + (px / dx))
200 for remx := ww; remx > 0; {
206 sum[index+0] += r64 * qxy
207 sum[index+1] += g64 * qxy
208 sum[index+2] += b64 * qxy
209 sum[index+3] += a64 * qxy
219 return average(sum, w, h, n)
222 // Resample returns a resampled copy of the image slice r of m.
223 // The returned image has width w and height h.
224 func Resample(m image.Image, r image.Rectangle, w, h int) image.Image {
228 if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
229 return image.NewRGBA64(image.Rect(0, 0, w, h))
231 curw, curh := r.Dx(), r.Dy()
232 img := image.NewRGBA(image.Rect(0, 0, w, h))
233 for y := 0; y < h; y++ {
234 for x := 0; x < w; x++ {
235 // Get a source pixel.
238 r32, g32, b32, a32 := m.At(subx, suby).RGBA()
243 img.SetRGBA(x, y, color.RGBA{r, g, b, a})