mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-24 18:02:02 +01:00
internal/restorable: add a new parameter specifying a mask for ReplacePixels
This commit is contained in:
parent
e55dbbf3cd
commit
5c79b86412
@ -563,7 +563,7 @@ func (i *Image) replacePixels(pix []byte) {
|
||||
|
||||
px, py, pw, ph := i.regionWithPadding()
|
||||
if pix == nil {
|
||||
i.backend.restorable.ReplacePixels(nil, px, py, pw, ph)
|
||||
i.backend.restorable.ReplacePixels(nil, nil, px, py, pw, ph)
|
||||
return
|
||||
}
|
||||
|
||||
@ -598,7 +598,8 @@ func (i *Image) replacePixels(pix []byte) {
|
||||
copy(pixb[4*((j+paddingSize)*pw+paddingSize):], pix[4*j*ow:4*(j+1)*ow])
|
||||
}
|
||||
|
||||
i.backend.restorable.ReplacePixels(pixb, px, py, pw, ph)
|
||||
// TODO: Specify a mask if needed.
|
||||
i.backend.restorable.ReplacePixels(pixb, nil, px, py, pw, ph)
|
||||
}
|
||||
|
||||
func (img *Image) Pixels(graphicsDriver graphicsdriver.Graphics) ([]byte, error) {
|
||||
|
@ -38,11 +38,11 @@ func (p *Pixels) Apply(img *graphicscommand.Image) {
|
||||
p.pixelsRecords.apply(img)
|
||||
}
|
||||
|
||||
func (p *Pixels) AddOrReplace(pix []byte, x, y, width, height int) {
|
||||
func (p *Pixels) AddOrReplace(pix []byte, mask []byte, x, y, width, height int) {
|
||||
if p.pixelsRecords == nil {
|
||||
p.pixelsRecords = &pixelsRecords{}
|
||||
}
|
||||
p.pixelsRecords.addOrReplace(pix, x, y, width, height)
|
||||
p.pixelsRecords.addOrReplace(pix, mask, x, y, width, height)
|
||||
}
|
||||
|
||||
func (p *Pixels) Clear(x, y, width, height int) {
|
||||
@ -130,7 +130,7 @@ func ensureEmptyImage() *Image {
|
||||
|
||||
// As emptyImage is the source at clearImage, initialize this with ReplacePixels, not clearImage.
|
||||
// This operation is also important when restoring emptyImage.
|
||||
emptyImage.ReplacePixels(pix, 0, 0, w, h)
|
||||
emptyImage.ReplacePixels(pix, nil, 0, 0, w, h)
|
||||
theImages.add(emptyImage)
|
||||
return emptyImage
|
||||
}
|
||||
@ -284,13 +284,13 @@ func (i *Image) makeStale() {
|
||||
|
||||
// ClearPixels clears the specified region by ReplacePixels.
|
||||
func (i *Image) ClearPixels(x, y, width, height int) {
|
||||
i.ReplacePixels(nil, x, y, width, height)
|
||||
i.ReplacePixels(nil, nil, x, y, width, height)
|
||||
}
|
||||
|
||||
// ReplacePixels replaces the image pixels with the given pixels slice.
|
||||
//
|
||||
// The specified region must not be overlapped with other regions by ReplacePixels.
|
||||
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
||||
func (i *Image) ReplacePixels(pixels []byte, mask []byte, x, y, width, height int) {
|
||||
if width <= 0 || height <= 0 {
|
||||
panic("restorable: width/height must be positive")
|
||||
}
|
||||
@ -323,7 +323,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
||||
// This function is responsible to copy this.
|
||||
copiedPixels := make([]byte, len(pixels))
|
||||
copy(copiedPixels, pixels)
|
||||
i.basePixels.AddOrReplace(copiedPixels, 0, 0, w, h)
|
||||
i.basePixels.AddOrReplace(copiedPixels, mask, 0, 0, w, h)
|
||||
} else {
|
||||
i.basePixels.Clear(0, 0, w, h)
|
||||
}
|
||||
@ -347,7 +347,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
||||
// This function is responsible to copy this.
|
||||
copiedPixels := make([]byte, len(pixels))
|
||||
copy(copiedPixels, pixels)
|
||||
i.basePixels.AddOrReplace(copiedPixels, x, y, width, height)
|
||||
i.basePixels.AddOrReplace(copiedPixels, mask, x, y, width, height)
|
||||
} else {
|
||||
i.basePixels.Clear(x, y, width, height)
|
||||
}
|
||||
@ -502,7 +502,7 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error
|
||||
return err
|
||||
}
|
||||
i.basePixels = Pixels{}
|
||||
i.basePixels.AddOrReplace(pix, 0, 0, i.width, i.height)
|
||||
i.basePixels.AddOrReplace(pix, nil, 0, 0, i.width, i.height)
|
||||
i.clearDrawTrianglesHistory()
|
||||
i.stale = false
|
||||
return nil
|
||||
@ -630,7 +630,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
|
||||
if err := gimg.ReadPixels(graphicsDriver, pix); err != nil {
|
||||
return err
|
||||
}
|
||||
i.basePixels.AddOrReplace(pix, 0, 0, w, h)
|
||||
i.basePixels.AddOrReplace(pix, nil, 0, 0, w, h)
|
||||
}
|
||||
|
||||
i.image = gimg
|
||||
|
@ -61,7 +61,7 @@ func TestRestore(t *testing.T) {
|
||||
defer img0.Dispose()
|
||||
|
||||
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
|
||||
img0.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, 1, 1)
|
||||
img0.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, nil, 0, 0, 1, 1)
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -129,7 +129,7 @@ func TestRestoreChain(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
clr := color.RGBA{0x00, 0x00, 0x00, 0xff}
|
||||
imgs[0].ReplacePixels([]byte{clr.R, clr.G, clr.B, clr.A}, 0, 0, 1, 1)
|
||||
imgs[0].ReplacePixels([]byte{clr.R, clr.G, clr.B, clr.A}, nil, 0, 0, 1, 1)
|
||||
for i := 0; i < num-1; i++ {
|
||||
vs := quadVertices(1, 1, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
@ -174,11 +174,11 @@ func TestRestoreChain2(t *testing.T) {
|
||||
}()
|
||||
|
||||
clr0 := color.RGBA{0xff, 0x00, 0x00, 0xff}
|
||||
imgs[0].ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
|
||||
imgs[0].ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, nil, 0, 0, w, h)
|
||||
clr7 := color.RGBA{0x00, 0xff, 0x00, 0xff}
|
||||
imgs[7].ReplacePixels([]byte{clr7.R, clr7.G, clr7.B, clr7.A}, 0, 0, w, h)
|
||||
imgs[7].ReplacePixels([]byte{clr7.R, clr7.G, clr7.B, clr7.A}, nil, 0, 0, w, h)
|
||||
clr8 := color.RGBA{0x00, 0x00, 0xff, 0xff}
|
||||
imgs[8].ReplacePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, 0, 0, w, h)
|
||||
imgs[8].ReplacePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, nil, 0, 0, w, h)
|
||||
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
@ -228,7 +228,7 @@ func TestRestoreOverrideSource(t *testing.T) {
|
||||
}()
|
||||
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
|
||||
clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff}
|
||||
img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
|
||||
img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, nil, 0, 0, w, h)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
X: 0,
|
||||
@ -238,7 +238,7 @@ func TestRestoreOverrideSource(t *testing.T) {
|
||||
}
|
||||
img2.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
|
||||
img3.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
|
||||
img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
|
||||
img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, nil, 0, 0, w, h)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
|
||||
t.Fatal(err)
|
||||
@ -411,7 +411,7 @@ func TestRestoreComplexGraph(t *testing.T) {
|
||||
func newImageFromImage(rgba *image.RGBA) *restorable.Image {
|
||||
s := rgba.Bounds().Size()
|
||||
img := restorable.NewImage(s.X, s.Y)
|
||||
img.ReplacePixels(rgba.Pix, 0, 0, s.X, s.Y)
|
||||
img.ReplacePixels(rgba.Pix, nil, 0, 0, s.X, s.Y)
|
||||
return img
|
||||
}
|
||||
|
||||
@ -485,7 +485,7 @@ func TestReplacePixels(t *testing.T) {
|
||||
for i := range pix {
|
||||
pix[i] = 0xff
|
||||
}
|
||||
img.ReplacePixels(pix, 5, 7, 4, 4)
|
||||
img.ReplacePixels(pix, nil, 5, 7, 4, 4)
|
||||
// Check the region (5, 7)-(9, 11). Outside state is indeterministic.
|
||||
for j := 7; j < 11; j++ {
|
||||
for i := 5; i < 9; i++ {
|
||||
@ -541,7 +541,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
|
||||
Height: 1,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
|
||||
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
|
||||
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, nil, 0, 0, 2, 1)
|
||||
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
|
||||
t.Fatal(err)
|
||||
@ -612,7 +612,7 @@ func TestReplacePixelsPart(t *testing.T) {
|
||||
|
||||
img := restorable.NewImage(4, 4)
|
||||
// This doesn't make the image stale. Its base pixels are available.
|
||||
img.ReplacePixels(pix, 1, 1, 2, 2)
|
||||
img.ReplacePixels(pix, nil, 1, 1, 2, 2)
|
||||
|
||||
cases := []struct {
|
||||
i int
|
||||
@ -687,7 +687,7 @@ func TestReplacePixelsOnly(t *testing.T) {
|
||||
defer img1.Dispose()
|
||||
|
||||
for i := 0; i < w*h; i += 5 {
|
||||
img0.ReplacePixels([]byte{1, 2, 3, 4}, i%w, i/w, 1, 1)
|
||||
img0.ReplacePixels([]byte{1, 2, 3, 4}, nil, i%w, i/w, 1, 1)
|
||||
}
|
||||
|
||||
vs := quadVertices(1, 1, 0, 0)
|
||||
@ -699,7 +699,7 @@ func TestReplacePixelsOnly(t *testing.T) {
|
||||
Height: 1,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
|
||||
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
|
||||
img0.ReplacePixels([]byte{5, 6, 7, 8}, nil, 0, 0, 1, 1)
|
||||
|
||||
// BasePixelsForTesting is available without GPU accessing.
|
||||
for j := 0; j < h; j++ {
|
||||
@ -742,14 +742,14 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
|
||||
src := restorable.NewImage(w, h)
|
||||
|
||||
// First, make sure that dst has pixels
|
||||
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
|
||||
dst.ReplacePixels(make([]byte, 4*w*h), nil, 0, 0, w, h)
|
||||
|
||||
// Second, draw src to dst. If the implementation is correct, dst becomes stale.
|
||||
pix := make([]byte, 4*w*h)
|
||||
for i := range pix {
|
||||
pix[i] = 0xff
|
||||
}
|
||||
src.ReplacePixels(pix, 0, 0, w, h)
|
||||
src.ReplacePixels(pix, nil, 0, 0, w, h)
|
||||
vs := quadVertices(1, 1, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
dr := graphicsdriver.Region{
|
||||
@ -786,7 +786,7 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*restorable.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*w*h), 0, 0, w, h)
|
||||
dst.ReplacePixels(make([]byte, 4*w*h), nil, 0, 0, w, h)
|
||||
// ReplacePixels for a whole image doesn't panic.
|
||||
}
|
||||
|
||||
@ -810,7 +810,7 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*restorable.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)
|
||||
}
|
||||
|
||||
func TestExtend(t *testing.T) {
|
||||
@ -832,7 +832,7 @@ func TestExtend(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
orig.ReplacePixels(pix, 0, 0, w, h)
|
||||
orig.ReplacePixels(pix, nil, 0, 0, w, h)
|
||||
extended := orig.Extend(w*2, h*2) // After this, orig is already disposed.
|
||||
|
||||
for j := 0; j < h*2; j++ {
|
||||
@ -855,13 +855,13 @@ func TestExtend(t *testing.T) {
|
||||
func TestClearPixels(t *testing.T) {
|
||||
const w, h = 16, 16
|
||||
img := restorable.NewImage(w, h)
|
||||
img.ReplacePixels(make([]byte, 4*4*4), 0, 0, 4, 4)
|
||||
img.ReplacePixels(make([]byte, 4*4*4), 4, 0, 4, 4)
|
||||
img.ReplacePixels(make([]byte, 4*4*4), nil, 0, 0, 4, 4)
|
||||
img.ReplacePixels(make([]byte, 4*4*4), nil, 4, 0, 4, 4)
|
||||
img.ClearPixels(0, 0, 4, 4)
|
||||
img.ClearPixels(4, 0, 4, 4)
|
||||
|
||||
// After clearing, the regions will be available again.
|
||||
img.ReplacePixels(make([]byte, 4*8*4), 0, 0, 8, 4)
|
||||
img.ReplacePixels(make([]byte, 4*8*4), nil, 0, 0, 8, 4)
|
||||
}
|
||||
|
||||
func TestMutateSlices(t *testing.T) {
|
||||
@ -875,7 +875,7 @@ func TestMutateSlices(t *testing.T) {
|
||||
pix[4*i+2] = byte(i)
|
||||
pix[4*i+3] = 0xff
|
||||
}
|
||||
src.ReplacePixels(pix, 0, 0, w, h)
|
||||
src.ReplacePixels(pix, nil, 0, 0, w, h)
|
||||
|
||||
vs := quadVertices(w, h, 0, 0)
|
||||
is := make([]uint16, len(graphics.QuadIndices()))
|
||||
@ -932,7 +932,7 @@ func TestOverlappedPixels(t *testing.T) {
|
||||
pix0[idx+3] = 0xff
|
||||
}
|
||||
}
|
||||
dst.ReplacePixels(pix0, 0, 0, 2, 2)
|
||||
dst.ReplacePixels(pix0, nil, 0, 0, 2, 2)
|
||||
|
||||
pix1 := make([]byte, 4*2*2)
|
||||
for j := 0; j < 2; j++ {
|
||||
@ -944,7 +944,7 @@ func TestOverlappedPixels(t *testing.T) {
|
||||
pix1[idx+3] = 0xff
|
||||
}
|
||||
}
|
||||
dst.ReplacePixels(pix1, 1, 1, 2, 2)
|
||||
dst.ReplacePixels(pix1, nil, 1, 1, 2, 2)
|
||||
|
||||
wantColors := []color.RGBA{
|
||||
{0xff, 0, 0, 0xff},
|
||||
@ -973,7 +973,7 @@ func TestOverlappedPixels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
dst.ReplacePixels(nil, 1, 0, 2, 2)
|
||||
dst.ReplacePixels(nil, nil, 1, 0, 2, 2)
|
||||
|
||||
wantColors = []color.RGBA{
|
||||
{0xff, 0, 0, 0xff},
|
||||
@ -1012,7 +1012,7 @@ func TestOverlappedPixels(t *testing.T) {
|
||||
pix2[idx+3] = 0xff
|
||||
}
|
||||
}
|
||||
dst.ReplacePixels(pix2, 1, 1, 2, 2)
|
||||
dst.ReplacePixels(pix2, nil, 1, 1, 2, 2)
|
||||
|
||||
wantColors = []color.RGBA{
|
||||
{0xff, 0, 0, 0xff},
|
||||
@ -1062,3 +1062,151 @@ func TestOverlappedPixels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplacePixelsWithMask(t *testing.T) {
|
||||
dst := restorable.NewImage(3, 3)
|
||||
|
||||
pix0 := make([]byte, 4*2*2)
|
||||
for j := 0; j < 2; j++ {
|
||||
for i := 0; i < 2; i++ {
|
||||
idx := 4 * (j*2 + i)
|
||||
pix0[idx] = 0xff
|
||||
pix0[idx+1] = 0
|
||||
pix0[idx+2] = 0
|
||||
pix0[idx+3] = 0xff
|
||||
}
|
||||
}
|
||||
dst.ReplacePixels(pix0, nil, 0, 0, 2, 2)
|
||||
|
||||
pix1 := make([]byte, 4*2*2)
|
||||
for j := 0; j < 2; j++ {
|
||||
for i := 0; i < 2; i++ {
|
||||
idx := 4 * (j*2 + i)
|
||||
pix1[idx] = 0
|
||||
pix1[idx+1] = 0xff
|
||||
pix1[idx+2] = 0
|
||||
pix1[idx+3] = 0xff
|
||||
}
|
||||
}
|
||||
|
||||
mask1 := []byte{0b00001110}
|
||||
dst.ReplacePixels(pix1, mask1, 1, 1, 2, 2)
|
||||
|
||||
wantColors := []color.RGBA{
|
||||
{0xff, 0, 0, 0xff},
|
||||
{0xff, 0, 0, 0xff},
|
||||
{0, 0, 0, 0},
|
||||
|
||||
{0xff, 0, 0, 0xff},
|
||||
{0xff, 0, 0, 0xff},
|
||||
{0, 0xff, 0, 0xff},
|
||||
|
||||
{0, 0, 0, 0},
|
||||
{0, 0xff, 0, 0xff},
|
||||
{0, 0xff, 0, 0xff},
|
||||
}
|
||||
for j := 0; j < 3; j++ {
|
||||
for i := 0; i < 3; i++ {
|
||||
r, g, b, a, err := dst.At(ui.GraphicsDriverForTesting(), i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := color.RGBA{r, g, b, a}
|
||||
want := wantColors[3*j+i]
|
||||
if got != want {
|
||||
t.Errorf("color at (%d, %d): got %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst.ReplacePixels(nil, nil, 1, 0, 2, 2)
|
||||
|
||||
wantColors = []color.RGBA{
|
||||
{0xff, 0, 0, 0xff},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
|
||||
{0xff, 0, 0, 0xff},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
|
||||
{0, 0, 0, 0},
|
||||
{0, 0xff, 0, 0xff},
|
||||
{0, 0xff, 0, 0xff},
|
||||
}
|
||||
for j := 0; j < 3; j++ {
|
||||
for i := 0; i < 3; i++ {
|
||||
r, g, b, a, err := dst.At(ui.GraphicsDriverForTesting(), i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := color.RGBA{r, g, b, a}
|
||||
want := wantColors[3*j+i]
|
||||
if got != want {
|
||||
t.Errorf("color at (%d, %d): got %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the same region with pix1/mask1.
|
||||
pix2 := make([]byte, 4*2*2)
|
||||
for j := 0; j < 2; j++ {
|
||||
for i := 0; i < 2; i++ {
|
||||
idx := 4 * (j*2 + i)
|
||||
pix2[idx] = 0
|
||||
pix2[idx+1] = 0
|
||||
pix2[idx+2] = 0xff
|
||||
pix2[idx+3] = 0xff
|
||||
}
|
||||
}
|
||||
mask2 := []byte{0b00000111}
|
||||
dst.ReplacePixels(pix2, mask2, 1, 1, 2, 2)
|
||||
|
||||
wantColors = []color.RGBA{
|
||||
{0xff, 0, 0, 0xff},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
|
||||
{0xff, 0, 0, 0xff},
|
||||
{0, 0, 0xff, 0xff},
|
||||
{0, 0, 0xff, 0xff},
|
||||
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0xff, 0xff},
|
||||
{0, 0xff, 0, 0xff},
|
||||
}
|
||||
for j := 0; j < 3; j++ {
|
||||
for i := 0; i < 3; i++ {
|
||||
r, g, b, a, err := dst.At(ui.GraphicsDriverForTesting(), i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := color.RGBA{r, g, b, a}
|
||||
want := wantColors[3*j+i]
|
||||
if got != want {
|
||||
t.Errorf("color at (%d, %d): got %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for j := 0; j < 3; j++ {
|
||||
for i := 0; i < 3; i++ {
|
||||
r, g, b, a, err := dst.At(ui.GraphicsDriverForTesting(), i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := color.RGBA{r, g, b, a}
|
||||
want := wantColors[3*j+i]
|
||||
if got != want {
|
||||
t.Errorf("color at (%d, %d): got %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
type pixelsRecord struct {
|
||||
rect image.Rectangle
|
||||
pix []byte
|
||||
mask []byte
|
||||
}
|
||||
|
||||
func (p *pixelsRecord) clearIfOverlapped(rect image.Rectangle) {
|
||||
@ -42,20 +43,48 @@ func (p *pixelsRecord) clearIfOverlapped(rect image.Rectangle) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pixelsRecord) merge(pix []byte, mask []byte) {
|
||||
if len(p.pix) != len(pix) {
|
||||
panic(fmt.Sprintf("restorable: len(p.pix) (%d) and len(pix) (%d) must match but not", len(p.pix), len(pix)))
|
||||
}
|
||||
|
||||
if mask == nil {
|
||||
p.pix = pix
|
||||
return
|
||||
}
|
||||
|
||||
if p.mask == nil {
|
||||
p.mask = mask
|
||||
}
|
||||
if len(p.mask) != len(mask) {
|
||||
panic(fmt.Sprintf("restorable: len(p.mask) (%d) and len(mask) (%d) must match but not", len(p.mask), len(mask)))
|
||||
}
|
||||
|
||||
for i := 0; i < len(p.pix)/4; i++ {
|
||||
if mask[i/8]>>(i%8)&1 == 0 {
|
||||
continue
|
||||
}
|
||||
p.mask[i/8] |= 1 << (i % 8)
|
||||
copy(p.pix[4*i:4*(i+1)], pix[4*i:4*(i+1)])
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pixelsRecord) at(x, y int) (r, g, b, a byte, ok bool) {
|
||||
if !image.Pt(x, y).In(p.rect) {
|
||||
return 0, 0, 0, 0, false
|
||||
}
|
||||
|
||||
idx := 4 * ((y-p.rect.Min.Y)*p.rect.Dx() + (x - p.rect.Min.X))
|
||||
return p.pix[idx], p.pix[idx+1], p.pix[idx+2], p.pix[idx+3], true
|
||||
idx := ((y-p.rect.Min.Y)*p.rect.Dx() + (x - p.rect.Min.X))
|
||||
if p.mask != nil && p.mask[idx/8]>>(idx%8)&1 == 0 {
|
||||
return 0, 0, 0, 0, false
|
||||
}
|
||||
return p.pix[4*idx], p.pix[4*idx+1], p.pix[4*idx+2], p.pix[4*idx+3], true
|
||||
}
|
||||
|
||||
type pixelsRecords struct {
|
||||
records []*pixelsRecord
|
||||
}
|
||||
|
||||
func (pr *pixelsRecords) addOrReplace(pixels []byte, x, y, width, height int) {
|
||||
func (pr *pixelsRecords) addOrReplace(pixels []byte, mask []byte, x, y, width, height int) {
|
||||
if len(pixels) != 4*width*height {
|
||||
msg := fmt.Sprintf("restorable: len(pixels) must be 4*%d*%d = %d but %d", width, height, 4*width*height, len(pixels))
|
||||
if pixels == nil {
|
||||
@ -64,8 +93,32 @@ func (pr *pixelsRecords) addOrReplace(pixels []byte, x, y, width, height int) {
|
||||
panic(msg)
|
||||
}
|
||||
|
||||
// Remove or update the duplicated records first.
|
||||
rect := image.Rect(x, y, x+width, y+height)
|
||||
if mask != nil {
|
||||
// If a mask is specified, try merging the records.
|
||||
var merged bool
|
||||
for idx := len(pr.records) - 1; idx >= 0; idx-- {
|
||||
if r := pr.records[idx]; r.rect.Overlaps(rect) {
|
||||
if r.rect == rect {
|
||||
r.merge(pixels, mask)
|
||||
merged = true
|
||||
}
|
||||
// If there is an overlap with other regions in the existing records,
|
||||
// give up modifying them and just add a record.
|
||||
break
|
||||
}
|
||||
}
|
||||
if !merged {
|
||||
pr.records = append(pr.records, &pixelsRecord{
|
||||
rect: rect,
|
||||
pix: pixels,
|
||||
mask: mask,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Remove or update the duplicated records first.
|
||||
var n int
|
||||
for _, r := range pr.records {
|
||||
if r.rect.In(rect) {
|
||||
@ -118,6 +171,6 @@ func (pr *pixelsRecords) at(i, j int) (r, g, b, a byte, ok 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, nil, r.rect.Min.X, r.rect.Min.Y, r.rect.Dx(), r.rect.Dy())
|
||||
img.ReplacePixels(r.pix, r.mask, r.rect.Min.X, r.rect.Min.Y, r.rect.Dx(), r.rect.Dy())
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ func TestShaderChain(t *testing.T) {
|
||||
imgs = append(imgs, img)
|
||||
}
|
||||
|
||||
imgs[0].ReplacePixels([]byte{0xff, 0, 0, 0xff}, 0, 0, 1, 1)
|
||||
imgs[0].ReplacePixels([]byte{0xff, 0, 0, 0xff}, nil, 0, 0, 1, 1)
|
||||
|
||||
ir := etesting.ShaderProgramImages(needsInvertY(), 1)
|
||||
s := restorable.NewShader(&ir)
|
||||
@ -131,9 +131,9 @@ func TestShaderMultipleSources(t *testing.T) {
|
||||
for i := range srcs {
|
||||
srcs[i] = restorable.NewImage(1, 1)
|
||||
}
|
||||
srcs[0].ReplacePixels([]byte{0x40, 0, 0, 0xff}, 0, 0, 1, 1)
|
||||
srcs[1].ReplacePixels([]byte{0, 0x80, 0, 0xff}, 0, 0, 1, 1)
|
||||
srcs[2].ReplacePixels([]byte{0, 0, 0xc0, 0xff}, 0, 0, 1, 1)
|
||||
srcs[0].ReplacePixels([]byte{0x40, 0, 0, 0xff}, nil, 0, 0, 1, 1)
|
||||
srcs[1].ReplacePixels([]byte{0, 0x80, 0, 0xff}, nil, 0, 0, 1, 1)
|
||||
srcs[2].ReplacePixels([]byte{0, 0, 0xc0, 0xff}, nil, 0, 0, 1, 1)
|
||||
|
||||
dst := restorable.NewImage(1, 1)
|
||||
|
||||
@ -171,7 +171,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
|
||||
0x40, 0, 0, 0xff,
|
||||
0, 0x80, 0, 0xff,
|
||||
0, 0, 0xc0, 0xff,
|
||||
}, 0, 0, 3, 1)
|
||||
}, nil, 0, 0, 3, 1)
|
||||
srcs := [graphics.ShaderImageNum]*restorable.Image{src, src, src}
|
||||
|
||||
dst := restorable.NewImage(1, 1)
|
||||
|
Loading…
Reference in New Issue
Block a user