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  }