github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/helper/resize.go (about) 1 package helper 2 3 import ( 4 "image" 5 "image/color" 6 ) 7 8 //调整传回的图像片的R米的缩放副本。 9 //返回的图像具有宽度w和高度h。 10 // Resize returns a scaled copy of the image slice r of m. 11 // The returned image has width w and height h. 12 func Resize(m image.Image, r image.Rectangle, w, h int) image.Image { 13 if w < 0 || h < 0 { 14 return nil 15 } 16 if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 { 17 return image.NewRGBA64(image.Rect(0, 0, w, h)) 18 } 19 switch m := m.(type) { 20 case *image.RGBA: 21 return resizeRGBA(m, r, w, h) 22 case *image.YCbCr: 23 if m, ok := resizeYCbCr(m, r, w, h); ok { 24 return m 25 } 26 } 27 ww, hh := uint64(w), uint64(h) 28 dx, dy := uint64(r.Dx()), uint64(r.Dy()) 29 // The scaling algorithm is to nearest-neighbor magnify the dx * dy source 30 // to a (ww*dx) * (hh*dy) intermediate image and then minify the intermediate 31 // image back down to a ww * hh destination with a simple box filter. 32 // The intermediate image is implied, we do not physically allocate a slice 33 // of length ww*dx*hh*dy. 34 // For example, consider a 4*3 source image. Label its pixels from a-l: 35 // abcd 36 // efgh 37 // ijkl 38 // To resize this to a 3*2 destination image, the intermediate is 12*6. 39 // Whitespace has been added to delineate the destination pixels: 40 // aaab bbcc cddd 41 // aaab bbcc cddd 42 // eeef ffgg ghhh 43 // 44 // eeef ffgg ghhh 45 // iiij jjkk klll 46 // iiij jjkk klll 47 // Thus, the 'b' source pixel contributes one third of its value to the 48 // (0, 0) destination pixel and two thirds to (1, 0). 49 // The implementation is a two-step process. First, the source pixels are 50 // iterated over and each source pixel's contribution to 1 or more 51 // destination pixels are summed. Second, the sums are divided by a scaling 52 // factor to yield the destination pixels. 53 // TODO: By interleaving the two steps, instead of doing all of 54 // step 1 first and all of step 2 second, we could allocate a smaller sum 55 // slice of length 4*w*2 instead of 4*w*h, although the resultant code 56 // would become more complicated. 57 n, sum := dx*dy, make([]uint64, 4*w*h) 58 for y := r.Min.Y; y < r.Max.Y; y++ { 59 for x := r.Min.X; x < r.Max.X; x++ { 60 // Get the source pixel. 61 r32, g32, b32, a32 := m.At(x, y).RGBA() 62 r64 := uint64(r32) 63 g64 := uint64(g32) 64 b64 := uint64(b32) 65 a64 := uint64(a32) 66 // Spread the source pixel over 1 or more destination rows. 67 py := uint64(y) * hh 68 for remy := hh; remy > 0; { 69 qy := dy - (py % dy) 70 if qy > remy { 71 qy = remy 72 } 73 // Spread the source pixel over 1 or more destination columns. 74 px := uint64(x) * ww 75 index := 4 * ((py/dy)*ww + (px / dx)) 76 for remx := ww; remx > 0; { 77 qx := dx - (px % dx) 78 if qx > remx { 79 qx = remx 80 } 81 sum[index+0] += r64 * qx * qy 82 sum[index+1] += g64 * qx * qy 83 sum[index+2] += b64 * qx * qy 84 sum[index+3] += a64 * qx * qy 85 index += 4 86 px += qx 87 remx -= qx 88 } 89 py += qy 90 remy -= qy 91 } 92 } 93 } 94 return average(sum, w, h, n*0x0101) 95 } 96 97 //平均转换平均值,并返回结果。 98 // average convert the sums to averages and returns the result. 99 func average(sum []uint64, w, h int, n uint64) image.Image { 100 ret := image.NewRGBA(image.Rect(0, 0, w, h)) 101 for y := 0; y < h; y++ { 102 for x := 0; x < w; x++ { 103 index := 4 * (y*w + x) 104 ret.SetRGBA(x, y, color.RGBA{ 105 uint8(sum[index+0] / n), 106 uint8(sum[index+1] / n), 107 uint8(sum[index+2] / n), 108 uint8(sum[index+3] / n), 109 }) 110 } 111 } 112 return ret 113 } 114 115 // resizeYCbCr的返回片的R米的RGB图像的缩放副本。 116 // 返回的图像具有宽度w和高度h。 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) * 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) * 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的返回一个缩放的属于m的RGBA图像切片。 176 // 返回的图像具有宽度w和高度h。 177 // resizeRGBA returns a scaled copy of the RGBA image slice r of m. 178 // The returned image has width w and height h. 179 func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image { 180 ww, hh := uint64(w), uint64(h) 181 dx, dy := uint64(r.Dx()), uint64(r.Dy()) 182 // See comment in Resize. 183 n, sum := dx*dy, make([]uint64, 4*w*h) 184 for y := r.Min.Y; y < r.Max.Y; y++ { 185 pixOffset := m.PixOffset(r.Min.X, y) 186 for x := r.Min.X; x < r.Max.X; x++ { 187 // Get the source pixel. 188 r64 := uint64(m.Pix[pixOffset+0]) 189 g64 := uint64(m.Pix[pixOffset+1]) 190 b64 := uint64(m.Pix[pixOffset+2]) 191 a64 := uint64(m.Pix[pixOffset+3]) 192 pixOffset += 4 193 // Spread the source pixel over 1 or more destination rows. 194 py := uint64(y) * hh 195 for remy := hh; remy > 0; { 196 qy := dy - (py % dy) 197 if qy > remy { 198 qy = remy 199 } 200 // Spread the source pixel over 1 or more destination columns. 201 px := uint64(x) * ww 202 index := 4 * ((py/dy)*ww + (px / dx)) 203 for remx := ww; remx > 0; { 204 qx := dx - (px % dx) 205 if qx > remx { 206 qx = remx 207 } 208 qxy := qx * qy 209 sum[index+0] += r64 * qxy 210 sum[index+1] += g64 * qxy 211 sum[index+2] += b64 * qxy 212 sum[index+3] += a64 * qxy 213 index += 4 214 px += qx 215 remx -= qx 216 } 217 py += qy 218 remy -= qy 219 } 220 } 221 } 222 return average(sum, w, h, n) 223 } 224 225 // 重新取样返回一个重采样本的图像切片r的副本。 226 // 返回的图像具有宽度w和高度h。 227 // Resample returns a resampled copy of the image slice r of m. 228 // The returned image has width w and height h. 229 func Resample(m image.Image, r image.Rectangle, w, h int) image.Image { 230 if w < 0 || h < 0 { 231 return nil 232 } 233 if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 { 234 return image.NewRGBA64(image.Rect(0, 0, w, h)) 235 } 236 curw, curh := r.Dx(), r.Dy() 237 img := image.NewRGBA(image.Rect(0, 0, w, h)) 238 for y := 0; y < h; y++ { 239 for x := 0; x < w; x++ { 240 // Get a source pixel. 241 subx := x * curw / w 242 suby := y * curh / h 243 r32, g32, b32, a32 := m.At(subx, suby).RGBA() 244 r := uint8(r32 >> 8) 245 g := uint8(g32 >> 8) 246 b := uint8(b32 >> 8) 247 a := uint8(a32 >> 8) 248 img.SetRGBA(x, y, color.RGBA{r, g, b, a}) 249 } 250 } 251 return img 252 }