internal/graphicscommand: add a new paramter 'mask' to ReplacePixels

This commit is contained in:
Hajime Hoshi 2022-03-21 01:03:00 +09:00
parent c1a0d83f8d
commit 5fe6791b5d
6 changed files with 86 additions and 6 deletions

View File

@ -584,7 +584,36 @@ func (c *replacePixelsCommand) String() string {
// Exec executes the replacePixelsCommand.
func (c *replacePixelsCommand) Exec(graphicsDriver graphicsdriver.Graphics, indexOffset int) error {
c.dst.image.ReplacePixels(c.args)
var lastArgIdx int
for i, a := range c.args {
if a.Mask == nil {
continue
}
if len(c.args[lastArgIdx:i]) > 0 {
c.dst.image.ReplacePixels(c.args[lastArgIdx:i])
lastArgIdx = i
}
orig := make([]byte, 4*c.dst.width*c.dst.height)
if err := c.dst.image.ReadPixels(orig); err != nil {
return err
}
for j := 0; j < a.Height; j++ {
for i := 0; i < a.Width; i++ {
idx := j*a.Width + i
if a.Mask[idx/8]>>(idx%8)&1 == 0 {
srcIdx := (a.Y+j)*c.dst.width + a.X + i
copy(a.Pixels[4*idx:4*(idx+1)], orig[4*srcIdx:4*(srcIdx+1)])
}
}
}
a.Mask = nil
}
if len(c.args[lastArgIdx:]) > 0 {
c.dst.image.ReplacePixels(c.args[lastArgIdx:])
}
return nil
}

View File

@ -180,9 +180,10 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte) e
return nil
}
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
func (i *Image) ReplacePixels(pixels []byte, mask []byte, x, y, width, height int) {
i.bufferedRP = append(i.bufferedRP, &graphicsdriver.ReplacePixelsArgs{
Pixels: pixels,
Mask: mask,
X: x,
Y: y,
Width: width,

View File

@ -85,11 +85,60 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
}
dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeClear, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
dst.ReplacePixels(make([]byte, 4), nil, 0, 0, 1, 1)
// TODO: Check the result.
}
func TestReplacePixelsWithMask(t *testing.T) {
const w, h = 4, 3
src := graphicscommand.NewImage(w, h)
dst := graphicscommand.NewImage(w, h)
vs := quadVertices(w, h)
is := graphics.QuadIndices()
dr := graphicsdriver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeClear, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
pix0 := make([]byte, 4*w*h)
for i := range pix0 {
pix0[i] = 0x40
}
dst.ReplacePixels(pix0, nil, 0, 0, w, h)
pix1 := make([]byte, 4*w*h)
for i := range pix1 {
pix1[i] = 0x80
}
mask1 := []byte{0b11110110, 0b00000110}
dst.ReplacePixels(pix1, mask1, 0, 0, w, h)
readPix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), readPix); err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
idx := 4 * (i + w*j)
got := color.RGBA{readPix[idx], readPix[idx+1], readPix[idx+2], readPix[idx+3]}
var want color.RGBA
if (i != 0 && i != w-1) || (j != 0 && j != h-1) {
want = color.RGBA{0x80, 0x80, 0x80, 0x80}
} else {
want = color.RGBA{0x40, 0x40, 0x40, 0x40}
}
if got != want {
t.Errorf("dst.At(%d, %d) after ReplacePixels: got %v, want: %v", i, j, got, want)
}
}
}
}
func TestShader(t *testing.T) {
const w, h = 16, 16
clr := graphicscommand.NewImage(w, h)

View File

@ -83,6 +83,7 @@ type ImageID int
type ReplacePixelsArgs struct {
Pixels []byte
Mask []byte
X int
Y int
Width int

View File

@ -304,12 +304,12 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
theImages.makeStaleIfDependingOn(i)
if pixels != nil {
i.image.ReplacePixels(pixels, x, y, width, height)
i.image.ReplacePixels(pixels, nil, x, y, width, height)
} else {
// TODO: When pixels == nil, we don't have to care the pixel state there. In such cases, the image
// accepts only ReplacePixels and not Fill or DrawTriangles.
// TODO: Separate Image struct into two: images for only-ReplacePixels, and the others.
i.image.ReplacePixels(make([]byte, 4*width*height), x, y, width, height)
i.image.ReplacePixels(make([]byte, 4*width*height), nil, x, y, width, height)
}
if !NeedsRestoring() || i.screen || i.volatile {

View File

@ -117,6 +117,6 @@ func (pr *pixelsRecords) at(i, j int) (byte, byte, byte, byte, bool) {
func (pr *pixelsRecords) apply(img *graphicscommand.Image) {
// TODO: Isn't this too heavy? Can we merge the operations?
for _, r := range pr.records {
img.ReplacePixels(r.pix, r.rect.Min.X, r.rect.Min.Y, r.rect.Dx(), r.rect.Dy())
img.ReplacePixels(r.pix, nil, r.rect.Min.X, r.rect.Min.Y, r.rect.Dx(), r.rect.Dy())
}
}