func getGaussianNoisePixel(k1, k2, expectation float64, pixel color.RGBA64) color.RGBA64 { gnPixel := color.RGBA64{} gnPixel.B = getGaussianNoiseValue(pixel.B, k1, k2, expectation) gnPixel.R = getGaussianNoiseValue(pixel.R, k1, k2, expectation) gnPixel.G = getGaussianNoiseValue(pixel.G, k1, k2, expectation) return gnPixel }
func getLinearNoisePixel(pixel color.RGBA64, a, b uint32) color.RGBA64 { lnPixel := color.RGBA64{} lnPixel.R = getLinearNoiseValue(pixel.R, a, b) lnPixel.G = getLinearNoiseValue(pixel.G, a, b) lnPixel.B = getLinearNoiseValue(pixel.B, a, b) return lnPixel }
func interpolateColor(c1, c2 color.Color, where float64) color.Color { r1, g1, b1, a1 := c1.RGBA() r2, g2, b2, a2 := c2.RGBA() var c color.RGBA64 c.R = uint16(float64(r2-r1)*where + float64(r1) + 0.5) c.G = uint16(float64(g2-g1)*where + float64(g1) + 0.5) c.B = uint16(float64(b2-b1)*where + float64(b1) + 0.5) c.A = uint16(float64(a2-a1)*where + float64(a1) + 0.5) return c }
func (burkes) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) { quantError0 := make([][3]int32, r.Dx()+4) quantError1 := make([][3]int32, r.Dx()+4) out := color.RGBA64{A: 0xffff} for y := 0; y != r.Dy(); y++ { for x := 0; x != r.Dx(); x++ { sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA() er, eg, eb := int32(sr), int32(sg), int32(sb) er = clamp(er + quantError0[x+2][0]/32) eg = clamp(eg + quantError0[x+2][1]/32) eb = clamp(eb + quantError0[x+2][2]/32) out.R = uint16(er) out.G = uint16(eg) out.B = uint16(eb) dst.Set(r.Min.X+x, r.Min.Y+y, &out) sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() er -= int32(sr) eg -= int32(sg) eb -= int32(sb) quantError0[x+3][0] += er * 8 quantError0[x+3][1] += eg * 8 quantError0[x+3][2] += eb * 8 quantError0[x+4][0] += er * 4 quantError0[x+4][1] += eg * 4 quantError0[x+4][2] += eb * 4 quantError1[x+0][0] += er * 2 quantError1[x+0][1] += eg * 2 quantError1[x+0][2] += eb * 2 quantError1[x+1][0] += er * 4 quantError1[x+1][1] += eg * 4 quantError1[x+1][2] += eb * 4 quantError1[x+2][0] += er * 8 quantError1[x+2][1] += eg * 8 quantError1[x+2][2] += eb * 8 quantError1[x+3][0] += er * 4 quantError1[x+3][1] += eg * 4 quantError1[x+3][2] += eb * 4 quantError1[x+4][0] += er * 2 quantError1[x+4][1] += eg * 2 quantError1[x+4][2] += eb * 2 } // Recycle the quantization error buffers. quantError0, quantError1 = quantError1, quantError0 for i := range quantError1 { quantError1[i] = [3]int32{} } } }
func (z *Rasterizer) rasterizeOpSrc(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) { z.accumulateMask() out := color.RGBA64{} outc := color.Color(&out) for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ { for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ { sr, sg, sb, sa := src.At(sp.X+x, sp.Y+y).RGBA() ma := z.bufU32[y*z.size.X+x] // This algorithm comes from the standard library's image/draw // package. out.R = uint16(sr * ma / 0xffff) out.G = uint16(sg * ma / 0xffff) out.B = uint16(sb * ma / 0xffff) out.A = uint16(sa * ma / 0xffff) dst.Set(r.Min.X+x, r.Min.Y+y, outc) } } }
func (falseFloydSteinberg) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) { quantErrorNext := make([][3]int32, r.Dx()+1) out := color.RGBA64{A: 0xffff} for y := 0; y != r.Dy(); y++ { quantError := [3]int32{} quantErrorNext[0] = [3]int32{} for x := 0; x != r.Dx(); x++ { sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA() er, eg, eb := int32(sr), int32(sg), int32(sb) er = clamp(er + (quantErrorNext[x][0]+quantError[0])/16) eg = clamp(eg + (quantErrorNext[x][1]+quantError[1])/16) eb = clamp(eb + (quantErrorNext[x][2]+quantError[2])/16) out.R = uint16(er) out.G = uint16(eg) out.B = uint16(eb) dst.Set(r.Min.X+x, r.Min.Y+y, &out) sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() er -= int32(sr) eg -= int32(sg) eb -= int32(sb) quantError[0] = er * 3 quantError[1] = eg * 3 quantError[2] = eb * 3 quantErrorNext[x+0][0] += er * 3 quantErrorNext[x+0][1] += eg * 3 quantErrorNext[x+0][2] += eb * 3 quantErrorNext[x+1][0] = er * 2 quantErrorNext[x+1][1] = eg * 2 quantErrorNext[x+1][2] = eb * 2 } } }
func bilinearGeneral(src image.Image, x, y float64) color.Color { p := findLinearSrc(src.Bounds(), x, y) var fr, fg, fb, fa float64 var r, g, b, a uint32 r, g, b, a = src.At(p.low.X, p.low.Y).RGBA() fr += float64(r) * p.frac00 fg += float64(g) * p.frac00 fb += float64(b) * p.frac00 fa += float64(a) * p.frac00 r, g, b, a = src.At(p.high.X, p.low.Y).RGBA() fr += float64(r) * p.frac01 fg += float64(g) * p.frac01 fb += float64(b) * p.frac01 fa += float64(a) * p.frac01 r, g, b, a = src.At(p.low.X, p.high.Y).RGBA() fr += float64(r) * p.frac10 fg += float64(g) * p.frac10 fb += float64(b) * p.frac10 fa += float64(a) * p.frac10 r, g, b, a = src.At(p.high.X, p.high.Y).RGBA() fr += float64(r) * p.frac11 fg += float64(g) * p.frac11 fb += float64(b) * p.frac11 fa += float64(a) * p.frac11 var c color.RGBA64 c.R = uint16(fr + 0.5) c.G = uint16(fg + 0.5) c.B = uint16(fb + 0.5) c.A = uint16(fa + 0.5) return c }
func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, floydSteinberg bool) { // TODO(nigeltao): handle the case where the dst and src overlap. // Does it even make sense to try and do Floyd-Steinberg whilst // walking the image backward (right-to-left bottom-to-top)? // If dst is an *image.Paletted, we have a fast path for dst.Set and // dst.At. The dst.Set equivalent is a batch version of the algorithm // used by color.Palette's Index method in image/color/color.go, plus // optional Floyd-Steinberg error diffusion. palette, pix, stride := [][3]int32(nil), []byte(nil), 0 if p, ok := dst.(*image.Paletted); ok { palette = make([][3]int32, len(p.Palette)) for i, col := range p.Palette { r, g, b, _ := col.RGBA() palette[i][0] = int32(r) palette[i][1] = int32(g) palette[i][2] = int32(b) } pix, stride = p.Pix[p.PixOffset(r.Min.X, r.Min.Y):], p.Stride } // quantErrorCurr and quantErrorNext are the Floyd-Steinberg quantization // errors that have been propagated to the pixels in the current and next // rows. The +2 simplifies calculation near the edges. var quantErrorCurr, quantErrorNext [][3]int32 if floydSteinberg { quantErrorCurr = make([][3]int32, r.Dx()+2) quantErrorNext = make([][3]int32, r.Dx()+2) } // Loop over each source pixel. out := color.RGBA64{A: 0xffff} for y := 0; y != r.Dy(); y++ { for x := 0; x != r.Dx(); x++ { // er, eg and eb are the pixel's R,G,B values plus the // optional Floyd-Steinberg error. sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA() er, eg, eb := int32(sr), int32(sg), int32(sb) if floydSteinberg { er = clamp(er + quantErrorCurr[x+1][0]/16) eg = clamp(eg + quantErrorCurr[x+1][1]/16) eb = clamp(eb + quantErrorCurr[x+1][2]/16) } if palette != nil { // Find the closest palette color in Euclidean R,G,B space: the // one that minimizes sum-squared-difference. We shift by 1 bit // to avoid potential uint32 overflow in sum-squared-difference. // TODO(nigeltao): consider smarter algorithms. bestIndex, bestSSD := 0, uint32(1<<32-1) for index, p := range palette { delta := (er - p[0]) >> 1 ssd := uint32(delta * delta) delta = (eg - p[1]) >> 1 ssd += uint32(delta * delta) delta = (eb - p[2]) >> 1 ssd += uint32(delta * delta) if ssd < bestSSD { bestIndex, bestSSD = index, ssd if ssd == 0 { break } } } pix[y*stride+x] = byte(bestIndex) if !floydSteinberg { continue } er -= int32(palette[bestIndex][0]) eg -= int32(palette[bestIndex][1]) eb -= int32(palette[bestIndex][2]) } else { out.R = uint16(er) out.G = uint16(eg) out.B = uint16(eb) // The third argument is &out instead of out (and out is // declared outside of the inner loop) to avoid the implicit // conversion to color.Color here allocating memory in the // inner loop if sizeof(color.RGBA64) > sizeof(uintptr). dst.Set(r.Min.X+x, r.Min.Y+y, &out) if !floydSteinberg { continue } sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() er -= int32(sr) eg -= int32(sg) eb -= int32(sb) } // Propagate the Floyd-Steinberg quantization error. quantErrorNext[x+0][0] += er * 3 quantErrorNext[x+0][1] += eg * 3 quantErrorNext[x+0][2] += eb * 3 quantErrorNext[x+1][0] += er * 5 quantErrorNext[x+1][1] += eg * 5 quantErrorNext[x+1][2] += eb * 5 quantErrorNext[x+2][0] += er * 1 quantErrorNext[x+2][1] += eg * 1 quantErrorNext[x+2][2] += eb * 1 quantErrorCurr[x+2][0] += er * 7 quantErrorCurr[x+2][1] += eg * 7 quantErrorCurr[x+2][2] += eb * 7 } // Recycle the quantization error buffers. if floydSteinberg { quantErrorCurr, quantErrorNext = quantErrorNext, quantErrorCurr for i := range quantErrorNext { quantErrorNext[i] = [3]int32{} } } } }
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { clip(dst, &r, src, &sp, mask, &mp) if r.Empty() { return } // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. switch dst0 := dst.(type) { case *image.RGBA: if op == Over { if mask == nil { switch src0 := src.(type) { case *image.Uniform: drawFillOver(dst0, r, src0) return case *image.RGBA: drawCopyOver(dst0, r, src0, sp) return case *image.NRGBA: drawNRGBAOver(dst0, r, src0, sp) return case *image.YCbCr: if drawYCbCr(dst0, r, src0, sp) { return } } } else if mask0, ok := mask.(*image.Alpha); ok { switch src0 := src.(type) { case *image.Uniform: drawGlyphOver(dst0, r, src0, mask0, mp) return } } } else { if mask == nil { switch src0 := src.(type) { case *image.Uniform: drawFillSrc(dst0, r, src0) return case *image.RGBA: drawCopySrc(dst0, r, src0, sp) return case *image.NRGBA: drawNRGBASrc(dst0, r, src0, sp) return case *image.YCbCr: if drawYCbCr(dst0, r, src0, sp) { return } } } } drawRGBA(dst0, r, src, sp, mask, mp, op) return case *image.Paletted: if op == Src && mask == nil && !processBackward(dst, r, src, sp) { drawPaletted(dst0, r, src, sp, false) } } x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 if processBackward(dst, r, src, sp) { x0, x1, dx = x1-1, x0-1, -1 y0, y1, dy = y1-1, y0-1, -1 } var out color.RGBA64 sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } switch { case ma == 0: if op == Over { // No-op. } else { dst.Set(x, y, color.Transparent) } case ma == m && op == Src: dst.Set(x, y, src.At(sx, sy)) default: sr, sg, sb, sa := src.At(sx, sy).RGBA() if op == Over { dr, dg, db, da := dst.At(x, y).RGBA() a := m - (sa * ma / m) out.R = uint16((dr*a + sr*ma) / m) out.G = uint16((dg*a + sg*ma) / m) out.B = uint16((db*a + sb*ma) / m) out.A = uint16((da*a + sa*ma) / m) } else { out.R = uint16(sr * ma / m) out.G = uint16(sg * ma / m) out.B = uint16(sb * ma / m) out.A = uint16(sa * ma / m) } // The third argument is &out instead of out (and out is // declared outside of the inner loop) to avoid the implicit // conversion to color.Color here allocating memory in the // inner loop if sizeof(color.RGBA64) > sizeof(uintptr). dst.Set(x, y, &out) } } } }
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { clip(dst, &r, src, &sp, mask, &mp) if r.Empty() { return } // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. if dst0, ok := dst.(*image.RGBA); ok { if op == Over { if mask == nil { switch src0 := src.(type) { case *image.Uniform: drawFillOver(dst0, r, src0) return case *image.RGBA: drawCopyOver(dst0, r, src0, sp) return case *image.NRGBA: drawNRGBAOver(dst0, r, src0, sp) return case *ycbcr.YCbCr: drawYCbCr(dst0, r, src0, sp) return } } else if mask0, ok := mask.(*image.Alpha); ok { switch src0 := src.(type) { case *image.Uniform: drawGlyphOver(dst0, r, src0, mask0, mp) return } } } else { if mask == nil { switch src0 := src.(type) { case *image.Uniform: drawFillSrc(dst0, r, src0) return case *image.RGBA: drawCopySrc(dst0, r, src0, sp) return case *image.NRGBA: drawNRGBASrc(dst0, r, src0, sp) return case *ycbcr.YCbCr: drawYCbCr(dst0, r, src0, sp) return } } } drawRGBA(dst0, r, src, sp, mask, mp, op) return } x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { // Rectangles overlap: process backward? if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { x0, x1, dx = x1-1, x0-1, -1 y0, y1, dy = y1-1, y0-1, -1 } } var out *color.RGBA64 sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } switch { case ma == 0: if op == Over { // No-op. } else { dst.Set(x, y, color.Transparent) } case ma == m && op == Src: dst.Set(x, y, src.At(sx, sy)) default: sr, sg, sb, sa := src.At(sx, sy).RGBA() if out == nil { out = new(color.RGBA64) } if op == Over { dr, dg, db, da := dst.At(x, y).RGBA() a := m - (sa * ma / m) out.R = uint16((dr*a + sr*ma) / m) out.G = uint16((dg*a + sg*ma) / m) out.B = uint16((db*a + sb*ma) / m) out.A = uint16((da*a + sa*ma) / m) } else { out.R = uint16(sr * ma / m) out.G = uint16(sg * ma / m) out.B = uint16(sb * ma / m) out.A = uint16(sa * ma / m) } dst.Set(x, y, out) } } } }
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. // The implementation is simple and slow. // TODO(nigeltao): Optimize this. func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) { dx, dy := src.Width()-sp.X, src.Height()-sp.Y if mask != nil { if dx > mask.Width()-mp.X { dx = mask.Width() - mp.X } if dy > mask.Height()-mp.Y { dy = mask.Height() - mp.Y } } if r.Dx() > dx { r.Max.X = r.Min.X + dx } if r.Dy() > dy { r.Max.Y = r.Min.Y + dy } // TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y. // TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y. // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. switch dst0 := dst.(type) { case *image.RGBA: if op == Over { if mask == nil { if src0, ok := src.(image.Uniform); ok { drawFillOver(dst0, r, src0) return } if src0, ok := src.(*image.RGBA); ok { if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { // TODO(nigeltao): Implement a fast path for the overlapping case. } else { drawCopyOver(dst0, r, src0, sp) return } } } else if mask0, ok := mask.(*image.Alpha); ok { if src0, ok := src.(image.Uniform); ok { drawGlyphOver(dst0, r, src0, mask0, mp) return } } } else { if mask == nil { if src0, ok := src.(image.Uniform); ok { drawFillSrc(dst0, r, src0) return } if src0, ok := src.(*image.RGBA); ok { if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { // TODO(nigeltao): Implement a fast path for the overlapping case. } else { drawCopySrc(dst0, r, src0, sp) return } } } } drawRGBA(dst0, r, src, sp, mask, mp, op) return case DrawMasker: // Destination might wish to perform the draw operation itself if dst0.DrawMask(r, src, sp, mask, mp, op) { return } } x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { // Rectangles overlap: process backward? if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { x0, x1, dx = x1-1, x0-1, -1 y0, y1, dy = y1-1, y0-1, -1 } } var out *color.RGBA64 sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } switch { case ma == 0: if op == Over { // No-op. } else { dst.Set(x, y, zeroColor) } case ma == m && op == Src: dst.Set(x, y, src.At(sx, sy)) default: sr, sg, sb, sa := src.At(sx, sy).RGBA() if out == nil { out = new(color.RGBA64) } if op == Over { dr, dg, db, da := dst.At(x, y).RGBA() a := m - (sa * ma / m) out.R = uint16((dr*a + sr*ma) / m) out.G = uint16((dg*a + sg*ma) / m) out.B = uint16((db*a + sb*ma) / m) out.A = uint16((da*a + sa*ma) / m) } else { out.R = uint16(sr * ma / m) out.G = uint16(sg * ma / m) out.B = uint16(sb * ma / m) out.A = uint16(sa * ma / m) } dst.Set(x, y, out) } } } }