func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) { // An image.YCbCr is always fully opaque, and so if the mask is implicitly nil // (i.e. fully opaque) then the op is effectively always Src. x0 := (r.Min.X - dst.Rect.Min.X) * 4 x1 := (r.Max.X - dst.Rect.Min.X) * 4 y0 := r.Min.Y - dst.Rect.Min.Y y1 := r.Max.Y - dst.Rect.Min.Y switch src.SubsampleRatio { case image.YCbCrSubsampleRatio422: for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { dpix := dst.Pix[y*dst.Stride:] yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2 for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { ci := ciBase + sx/2 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) dpix[x+0] = rr dpix[x+1] = gg dpix[x+2] = bb dpix[x+3] = 255 } } case image.YCbCrSubsampleRatio420: for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { dpix := dst.Pix[y*dst.Stride:] yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) ciBase := (sy/2-src.Rect.Min.Y/2)*src.CStride - src.Rect.Min.X/2 for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 { ci := ciBase + sx/2 rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) dpix[x+0] = rr dpix[x+1] = gg dpix[x+2] = bb dpix[x+3] = 255 } } default: // Default to 4:4:4 subsampling. for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 { dpix := dst.Pix[y*dst.Stride:] yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X) ci := (sy-src.Rect.Min.Y)*src.CStride + (sp.X - src.Rect.Min.X) for x := x0; x != x1; x, yi, ci = x+4, yi+1, ci+1 { rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) dpix[x+0] = rr dpix[x+1] = gg dpix[x+2] = bb dpix[x+3] = 255 } } } }
func Multicolour(p Pixel) (r, g, b uint8) { if p.Inside { return 0, 0, 0 } var y, cb, cr, iterations uint8 iterations = uint8(p.Iterations % 64) y = 193 switch { case iterations < 16: cb = iterations * 16 cr = 0 case iterations < 32: cb = 255 cr = uint8((iterations % 16) * 16) case iterations < 48: cb = uint8((15 - (iterations % 16)) * 16) cr = 255 case true: cb = 0 cr = uint8((15 - (iterations % 16)) * 16) } return color.YCbCrToRGB(y, cb, cr) }
func encodeYCbCrStream(w io.Writer, img *image.YCbCr) error { var yy, cb, cr uint8 var i, j int dx, dy := img.Rect.Dx(), img.Rect.Dy() buf := make([]byte, 3*dx*dy) bi := 0 for y := 0; y < dy; y++ { for x := 0; x < dx; x++ { i, j = x, y switch img.SubsampleRatio { case image.YCbCrSubsampleRatio420: j /= 2 fallthrough case image.YCbCrSubsampleRatio422: i /= 2 } yy = img.Y[y*img.YStride+x] cb = img.Cb[j*img.CStride+i] cr = img.Cr[j*img.CStride+i] buf[bi+0], buf[bi+1], buf[bi+2] = color.YCbCrToRGB(yy, cb, cr) bi += 3 } } _, err := w.Write(buf) return err }
func (c Color) RGBA() (r, g, b, a uint32) { r8, g8, b8 := color.YCbCrToRGB(c.Y, c.Cb, c.Cr) a = uint32(c.A) * 0x101 r = uint32(r8) * 0x101 * a / 0xffff g = uint32(g8) * 0x101 * a / 0xffff b = uint32(b8) * 0x101 * a / 0xffff return }
// resizeYCbCr returns a scaled copy of the YCbCr image slice r of m. // The returned image has width w and height h. func resizeYCbCr(m *image.YCbCr, r image.Rectangle, w, h int) (image.Image, bool) { var verticalRes int switch m.SubsampleRatio { case image.YCbCrSubsampleRatio420: verticalRes = 2 case image.YCbCrSubsampleRatio422: verticalRes = 1 default: return nil, false } ww, hh := uint64(w), uint64(h) dx, dy := uint64(r.Dx()), uint64(r.Dy()) // See comment in Resize. n, sum := dx*dy, make([]uint64, 4*w*h) for y := r.Min.Y; y < r.Max.Y; y++ { Y := m.Y[y*m.YStride:] Cb := m.Cb[y/verticalRes*m.CStride:] Cr := m.Cr[y/verticalRes*m.CStride:] for x := r.Min.X; x < r.Max.X; x++ { // Get the source pixel. r8, g8, b8 := color.YCbCrToRGB(Y[x], Cb[x/2], Cr[x/2]) r64 := uint64(r8) g64 := uint64(g8) b64 := uint64(b8) // Spread the source pixel over 1 or more destination rows. py := uint64(y) * hh for remy := hh; remy > 0; { qy := dy - (py % dy) if qy > remy { qy = remy } // Spread the source pixel over 1 or more destination columns. px := uint64(x) * ww index := 4 * ((py/dy)*ww + (px / dx)) for remx := ww; remx > 0; { qx := dx - (px % dx) if qx > remx { qx = remx } qxy := qx * qy sum[index+0] += r64 * qxy sum[index+1] += g64 * qxy sum[index+2] += b64 * qxy sum[index+3] += 0xFFFF * qxy index += 4 px += qx remx -= qx } py += qy remy -= qy } } } return average(sum, w, h, n), true }
func (c *ycbcrConverter) at(x, y int, result *colorArray) { xx, yy := replicateBorder(x, y, c.src.Rect) yi := c.src.YOffset(xx, yy) ci := c.src.COffset(xx, yy) r, g, b := color.YCbCrToRGB(c.src.Y[yi], c.src.Cb[ci], c.src.Cr[ci]) result[0] = float32(uint16(r) * 0x101) result[1] = float32(uint16(g) * 0x101) result[2] = float32(uint16(b) * 0x101) result[3] = float32(0xffff) return }
func (c *ycbcrConverter) at(x, y int) colorArray { xx, yy := replicateBorder(x, y, c.src.Rect) yi := c.src.YOffset(xx, yy) ci := c.src.COffset(xx, yy) r, g, b := color.YCbCrToRGB(c.src.Y[yi], c.src.Cb[ci], c.src.Cr[ci]) return colorArray{ float32(uint16(r) * 0x101), float32(uint16(g) * 0x101), float32(uint16(b) * 0x101), float32(0xffff), } }
func (c *ycbcrConverter) at(x, y int) colorArray { if !(image.Point{x, y}.In(c.src.Rect)) { return colorArray{0, 0, 0, 0} } yi := c.src.YOffset(x, y) ci := c.src.COffset(x, y) r, g, b := color.YCbCrToRGB(c.src.Y[yi], c.src.Cb[ci], c.src.Cr[ci]) return colorArray{ float32(uint16(r) * 0x101), float32(uint16(g) * 0x101), float32(uint16(b) * 0x101), float32(0xffff), } }
func convertYCbCr(dest *Image, src *image.YCbCr) { var r, g, b uint8 var x, y, i, yi, ci int for x = dest.Rect.Min.X; x < dest.Rect.Max.X; x++ { for y = dest.Rect.Min.Y; y < dest.Rect.Max.Y; y++ { yi, ci = src.YOffset(x, y), src.COffset(x, y) r, g, b = color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci]) i = dest.PixOffset(x, y) dest.Pix[i+0] = b dest.Pix[i+1] = g dest.Pix[i+2] = r dest.Pix[i+3] = 0xff } } }
func HistogramYCbCr(img *image.YCbCr, rect image.Rectangle, rgb bool) []int { var verticalRes int var horizontalRes int switch img.SubsampleRatio { case image.YCbCrSubsampleRatio420: verticalRes = 2 horizontalRes = 2 case image.YCbCrSubsampleRatio422: verticalRes = 1 horizontalRes = 2 case image.YCbCrSubsampleRatio444: verticalRes = 1 horizontalRes = 1 default: return nil } hist := make([]int, 256*3) for y := rect.Min.Y; y < rect.Max.Y; y++ { Y := img.Y[y*img.YStride:] Cb := img.Cb[y/verticalRes*img.CStride:] Cr := img.Cr[y/verticalRes*img.CStride:] for x := rect.Min.X; x < rect.Max.X; x++ { if rgb { r, g, b := color.YCbCrToRGB(Y[x], Cb[x/horizontalRes], Cr[x/horizontalRes]) hist[r] += 1 hist[int(g)+256] += 1 hist[int(b)+512] += 1 } else { hist[Y[x]] += 1 hist[int(Cb[x/horizontalRes])+256] += 1 hist[int(Cr[x/horizontalRes])+512] += 1 } } } return hist }
// Clone returns a copy of the given image. func Clone(img image.Image) *image.NRGBA { srcBounds := img.Bounds() srcMinX := srcBounds.Min.X srcMinY := srcBounds.Min.Y dstBounds := srcBounds.Sub(srcBounds.Min) dstW := dstBounds.Dx() dstH := dstBounds.Dy() dst := image.NewNRGBA(dstBounds) switch src := img.(type) { case *image.NRGBA: rowSize := srcBounds.Dx() * 4 parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) si := src.PixOffset(srcMinX, srcMinY+dstY) copy(dst.Pix[di:di+rowSize], src.Pix[si:si+rowSize]) } }) case *image.NRGBA64: parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) si := src.PixOffset(srcMinX, srcMinY+dstY) for dstX := 0; dstX < dstW; dstX++ { dst.Pix[di+0] = src.Pix[si+0] dst.Pix[di+1] = src.Pix[si+2] dst.Pix[di+2] = src.Pix[si+4] dst.Pix[di+3] = src.Pix[si+6] di += 4 si += 8 } } }) case *image.RGBA: parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) si := src.PixOffset(srcMinX, srcMinY+dstY) for dstX := 0; dstX < dstW; dstX++ { a := src.Pix[si+3] dst.Pix[di+3] = a switch a { case 0: dst.Pix[di+0] = 0 dst.Pix[di+1] = 0 dst.Pix[di+2] = 0 case 0xff: dst.Pix[di+0] = src.Pix[si+0] dst.Pix[di+1] = src.Pix[si+1] dst.Pix[di+2] = src.Pix[si+2] default: dst.Pix[di+0] = uint8(uint16(src.Pix[si+0]) * 0xff / uint16(a)) dst.Pix[di+1] = uint8(uint16(src.Pix[si+1]) * 0xff / uint16(a)) dst.Pix[di+2] = uint8(uint16(src.Pix[si+2]) * 0xff / uint16(a)) } di += 4 si += 4 } } }) case *image.RGBA64: parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) si := src.PixOffset(srcMinX, srcMinY+dstY) for dstX := 0; dstX < dstW; dstX++ { a := src.Pix[si+6] dst.Pix[di+3] = a switch a { case 0: dst.Pix[di+0] = 0 dst.Pix[di+1] = 0 dst.Pix[di+2] = 0 case 0xff: dst.Pix[di+0] = src.Pix[si+0] dst.Pix[di+1] = src.Pix[si+2] dst.Pix[di+2] = src.Pix[si+4] default: dst.Pix[di+0] = uint8(uint16(src.Pix[si+0]) * 0xff / uint16(a)) dst.Pix[di+1] = uint8(uint16(src.Pix[si+2]) * 0xff / uint16(a)) dst.Pix[di+2] = uint8(uint16(src.Pix[si+4]) * 0xff / uint16(a)) } di += 4 si += 8 } } }) case *image.Gray: parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) si := src.PixOffset(srcMinX, srcMinY+dstY) for dstX := 0; dstX < dstW; dstX++ { c := src.Pix[si] dst.Pix[di+0] = c dst.Pix[di+1] = c dst.Pix[di+2] = c dst.Pix[di+3] = 0xff di += 4 si += 1 } } }) case *image.Gray16: parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) si := src.PixOffset(srcMinX, srcMinY+dstY) for dstX := 0; dstX < dstW; dstX++ { c := src.Pix[si] dst.Pix[di+0] = c dst.Pix[di+1] = c dst.Pix[di+2] = c dst.Pix[di+3] = 0xff di += 4 si += 2 } } }) case *image.YCbCr: parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) switch src.SubsampleRatio { case image.YCbCrSubsampleRatio422: siy0 := dstY * src.YStride sic0 := dstY * src.CStride for dstX := 0; dstX < dstW; dstX = dstX + 1 { siy := siy0 + dstX sic := sic0 + ((srcMinX+dstX)/2 - srcMinX/2) r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic]) dst.Pix[di+0] = r dst.Pix[di+1] = g dst.Pix[di+2] = b dst.Pix[di+3] = 0xff di += 4 } case image.YCbCrSubsampleRatio420: siy0 := dstY * src.YStride sic0 := ((srcMinY+dstY)/2 - srcMinY/2) * src.CStride for dstX := 0; dstX < dstW; dstX = dstX + 1 { siy := siy0 + dstX sic := sic0 + ((srcMinX+dstX)/2 - srcMinX/2) r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic]) dst.Pix[di+0] = r dst.Pix[di+1] = g dst.Pix[di+2] = b dst.Pix[di+3] = 0xff di += 4 } case image.YCbCrSubsampleRatio440: siy0 := dstY * src.YStride sic0 := ((srcMinY+dstY)/2 - srcMinY/2) * src.CStride for dstX := 0; dstX < dstW; dstX = dstX + 1 { siy := siy0 + dstX sic := sic0 + dstX r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic]) dst.Pix[di+0] = r dst.Pix[di+1] = g dst.Pix[di+2] = b dst.Pix[di+3] = 0xff di += 4 } default: siy0 := dstY * src.YStride sic0 := dstY * src.CStride for dstX := 0; dstX < dstW; dstX++ { siy := siy0 + dstX sic := sic0 + dstX r, g, b := color.YCbCrToRGB(src.Y[siy], src.Cb[sic], src.Cr[sic]) dst.Pix[di+0] = r dst.Pix[di+1] = g dst.Pix[di+2] = b dst.Pix[di+3] = 0xff di += 4 } } } }) case *image.Paletted: plen := len(src.Palette) pnew := make([]color.NRGBA, plen) for i := 0; i < plen; i++ { pnew[i] = color.NRGBAModel.Convert(src.Palette[i]).(color.NRGBA) } parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) si := src.PixOffset(srcMinX, srcMinY+dstY) for dstX := 0; dstX < dstW; dstX++ { c := pnew[src.Pix[si]] dst.Pix[di+0] = c.R dst.Pix[di+1] = c.G dst.Pix[di+2] = c.B dst.Pix[di+3] = c.A di += 4 si += 1 } } }) default: parallel(dstH, func(partStart, partEnd int) { for dstY := partStart; dstY < partEnd; dstY++ { di := dst.PixOffset(0, dstY) for dstX := 0; dstX < dstW; dstX++ { c := color.NRGBAModel.Convert(img.At(srcMinX+dstX, srcMinY+dstY)).(color.NRGBA) dst.Pix[di+0] = c.R dst.Pix[di+1] = c.G dst.Pix[di+2] = c.B dst.Pix[di+3] = c.A di += 4 } } }) } return dst }
// Clone returns a copy of the given image. func Clone(img image.Image) *image.NRGBA { srcBounds := img.Bounds() dstBounds := srcBounds.Sub(srcBounds.Min) dst := image.NewNRGBA(dstBounds) dstMinX := dstBounds.Min.X dstMinY := dstBounds.Min.Y srcMinX := srcBounds.Min.X srcMinY := srcBounds.Min.Y srcMaxX := srcBounds.Max.X srcMaxY := srcBounds.Max.Y switch src0 := img.(type) { case *image.NRGBA: rowSize := srcBounds.Dx() * 4 numRows := srcBounds.Dy() i0 := dst.PixOffset(dstMinX, dstMinY) j0 := src0.PixOffset(srcMinX, srcMinY) di := dst.Stride dj := src0.Stride for row := 0; row < numRows; row++ { copy(dst.Pix[i0:i0+rowSize], src0.Pix[j0:j0+rowSize]) i0 += di j0 += dj } case *image.NRGBA64: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) dst.Pix[i+0] = src0.Pix[j+0] dst.Pix[i+1] = src0.Pix[j+2] dst.Pix[i+2] = src0.Pix[j+4] dst.Pix[i+3] = src0.Pix[j+6] } } case *image.RGBA: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) a := src0.Pix[j+3] dst.Pix[i+3] = a switch a { case 0: dst.Pix[i+0] = 0 dst.Pix[i+1] = 0 dst.Pix[i+2] = 0 case 0xff: dst.Pix[i+0] = src0.Pix[j+0] dst.Pix[i+1] = src0.Pix[j+1] dst.Pix[i+2] = src0.Pix[j+2] default: dst.Pix[i+0] = uint8(uint16(src0.Pix[j+0]) * 0xff / uint16(a)) dst.Pix[i+1] = uint8(uint16(src0.Pix[j+1]) * 0xff / uint16(a)) dst.Pix[i+2] = uint8(uint16(src0.Pix[j+2]) * 0xff / uint16(a)) } } } case *image.RGBA64: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) a := src0.Pix[j+6] dst.Pix[i+3] = a switch a { case 0: dst.Pix[i+0] = 0 dst.Pix[i+1] = 0 dst.Pix[i+2] = 0 case 0xff: dst.Pix[i+0] = src0.Pix[j+0] dst.Pix[i+1] = src0.Pix[j+2] dst.Pix[i+2] = src0.Pix[j+4] default: dst.Pix[i+0] = uint8(uint16(src0.Pix[j+0]) * 0xff / uint16(a)) dst.Pix[i+1] = uint8(uint16(src0.Pix[j+2]) * 0xff / uint16(a)) dst.Pix[i+2] = uint8(uint16(src0.Pix[j+4]) * 0xff / uint16(a)) } } } case *image.Gray: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) c := src0.Pix[j] dst.Pix[i+0] = c dst.Pix[i+1] = c dst.Pix[i+2] = c dst.Pix[i+3] = 0xff } } case *image.Gray16: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) c := src0.Pix[j] dst.Pix[i+0] = c dst.Pix[i+1] = c dst.Pix[i+2] = c dst.Pix[i+3] = 0xff } } case *image.YCbCr: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { yj := src0.YOffset(x, y) cj := src0.COffset(x, y) r, g, b := color.YCbCrToRGB(src0.Y[yj], src0.Cb[cj], src0.Cr[cj]) dst.Pix[i+0] = r dst.Pix[i+1] = g dst.Pix[i+2] = b dst.Pix[i+3] = 0xff } } case *image.Paletted: plen := len(src0.Palette) pnew := make([]color.NRGBA, plen) for i := 0; i < plen; i++ { pnew[i] = color.NRGBAModel.Convert(src0.Palette[i]).(color.NRGBA) } i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) c := pnew[src0.Pix[j]] dst.Pix[i+0] = c.R dst.Pix[i+1] = c.G dst.Pix[i+2] = c.B dst.Pix[i+3] = c.A } } default: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) dst.Pix[i+0] = c.R dst.Pix[i+1] = c.G dst.Pix[i+2] = c.B dst.Pix[i+3] = c.A } } } return dst }
func (p *pixelGetter) getPixel(x, y int) (px pixel) { switch p.imgType { case itNRGBA: i := p.imgNRGBA.PixOffset(x, y) r := float32(p.imgNRGBA.Pix[i+0]) * qf8 g := float32(p.imgNRGBA.Pix[i+1]) * qf8 b := float32(p.imgNRGBA.Pix[i+2]) * qf8 a := float32(p.imgNRGBA.Pix[i+3]) * qf8 px = pixel{r, g, b, a} case itNRGBA64: i := p.imgNRGBA64.PixOffset(x, y) r := float32(uint16(p.imgNRGBA64.Pix[i+0])<<8|uint16(p.imgNRGBA64.Pix[i+1])) * qf16 g := float32(uint16(p.imgNRGBA64.Pix[i+2])<<8|uint16(p.imgNRGBA64.Pix[i+3])) * qf16 b := float32(uint16(p.imgNRGBA64.Pix[i+4])<<8|uint16(p.imgNRGBA64.Pix[i+5])) * qf16 a := float32(uint16(p.imgNRGBA64.Pix[i+6])<<8|uint16(p.imgNRGBA64.Pix[i+7])) * qf16 px = pixel{r, g, b, a} case itRGBA: i := p.imgRGBA.PixOffset(x, y) a8 := p.imgRGBA.Pix[i+3] switch a8 { case 0: px = pixel{0.0, 0.0, 0.0, 0.0} case 255: r := float32(p.imgRGBA.Pix[i+0]) * qf8 g := float32(p.imgRGBA.Pix[i+1]) * qf8 b := float32(p.imgRGBA.Pix[i+2]) * qf8 px = pixel{r, g, b, 1.0} default: q := float32(1.0) / float32(a8) r := float32(p.imgRGBA.Pix[i+0]) * q g := float32(p.imgRGBA.Pix[i+1]) * q b := float32(p.imgRGBA.Pix[i+2]) * q a := float32(a8) * qf8 px = pixel{r, g, b, a} } case itRGBA64: i := p.imgRGBA64.PixOffset(x, y) a16 := uint16(p.imgRGBA64.Pix[i+6])<<8 | uint16(p.imgRGBA64.Pix[i+7]) switch a16 { case 0: px = pixel{0.0, 0.0, 0.0, 0.0} case 65535: r := float32(uint16(p.imgRGBA64.Pix[i+0])<<8|uint16(p.imgRGBA64.Pix[i+1])) * qf16 g := float32(uint16(p.imgRGBA64.Pix[i+2])<<8|uint16(p.imgRGBA64.Pix[i+3])) * qf16 b := float32(uint16(p.imgRGBA64.Pix[i+4])<<8|uint16(p.imgRGBA64.Pix[i+5])) * qf16 px = pixel{r, g, b, 1.0} default: q := float32(1.0) / float32(a16) r := float32(uint16(p.imgRGBA64.Pix[i+0])<<8|uint16(p.imgRGBA64.Pix[i+1])) * q g := float32(uint16(p.imgRGBA64.Pix[i+2])<<8|uint16(p.imgRGBA64.Pix[i+3])) * q b := float32(uint16(p.imgRGBA64.Pix[i+4])<<8|uint16(p.imgRGBA64.Pix[i+5])) * q a := float32(a16) * qf16 px = pixel{r, g, b, a} } case itGray: i := p.imgGray.PixOffset(x, y) v := float32(p.imgGray.Pix[i]) * qf8 px = pixel{v, v, v, 1.0} case itGray16: i := p.imgGray16.PixOffset(x, y) v := float32(uint16(p.imgGray16.Pix[i+0])<<8|uint16(p.imgGray16.Pix[i+1])) * qf16 px = pixel{v, v, v, 1.0} case itYCbCr: iy := p.imgYCbCr.YOffset(x, y) ic := p.imgYCbCr.COffset(x, y) r8, g8, b8 := color.YCbCrToRGB(p.imgYCbCr.Y[iy], p.imgYCbCr.Cb[ic], p.imgYCbCr.Cr[ic]) r := float32(r8) * qf8 g := float32(g8) * qf8 b := float32(b8) * qf8 px = pixel{r, g, b, 1.0} case itPaletted: i := p.imgPaletted.PixOffset(x, y) k := p.imgPaletted.Pix[i] px = p.imgPalette[k] case itGeneric: r16, g16, b16, a16 := p.imgGeneric.At(x, y).RGBA() switch a16 { case 0: px = pixel{0.0, 0.0, 0.0, 0.0} case 65535: r := float32(r16) * qf16 g := float32(g16) * qf16 b := float32(b16) * qf16 px = pixel{r, g, b, 1.0} default: q := float32(1.0) / float32(a16) r := float32(r16) * q g := float32(g16) * q b := float32(b16) * q a := float32(a16) * qf16 px = pixel{r, g, b, a} } } return }
// This function converts any image type to *image.NRGBA for faster pixel access // Optimized for most standard image types: NRGBA64, RGBA, RGBA64, YCbCr, Gray, Gray16 // If clone is true, the new image bounds will start at (0,0), also, a new copy // will be created even if the source image's type is already NRGBA func toNRGBA(src image.Image, clone bool) *image.NRGBA { if !clone { if src0, ok := src.(*image.NRGBA); ok { return src0 } } srcBounds := src.Bounds() dstBounds := srcBounds // if we need a copy - translate Min point to (0, 0) if clone { dstBounds = dstBounds.Sub(dstBounds.Min) } dst := image.NewNRGBA(dstBounds) dstMinX := dstBounds.Min.X dstMinY := dstBounds.Min.Y srcMinX := srcBounds.Min.X srcMinY := srcBounds.Min.Y srcMaxX := srcBounds.Max.X srcMaxY := srcBounds.Max.Y switch src0 := src.(type) { case *image.NRGBA: rowSize := srcBounds.Dx() * 4 numRows := srcBounds.Dy() i0 := dst.PixOffset(dstMinX, dstMinY) j0 := src0.PixOffset(srcMinX, srcMinY) di := dst.Stride dj := src0.Stride for row := 0; row < numRows; row++ { copy(dst.Pix[i0:i0+rowSize], src0.Pix[j0:j0+rowSize]) i0 += di j0 += dj } case *image.NRGBA64: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) dst.Pix[i+0] = src0.Pix[j+0] dst.Pix[i+1] = src0.Pix[j+2] dst.Pix[i+2] = src0.Pix[j+4] dst.Pix[i+3] = src0.Pix[j+6] } } case *image.RGBA: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) a := src0.Pix[j+3] dst.Pix[i+3] = a switch a { case 0: dst.Pix[i+0] = 0 dst.Pix[i+1] = 0 dst.Pix[i+2] = 0 case 0xff: dst.Pix[i+0] = src0.Pix[j+0] dst.Pix[i+1] = src0.Pix[j+1] dst.Pix[i+2] = src0.Pix[j+2] default: dst.Pix[i+0] = uint8(uint16(src0.Pix[j+0]) * 0xff / uint16(a)) dst.Pix[i+1] = uint8(uint16(src0.Pix[j+1]) * 0xff / uint16(a)) dst.Pix[i+2] = uint8(uint16(src0.Pix[j+2]) * 0xff / uint16(a)) } } } case *image.RGBA64: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) a := src0.Pix[j+6] dst.Pix[i+3] = a switch a { case 0: dst.Pix[i+0] = 0 dst.Pix[i+1] = 0 dst.Pix[i+2] = 0 case 0xff: dst.Pix[i+0] = src0.Pix[j+0] dst.Pix[i+1] = src0.Pix[j+2] dst.Pix[i+2] = src0.Pix[j+4] default: dst.Pix[i+0] = uint8(uint16(src0.Pix[j+0]) * 0xff / uint16(a)) dst.Pix[i+1] = uint8(uint16(src0.Pix[j+2]) * 0xff / uint16(a)) dst.Pix[i+2] = uint8(uint16(src0.Pix[j+4]) * 0xff / uint16(a)) } } } case *image.Gray: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) c := src0.Pix[j] dst.Pix[i+0] = c dst.Pix[i+1] = c dst.Pix[i+2] = c dst.Pix[i+3] = 0xff } } case *image.Gray16: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { j := src0.PixOffset(x, y) c := src0.Pix[j] dst.Pix[i+0] = c dst.Pix[i+1] = c dst.Pix[i+2] = c dst.Pix[i+3] = 0xff } } case *image.YCbCr: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { yj := src0.YOffset(x, y) cj := src0.COffset(x, y) r, g, b := color.YCbCrToRGB(src0.Y[yj], src0.Cb[cj], src0.Cr[cj]) dst.Pix[i+0] = r dst.Pix[i+1] = g dst.Pix[i+2] = b dst.Pix[i+3] = 0xff } } default: i0 := dst.PixOffset(dstMinX, dstMinY) for y := srcMinY; y < srcMaxY; y, i0 = y+1, i0+dst.Stride { for x, i := srcMinX, i0; x < srcMaxX; x, i = x+1, i+4 { c := color.NRGBAModel.Convert(src.At(x, y)).(color.NRGBA) dst.Pix[i+0] = c.R dst.Pix[i+1] = c.G dst.Pix[i+2] = c.B dst.Pix[i+3] = c.A } } } return dst }