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  }