// and the X Render extension.
package draw
-import "image"
+import (
+ "image"
+ "image/ycbcr"
+)
// m is the maximum color value returned by image.Color.RGBA.
const m = 1<<16 - 1
if dst0, ok := dst.(*image.RGBA); ok {
if op == Over {
if mask == nil {
- if src0, ok := src.(*image.ColorImage); ok {
+ switch src0 := src.(type) {
+ case *image.ColorImage:
drawFillOver(dst0, r, src0)
return
- }
- if src0, ok := src.(*image.RGBA); ok {
+ 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 {
- if src0, ok := src.(*image.ColorImage); ok {
+ switch src0 := src.(type) {
+ case *image.ColorImage:
drawGlyphOver(dst0, r, src0, mask0, mp)
return
}
}
} else {
if mask == nil {
- if src0, ok := src.(*image.ColorImage); ok {
+ switch src0 := src.(type) {
+ case *image.ColorImage:
drawFillSrc(dst0, r, src0)
return
- }
- if src0, ok := src.(*image.RGBA); ok {
+ 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
}
}
}
}
}
+func drawNRGBAOver(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
+ for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
+ dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
+ spix := src.Pix[sy*src.Stride : (sy+1)*src.Stride]
+ for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
+ // Convert from non-premultiplied color to pre-multiplied color.
+ // The order of operations here is to match the NRGBAColor.RGBA
+ // method in image/color.go.
+ snrgba := spix[sx]
+ sa := uint32(snrgba.A)
+ sr := uint32(snrgba.R) * 0x101 * sa / 0xff
+ sg := uint32(snrgba.G) * 0x101 * sa / 0xff
+ sb := uint32(snrgba.B) * 0x101 * sa / 0xff
+ sa *= 0x101
+
+ rgba := dpix[x]
+ dr := uint32(rgba.R)
+ dg := uint32(rgba.G)
+ db := uint32(rgba.B)
+ da := uint32(rgba.A)
+ a := (m - sa) * 0x101
+ dr = (dr*a + sr*m) / m
+ dg = (dg*a + sg*m) / m
+ db = (db*a + sb*m) / m
+ da = (da*a + sa*m) / m
+ dpix[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
+ }
+ }
+}
+
func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.ColorImage, mask *image.Alpha, mp image.Point) {
x0, x1 := r.Min.X, r.Max.X
y0, y1 := r.Min.Y, r.Max.Y
}
}
+func drawNRGBASrc(dst *image.RGBA, r image.Rectangle, src *image.NRGBA, sp image.Point) {
+ for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
+ dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
+ spix := src.Pix[sy*src.Stride : (sy+1)*src.Stride]
+ for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
+ // Convert from non-premultiplied color to pre-multiplied color.
+ // The order of operations here is to match the NRGBAColor.RGBA
+ // method in image/color.go.
+ snrgba := spix[sx]
+ sa := uint32(snrgba.A)
+ sr := uint32(snrgba.R) * 0x101 * sa / 0xff
+ sg := uint32(snrgba.G) * 0x101 * sa / 0xff
+ sb := uint32(snrgba.B) * 0x101 * sa / 0xff
+ sa *= 0x101
+
+ dpix[x] = image.RGBAColor{uint8(sr >> 8), uint8(sg >> 8), uint8(sb >> 8), uint8(sa >> 8)}
+ }
+ }
+}
+
+func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Point) {
+ // A YCbCr image is always fully opaque, and so if the mask is implicitly nil
+ // (i.e. fully opaque) then the op is effectively always Src.
+ var (
+ yy, cb, cr uint8
+ rr, gg, bb uint8
+ )
+ switch src.SubsampleRatio {
+ case ycbcr.SubsampleRatio422:
+ for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
+ dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
+ for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
+ i := sx / 2
+ yy = src.Y[sy*src.YStride+sx]
+ cb = src.Cb[sy*src.CStride+i]
+ cr = src.Cr[sy*src.CStride+i]
+ rr, gg, bb = ycbcr.YCbCrToRGB(yy, cb, cr)
+ dpix[x] = image.RGBAColor{rr, gg, bb, 255}
+ }
+ }
+ case ycbcr.SubsampleRatio420:
+ for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
+ dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
+ for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
+ i, j := sx/2, sy/2
+ yy = src.Y[sy*src.YStride+sx]
+ cb = src.Cb[j*src.CStride+i]
+ cr = src.Cr[j*src.CStride+i]
+ rr, gg, bb = ycbcr.YCbCrToRGB(yy, cb, cr)
+ dpix[x] = image.RGBAColor{rr, gg, bb, 255}
+ }
+ }
+ default:
+ // Default to 4:4:4 subsampling.
+ for y, sy := r.Min.Y, sp.Y; y != r.Max.Y; y, sy = y+1, sy+1 {
+ dpix := dst.Pix[y*dst.Stride : (y+1)*dst.Stride]
+ for x, sx := r.Min.X, sp.X; x != r.Max.X; x, sx = x+1, sx+1 {
+ yy = src.Y[sy*src.YStride+sx]
+ cb = src.Cb[sy*src.CStride+sx]
+ cr = src.Cr[sy*src.CStride+sx]
+ rr, gg, bb = ycbcr.YCbCrToRGB(yy, cb, cr)
+ dpix[x] = image.RGBAColor{rr, gg, bb, 255}
+ }
+ }
+ }
+}
+
func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) {
x0, x1, dx := r.Min.X, r.Max.X, 1
y0, y1, dy := r.Min.Y, r.Max.Y, 1
import (
"image"
+ "image/ycbcr"
"testing"
)
return m
}
+func vgradGreenNRGBA(alpha int) image.Image {
+ m := image.NewNRGBA(16, 16)
+ for y := 0; y < 16; y++ {
+ for x := 0; x < 16; x++ {
+ m.Set(x, y, image.RGBAColor{0, uint8(y * 0x11), 0, uint8(alpha)})
+ }
+ }
+ return m
+}
+
+func vgradCr() image.Image {
+ m := &ycbcr.YCbCr{
+ Y: make([]byte, 16*16),
+ Cb: make([]byte, 16*16),
+ Cr: make([]byte, 16*16),
+ YStride: 16,
+ CStride: 16,
+ SubsampleRatio: ycbcr.SubsampleRatio444,
+ Rect: image.Rect(0, 0, 16, 16),
+ }
+ for y := 0; y < 16; y++ {
+ for x := 0; x < 16; x++ {
+ m.Cr[y*m.CStride+x] = uint8(y * 0x11)
+ }
+ }
+ return m
+}
+
func hgradRed(alpha int) Image {
m := image.NewRGBA(16, 16)
for y := 0; y < 16; y++ {
{"copyAlphaSrc", vgradGreen(90), fillAlpha(192), Src, image.RGBAColor{0, 36, 0, 68}},
{"copyNil", vgradGreen(90), nil, Over, image.RGBAColor{88, 48, 0, 255}},
{"copyNilSrc", vgradGreen(90), nil, Src, image.RGBAColor{0, 48, 0, 90}},
+ // Uniform mask (100%, 75%, nil) and variable NRGBA source.
+ // At (x, y) == (8, 8):
+ // The destination pixel is {136, 0, 0, 255}.
+ // The source pixel is {0, 136, 0, 90} in NRGBA-space, which is {0, 48, 0, 90} in RGBA-space.
+ // The result pixel is different than in the "copy*" test cases because of rounding errors.
+ {"nrgba", vgradGreenNRGBA(90), fillAlpha(255), Over, image.RGBAColor{88, 46, 0, 255}},
+ {"nrgbaSrc", vgradGreenNRGBA(90), fillAlpha(255), Src, image.RGBAColor{0, 46, 0, 90}},
+ {"nrgbaAlpha", vgradGreenNRGBA(90), fillAlpha(192), Over, image.RGBAColor{100, 34, 0, 255}},
+ {"nrgbaAlphaSrc", vgradGreenNRGBA(90), fillAlpha(192), Src, image.RGBAColor{0, 34, 0, 68}},
+ {"nrgbaNil", vgradGreenNRGBA(90), nil, Over, image.RGBAColor{88, 46, 0, 255}},
+ {"nrgbaNilSrc", vgradGreenNRGBA(90), nil, Src, image.RGBAColor{0, 46, 0, 90}},
+ // Uniform mask (100%, 75%, nil) and variable YCbCr source.
+ // At (x, y) == (8, 8):
+ // The destination pixel is {136, 0, 0, 255}.
+ // The source pixel is {0, 0, 136} in YCbCr-space, which is {11, 38, 0, 255} in RGB-space.
+ {"ycbcr", vgradCr(), fillAlpha(255), Over, image.RGBAColor{11, 38, 0, 255}},
+ {"ycbcrSrc", vgradCr(), fillAlpha(255), Src, image.RGBAColor{11, 38, 0, 255}},
+ {"ycbcrAlpha", vgradCr(), fillAlpha(192), Over, image.RGBAColor{42, 28, 0, 255}},
+ {"ycbcrAlphaSrc", vgradCr(), fillAlpha(192), Src, image.RGBAColor{8, 28, 0, 192}},
+ {"ycbcrNil", vgradCr(), nil, Over, image.RGBAColor{11, 38, 0, 255}},
+ {"ycbcrNilSrc", vgradCr(), nil, Src, image.RGBAColor{11, 38, 0, 255}},
// Variable mask and variable source.
// At (x, y) == (8, 8):
// The destination pixel is {136, 0, 0, 255}.