// HalveInplace downsamples the image by 50% using averaging interpolation. func HalveInplace(m image.Image) image.Image { b := m.Bounds() switch m := m.(type) { case *image.YCbCr: for y := b.Min.Y; y < b.Max.Y/2; y++ { for x := b.Min.X; x < b.Max.X/2; x++ { y00 := uint32(m.Y[m.YOffset(2*x, 2*y)]) y10 := uint32(m.Y[m.YOffset(2*x+1, 2*y)]) y01 := uint32(m.Y[m.YOffset(2*x, 2*y+1)]) y11 := uint32(m.Y[m.YOffset(2*x+1, 2*y+1)]) // Add before divide with uint32 or we get errors in the least // significant bits. m.Y[m.YOffset(x, y)] = uint8((y00 + y10 + y01 + y11) >> 2) cb00 := uint32(m.Cb[m.COffset(2*x, 2*y)]) cb10 := uint32(m.Cb[m.COffset(2*x+1, 2*y)]) cb01 := uint32(m.Cb[m.COffset(2*x, 2*y+1)]) cb11 := uint32(m.Cb[m.COffset(2*x+1, 2*y+1)]) m.Cb[m.COffset(x, y)] = uint8((cb00 + cb10 + cb01 + cb11) >> 2) cr00 := uint32(m.Cr[m.COffset(2*x, 2*y)]) cr10 := uint32(m.Cr[m.COffset(2*x+1, 2*y)]) cr01 := uint32(m.Cr[m.COffset(2*x, 2*y+1)]) cr11 := uint32(m.Cr[m.COffset(2*x+1, 2*y+1)]) m.Cr[m.COffset(x, y)] = uint8((cr00 + cr10 + cr01 + cr11) >> 2) } } b.Max = b.Min.Add(b.Size().Div(2)) return subImage(m, b) case draw.Image: for y := b.Min.Y; y < b.Max.Y/2; y++ { for x := b.Min.X; x < b.Max.X/2; x++ { r00, g00, b00, a00 := m.At(2*x, 2*y).RGBA() r10, g10, b10, a10 := m.At(2*x+1, 2*y).RGBA() r01, g01, b01, a01 := m.At(2*x, 2*y+1).RGBA() r11, g11, b11, a11 := m.At(2*x+1, 2*y+1).RGBA() // Add before divide with uint32 or we get errors in the least // significant bits. r := (r00 + r10 + r01 + r11) >> 2 g := (g00 + g10 + g01 + g11) >> 2 b := (b00 + b10 + b01 + b11) >> 2 a := (a00 + a10 + a01 + a11) >> 2 m.Set(x, y, color.RGBA{ R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8), }) } } b.Max = b.Min.Add(b.Size().Div(2)) return subImage(m, b) default: // TODO(wathiede): fallback to generic Resample somehow? panic("Unhandled image type") } }
// ResampleInplace will resample m inplace, overwritting existing pixel data, // and return a subimage of m sized to w and h. func ResampleInplace(m image.Image, r image.Rectangle, w, h int) image.Image { // We don't support scaling up. if r.Dx() < w || r.Dy() < h { return m } switch m := m.(type) { case *image.YCbCr: xStep := float64(r.Dx()) / float64(w) yStep := float64(r.Dy()) / float64(h) for y := r.Min.Y; y < r.Min.Y+h; y++ { for x := r.Min.X; x < r.Min.X+w; x++ { xSrc := int(float64(x) * xStep) ySrc := int(float64(y) * yStep) cSrc := m.COffset(xSrc, ySrc) cDst := m.COffset(x, y) m.Y[m.YOffset(x, y)] = m.Y[m.YOffset(xSrc, ySrc)] m.Cb[cDst] = m.Cb[cSrc] m.Cr[cDst] = m.Cr[cSrc] } } case draw.Image: xStep := float64(r.Dx()) / float64(w) yStep := float64(r.Dy()) / float64(h) for y := r.Min.Y; y < r.Min.Y+h; y++ { for x := r.Min.X; x < r.Min.X+w; x++ { xSrc := int(float64(x) * xStep) ySrc := int(float64(y) * yStep) r, g, b, a := m.At(xSrc, ySrc).RGBA() m.Set(x, y, color.RGBA{ R: uint8(r >> 8), G: uint8(g >> 8), B: uint8(b >> 8), A: uint8(a >> 8), }) } } default: // TODO fallback to generic Resample somehow? panic("Unhandled image type") } r.Max.X = r.Min.X + w r.Max.Y = r.Min.Y + h return subImage(m, r) }