2010-02-10 19:38:16 -07:00
|
|
|
// Copyright 2010 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package draw
|
|
|
|
|
|
|
|
import (
|
|
|
|
"image"
|
exp/draw: fast paths for drawing a YCbCr or an NRGBA onto an RGBA.
On my laptop, I had an 800x600 jpeg and an 800x600 png (with
transparency). I timed how long it took to draw each image onto an
equivalently sized, zeroed RGBA image.
Previously, the jpeg took 75ms and the png took 70ms, going through
the medium-fast path, i.e. func drawRGBA in draw.go.
After this CL, the jpeg took 14ms, and the png took 21ms with the
Over operator and 12ms with the Src operator.
It's only a rough estimate basd on one image file, but it should
give an idea of the order of magnitude of improvement.
R=rsc, r
CC=adg, golang-dev
https://golang.org/cl/4468044
2011-05-04 11:17:53 -06:00
|
|
|
"image/ycbcr"
|
2010-02-10 19:38:16 -07:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func eq(c0, c1 image.Color) bool {
|
|
|
|
r0, g0, b0, a0 := c0.RGBA()
|
|
|
|
r1, g1, b1, a1 := c1.RGBA()
|
|
|
|
return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1
|
|
|
|
}
|
|
|
|
|
|
|
|
func fillBlue(alpha int) image.Image {
|
2010-10-11 21:05:50 -06:00
|
|
|
return image.NewColorImage(image.RGBAColor{0, 0, uint8(alpha), uint8(alpha)})
|
2010-02-10 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func fillAlpha(alpha int) image.Image {
|
2010-10-11 21:05:50 -06:00
|
|
|
return image.NewColorImage(image.AlphaColor{uint8(alpha)})
|
2010-02-10 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func vgradGreen(alpha int) image.Image {
|
2011-09-14 06:09:46 -06:00
|
|
|
m := image.NewRGBA(image.Rect(0, 0, 16, 16))
|
2010-02-10 19:38:16 -07:00
|
|
|
for y := 0; y < 16; y++ {
|
|
|
|
for x := 0; x < 16; x++ {
|
|
|
|
m.Set(x, y, image.RGBAColor{0, uint8(y * alpha / 15), 0, uint8(alpha)})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func vgradAlpha(alpha int) image.Image {
|
2011-09-14 06:09:46 -06:00
|
|
|
m := image.NewAlpha(image.Rect(0, 0, 16, 16))
|
2010-02-10 19:38:16 -07:00
|
|
|
for y := 0; y < 16; y++ {
|
|
|
|
for x := 0; x < 16; x++ {
|
|
|
|
m.Set(x, y, image.AlphaColor{uint8(y * alpha / 15)})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
exp/draw: fast paths for drawing a YCbCr or an NRGBA onto an RGBA.
On my laptop, I had an 800x600 jpeg and an 800x600 png (with
transparency). I timed how long it took to draw each image onto an
equivalently sized, zeroed RGBA image.
Previously, the jpeg took 75ms and the png took 70ms, going through
the medium-fast path, i.e. func drawRGBA in draw.go.
After this CL, the jpeg took 14ms, and the png took 21ms with the
Over operator and 12ms with the Src operator.
It's only a rough estimate basd on one image file, but it should
give an idea of the order of magnitude of improvement.
R=rsc, r
CC=adg, golang-dev
https://golang.org/cl/4468044
2011-05-04 11:17:53 -06:00
|
|
|
func vgradGreenNRGBA(alpha int) image.Image {
|
2011-09-14 06:09:46 -06:00
|
|
|
m := image.NewNRGBA(image.Rect(0, 0, 16, 16))
|
exp/draw: fast paths for drawing a YCbCr or an NRGBA onto an RGBA.
On my laptop, I had an 800x600 jpeg and an 800x600 png (with
transparency). I timed how long it took to draw each image onto an
equivalently sized, zeroed RGBA image.
Previously, the jpeg took 75ms and the png took 70ms, going through
the medium-fast path, i.e. func drawRGBA in draw.go.
After this CL, the jpeg took 14ms, and the png took 21ms with the
Over operator and 12ms with the Src operator.
It's only a rough estimate basd on one image file, but it should
give an idea of the order of magnitude of improvement.
R=rsc, r
CC=adg, golang-dev
https://golang.org/cl/4468044
2011-05-04 11:17:53 -06:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2010-02-10 19:38:16 -07:00
|
|
|
func hgradRed(alpha int) Image {
|
2011-09-14 06:09:46 -06:00
|
|
|
m := image.NewRGBA(image.Rect(0, 0, 16, 16))
|
2010-02-10 19:38:16 -07:00
|
|
|
for y := 0; y < 16; y++ {
|
|
|
|
for x := 0; x < 16; x++ {
|
|
|
|
m.Set(x, y, image.RGBAColor{uint8(x * alpha / 15), 0, 0, uint8(alpha)})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2010-09-10 04:09:56 -06:00
|
|
|
func gradYellow(alpha int) Image {
|
2011-09-14 06:09:46 -06:00
|
|
|
m := image.NewRGBA(image.Rect(0, 0, 16, 16))
|
2010-09-10 04:09:56 -06:00
|
|
|
for y := 0; y < 16; y++ {
|
|
|
|
for x := 0; x < 16; x++ {
|
|
|
|
m.Set(x, y, image.RGBAColor{uint8(x * alpha / 15), uint8(y * alpha / 15), 0, uint8(alpha)})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2010-02-10 19:38:16 -07:00
|
|
|
type drawTest struct {
|
|
|
|
desc string
|
|
|
|
src image.Image
|
|
|
|
mask image.Image
|
2010-02-16 20:34:51 -07:00
|
|
|
op Op
|
2010-02-10 19:38:16 -07:00
|
|
|
expected image.Color
|
|
|
|
}
|
|
|
|
|
|
|
|
var drawTests = []drawTest{
|
2010-02-16 20:34:51 -07:00
|
|
|
// Uniform mask (0% opaque).
|
2010-10-22 11:06:33 -06:00
|
|
|
{"nop", vgradGreen(255), fillAlpha(0), Over, image.RGBAColor{136, 0, 0, 255}},
|
|
|
|
{"clear", vgradGreen(255), fillAlpha(0), Src, image.RGBAColor{0, 0, 0, 0}},
|
2010-02-10 19:38:16 -07:00
|
|
|
// Uniform mask (100%, 75%, nil) and uniform source.
|
2010-02-16 20:34:51 -07:00
|
|
|
// At (x, y) == (8, 8):
|
|
|
|
// The destination pixel is {136, 0, 0, 255}.
|
|
|
|
// The source pixel is {0, 0, 90, 90}.
|
2010-10-22 11:06:33 -06:00
|
|
|
{"fill", fillBlue(90), fillAlpha(255), Over, image.RGBAColor{88, 0, 90, 255}},
|
|
|
|
{"fillSrc", fillBlue(90), fillAlpha(255), Src, image.RGBAColor{0, 0, 90, 90}},
|
|
|
|
{"fillAlpha", fillBlue(90), fillAlpha(192), Over, image.RGBAColor{100, 0, 68, 255}},
|
|
|
|
{"fillAlphaSrc", fillBlue(90), fillAlpha(192), Src, image.RGBAColor{0, 0, 68, 68}},
|
|
|
|
{"fillNil", fillBlue(90), nil, Over, image.RGBAColor{88, 0, 90, 255}},
|
|
|
|
{"fillNilSrc", fillBlue(90), nil, Src, image.RGBAColor{0, 0, 90, 90}},
|
2010-02-16 20:34:51 -07:00
|
|
|
// Uniform mask (100%, 75%, nil) and variable source.
|
|
|
|
// At (x, y) == (8, 8):
|
|
|
|
// The destination pixel is {136, 0, 0, 255}.
|
|
|
|
// The source pixel is {0, 48, 0, 90}.
|
2010-10-22 11:06:33 -06:00
|
|
|
{"copy", vgradGreen(90), fillAlpha(255), Over, image.RGBAColor{88, 48, 0, 255}},
|
|
|
|
{"copySrc", vgradGreen(90), fillAlpha(255), Src, image.RGBAColor{0, 48, 0, 90}},
|
|
|
|
{"copyAlpha", vgradGreen(90), fillAlpha(192), Over, image.RGBAColor{100, 36, 0, 255}},
|
|
|
|
{"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}},
|
exp/draw: fast paths for drawing a YCbCr or an NRGBA onto an RGBA.
On my laptop, I had an 800x600 jpeg and an 800x600 png (with
transparency). I timed how long it took to draw each image onto an
equivalently sized, zeroed RGBA image.
Previously, the jpeg took 75ms and the png took 70ms, going through
the medium-fast path, i.e. func drawRGBA in draw.go.
After this CL, the jpeg took 14ms, and the png took 21ms with the
Over operator and 12ms with the Src operator.
It's only a rough estimate basd on one image file, but it should
give an idea of the order of magnitude of improvement.
R=rsc, r
CC=adg, golang-dev
https://golang.org/cl/4468044
2011-05-04 11:17:53 -06:00
|
|
|
// 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}},
|
2010-02-16 20:34:51 -07:00
|
|
|
// Variable mask and variable source.
|
|
|
|
// At (x, y) == (8, 8):
|
2010-02-10 19:38:16 -07:00
|
|
|
// The destination pixel is {136, 0, 0, 255}.
|
|
|
|
// The source pixel is {0, 0, 255, 255}.
|
|
|
|
// The mask pixel's alpha is 102, or 40%.
|
2010-10-22 11:06:33 -06:00
|
|
|
{"generic", fillBlue(255), vgradAlpha(192), Over, image.RGBAColor{81, 0, 102, 255}},
|
|
|
|
{"genericSrc", fillBlue(255), vgradAlpha(192), Src, image.RGBAColor{0, 0, 102, 102}},
|
2010-02-10 19:38:16 -07:00
|
|
|
}
|
|
|
|
|
2011-07-07 17:54:21 -06:00
|
|
|
func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) image.Image {
|
2010-05-20 14:57:18 -06:00
|
|
|
// Since golden is a newly allocated image, we don't have to check if the
|
|
|
|
// input source and mask images and the output golden image overlap.
|
2010-08-09 20:08:52 -06:00
|
|
|
b := dst.Bounds()
|
2011-07-07 17:54:21 -06:00
|
|
|
sb := src.Bounds()
|
|
|
|
mb := image.Rect(-1e9, -1e9, 1e9, 1e9)
|
2010-09-10 04:09:56 -06:00
|
|
|
if mask != nil {
|
2011-07-07 17:54:21 -06:00
|
|
|
mb = mask.Bounds()
|
2010-09-10 04:09:56 -06:00
|
|
|
}
|
2011-09-14 06:09:46 -06:00
|
|
|
golden := image.NewRGBA(image.Rect(0, 0, b.Max.X, b.Max.Y))
|
2011-07-07 17:54:21 -06:00
|
|
|
for y := r.Min.Y; y < r.Max.Y; y++ {
|
|
|
|
sy := y + sp.Y - r.Min.Y
|
|
|
|
my := y + mp.Y - r.Min.Y
|
|
|
|
for x := r.Min.X; x < r.Max.X; x++ {
|
|
|
|
if !(image.Point{x, y}.In(b)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
sx := x + sp.X - r.Min.X
|
|
|
|
if !(image.Point{sx, sy}.In(sb)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
mx := x + mp.X - r.Min.X
|
|
|
|
if !(image.Point{mx, my}.In(mb)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2010-05-20 14:57:18 -06:00
|
|
|
const M = 1<<16 - 1
|
|
|
|
var dr, dg, db, da uint32
|
2010-09-10 04:09:56 -06:00
|
|
|
if op == Over {
|
2010-05-20 14:57:18 -06:00
|
|
|
dr, dg, db, da = dst.At(x, y).RGBA()
|
|
|
|
}
|
2010-09-10 04:09:56 -06:00
|
|
|
sr, sg, sb, sa := src.At(sx, sy).RGBA()
|
2010-05-20 14:57:18 -06:00
|
|
|
ma := uint32(M)
|
2010-09-10 04:09:56 -06:00
|
|
|
if mask != nil {
|
|
|
|
_, _, _, ma = mask.At(mx, my).RGBA()
|
2010-05-20 14:57:18 -06:00
|
|
|
}
|
|
|
|
a := M - (sa * ma / M)
|
|
|
|
golden.Set(x, y, image.RGBA64Color{
|
|
|
|
uint16((dr*a + sr*ma) / M),
|
|
|
|
uint16((dg*a + sg*ma) / M),
|
|
|
|
uint16((db*a + sb*ma) / M),
|
|
|
|
uint16((da*a + sa*ma) / M),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 17:54:21 -06:00
|
|
|
return golden.SubImage(b)
|
2010-05-20 14:57:18 -06:00
|
|
|
}
|
|
|
|
|
2010-02-10 19:38:16 -07:00
|
|
|
func TestDraw(t *testing.T) {
|
2011-07-07 17:54:21 -06:00
|
|
|
rr := []image.Rectangle{
|
|
|
|
image.Rect(0, 0, 0, 0),
|
|
|
|
image.Rect(0, 0, 16, 16),
|
|
|
|
image.Rect(3, 5, 12, 10),
|
|
|
|
image.Rect(0, 0, 9, 9),
|
|
|
|
image.Rect(8, 8, 16, 16),
|
|
|
|
image.Rect(8, 0, 9, 16),
|
|
|
|
image.Rect(0, 8, 16, 9),
|
|
|
|
image.Rect(8, 8, 9, 9),
|
|
|
|
image.Rect(8, 8, 8, 8),
|
|
|
|
}
|
|
|
|
for _, r := range rr {
|
|
|
|
loop:
|
|
|
|
for _, test := range drawTests {
|
|
|
|
dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image)
|
|
|
|
// Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
|
|
|
|
golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
|
|
|
|
b := dst.Bounds()
|
|
|
|
if !b.Eq(golden.Bounds()) {
|
|
|
|
t.Errorf("draw %v %s: bounds %v versus %v", r, test.desc, dst.Bounds(), golden.Bounds())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Draw the same combination onto the actual dst using the optimized DrawMask implementation.
|
|
|
|
DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op)
|
|
|
|
if image.Pt(8, 8).In(r) {
|
|
|
|
// Check that the resultant pixel at (8, 8) matches what we expect
|
|
|
|
// (the expected value can be verified by hand).
|
|
|
|
if !eq(dst.At(8, 8), test.expected) {
|
|
|
|
t.Errorf("draw %v %s: at (8, 8) %v versus %v", r, test.desc, dst.At(8, 8), test.expected)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check that the resultant dst image matches the golden output.
|
|
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
|
|
|
if !eq(dst.At(x, y), golden.At(x, y)) {
|
|
|
|
t.Errorf("draw %v %s: at (%d, %d), %v versus golden %v", r, test.desc, x, y, dst.At(x, y), golden.At(x, y))
|
|
|
|
continue loop
|
|
|
|
}
|
2010-05-20 14:57:18 -06:00
|
|
|
}
|
|
|
|
}
|
2010-02-10 19:38:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-06-04 18:30:39 -06:00
|
|
|
|
2010-09-10 04:09:56 -06:00
|
|
|
func TestDrawOverlap(t *testing.T) {
|
|
|
|
for _, op := range []Op{Over, Src} {
|
|
|
|
for yoff := -2; yoff <= 2; yoff++ {
|
|
|
|
loop:
|
|
|
|
for xoff := -2; xoff <= 2; xoff++ {
|
|
|
|
m := gradYellow(127).(*image.RGBA)
|
2011-07-07 17:54:21 -06:00
|
|
|
dst := m.SubImage(image.Rect(5, 5, 10, 10)).(*image.RGBA)
|
|
|
|
src := m.SubImage(image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff)).(*image.RGBA)
|
2010-09-10 04:09:56 -06:00
|
|
|
b := dst.Bounds()
|
2011-07-07 17:54:21 -06:00
|
|
|
// Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation.
|
|
|
|
golden := makeGolden(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
|
2010-09-10 04:09:56 -06:00
|
|
|
if !b.Eq(golden.Bounds()) {
|
|
|
|
t.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff, yoff, dst.Bounds(), golden.Bounds())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Draw the same combination onto the actual dst using the optimized DrawMask implementation.
|
|
|
|
DrawMask(dst, b, src, src.Bounds().Min, nil, image.ZP, op)
|
|
|
|
// Check that the resultant dst image matches the golden output.
|
|
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
|
|
|
if !eq(dst.At(x, y), golden.At(x, y)) {
|
|
|
|
t.Errorf("drawOverlap xoff=%d,yoff=%d: at (%d, %d), %v versus golden %v", xoff, yoff, x, y, dst.At(x, y), golden.At(x, y))
|
|
|
|
continue loop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-02 19:43:54 -06:00
|
|
|
// TestNonZeroSrcPt checks drawing with a non-zero src point parameter.
|
|
|
|
func TestNonZeroSrcPt(t *testing.T) {
|
2011-09-14 06:09:46 -06:00
|
|
|
a := image.NewRGBA(image.Rect(0, 0, 1, 1))
|
|
|
|
b := image.NewRGBA(image.Rect(0, 0, 2, 2))
|
2010-06-04 18:30:39 -06:00
|
|
|
b.Set(0, 0, image.RGBAColor{0, 0, 0, 5})
|
|
|
|
b.Set(1, 0, image.RGBAColor{0, 0, 5, 5})
|
|
|
|
b.Set(0, 1, image.RGBAColor{0, 5, 0, 5})
|
|
|
|
b.Set(1, 1, image.RGBAColor{5, 0, 0, 5})
|
2011-06-15 19:45:16 -06:00
|
|
|
Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1), Over)
|
2010-06-04 18:30:39 -06:00
|
|
|
if !eq(image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) {
|
2011-06-02 19:43:54 -06:00
|
|
|
t.Errorf("non-zero src pt: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0))
|
2010-06-04 18:30:39 -06:00
|
|
|
}
|
|
|
|
}
|
2011-07-04 22:42:31 -06:00
|
|
|
|
|
|
|
func TestFill(t *testing.T) {
|
|
|
|
rr := []image.Rectangle{
|
|
|
|
image.Rect(0, 0, 0, 0),
|
|
|
|
image.Rect(0, 0, 40, 30),
|
|
|
|
image.Rect(10, 0, 40, 30),
|
|
|
|
image.Rect(0, 20, 40, 30),
|
|
|
|
image.Rect(10, 20, 40, 30),
|
|
|
|
image.Rect(10, 20, 15, 25),
|
|
|
|
image.Rect(10, 0, 35, 30),
|
|
|
|
image.Rect(0, 15, 40, 16),
|
|
|
|
image.Rect(24, 24, 25, 25),
|
|
|
|
image.Rect(23, 23, 26, 26),
|
|
|
|
image.Rect(22, 22, 27, 27),
|
|
|
|
image.Rect(21, 21, 28, 28),
|
|
|
|
image.Rect(20, 20, 29, 29),
|
|
|
|
}
|
|
|
|
for _, r := range rr {
|
2011-09-14 06:09:46 -06:00
|
|
|
m := image.NewRGBA(image.Rect(0, 0, 40, 30)).SubImage(r).(*image.RGBA)
|
2011-07-04 22:42:31 -06:00
|
|
|
b := m.Bounds()
|
|
|
|
c := image.RGBAColor{11, 0, 0, 255}
|
|
|
|
src := &image.ColorImage{c}
|
|
|
|
check := func(desc string) {
|
|
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
|
|
|
if !eq(c, m.At(x, y)) {
|
|
|
|
t.Errorf("%s fill: at (%d, %d), sub-image bounds=%v: want %v got %v", desc, x, y, r, c, m.At(x, y))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Draw 1 pixel at a time.
|
|
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
|
|
|
DrawMask(m, image.Rect(x, y, x+1, y+1), src, image.ZP, nil, image.ZP, Src)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
check("pixel")
|
|
|
|
// Draw 1 row at a time.
|
|
|
|
c = image.RGBAColor{0, 22, 0, 255}
|
|
|
|
src = &image.ColorImage{c}
|
|
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
|
|
DrawMask(m, image.Rect(b.Min.X, y, b.Max.X, y+1), src, image.ZP, nil, image.ZP, Src)
|
|
|
|
}
|
|
|
|
check("row")
|
|
|
|
// Draw 1 column at a time.
|
|
|
|
c = image.RGBAColor{0, 0, 33, 255}
|
|
|
|
src = &image.ColorImage{c}
|
|
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
|
|
|
DrawMask(m, image.Rect(x, b.Min.Y, x+1, b.Max.Y), src, image.ZP, nil, image.ZP, Src)
|
|
|
|
}
|
|
|
|
check("column")
|
|
|
|
// Draw the whole image at once.
|
|
|
|
c = image.RGBAColor{44, 55, 66, 77}
|
|
|
|
src = &image.ColorImage{c}
|
|
|
|
DrawMask(m, b, src, image.ZP, nil, image.ZP, Src)
|
|
|
|
check("whole")
|
|
|
|
}
|
|
|
|
}
|