internal/restorable: refactoring: use image.Rectangle

This commit is contained in:
Hajime Hoshi 2023-04-28 00:12:42 +09:00
parent d9a2b0922d
commit cdcffd7d4f
5 changed files with 111 additions and 114 deletions

View File

@ -518,15 +518,15 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
func (i *Image) WritePixels(pix []byte, x, y, width, height int) {
backendsM.Lock()
defer backendsM.Unlock()
i.writePixels(pix, x, y, width, height)
i.writePixels(pix, image.Rect(x, y, x+width, y+height))
}
func (i *Image) writePixels(pix []byte, x, y, width, height int) {
func (i *Image) writePixels(pix []byte, region image.Rectangle) {
if i.disposed {
panic("atlas: the image must not be disposed at writePixels")
}
if l := 4 * width * height; len(pix) != l {
if l := 4 * region.Dx() * region.Dy(); len(pix) != l {
panic(fmt.Sprintf("atlas: len(p) must be %d but %d", l, len(pix)))
}
@ -542,19 +542,18 @@ func (i *Image) writePixels(pix []byte, x, y, width, height int) {
r := i.regionWithPadding()
if x != 0 || y != 0 || width != i.width || height != i.height || i.paddingSize() == 0 {
x += r.Min.X
y += r.Min.Y
if region.Min.X != 0 || region.Min.Y != 0 || region.Dx() != i.width || region.Dy() != i.height || i.paddingSize() == 0 {
region = region.Add(r.Min)
if pix == nil {
i.backend.restorable.WritePixels(nil, x, y, width, height)
i.backend.restorable.WritePixels(nil, region)
return
}
// Copy pixels in the case when pix is modified before the graphics command is executed.
pix2 := theTemporaryBytes.alloc(len(pix))
copy(pix2, pix)
i.backend.restorable.WritePixels(pix2, x, y, width, height)
i.backend.restorable.WritePixels(pix2, region)
return
}
@ -579,11 +578,11 @@ func (i *Image) writePixels(pix []byte, x, y, width, height int) {
}
// Copy the content.
for j := 0; j < height; j++ {
copy(pixb[4*j*r.Dx():], pix[4*j*width:4*(j+1)*width])
for j := 0; j < region.Dy(); j++ {
copy(pixb[4*j*r.Dx():], pix[4*j*region.Dx():4*(j+1)*region.Dx()])
}
i.backend.restorable.WritePixels(pixb, r.Min.X, r.Min.Y, r.Dx(), r.Dy())
i.backend.restorable.WritePixels(pixb, r)
}
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, x, y, width, height int) error {
@ -604,7 +603,7 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
r := i.regionWithPadding()
x += r.Min.X
y += r.Min.Y
return i.backend.restorable.ReadPixels(graphicsDriver, pixels, x, y, width, height)
return i.backend.restorable.ReadPixels(graphicsDriver, pixels, image.Rect(x, y, x+width, y+height))
}
// MarkDisposed marks the image as disposed. The actual operation is deferred.
@ -653,7 +652,7 @@ func (i *Image) dispose(markDisposed bool) {
if !i.backend.page.IsEmpty() {
// As this part can be reused, this should be cleared explicitly.
r := i.regionWithPadding()
i.backend.restorable.ClearPixels(r.Min.X, r.Min.Y, r.Dx(), r.Dy())
i.backend.restorable.ClearPixels(r)
return
}

View File

@ -38,30 +38,30 @@ 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, region image.Rectangle) {
if p.pixelsRecords == nil {
p.pixelsRecords = &pixelsRecords{}
}
p.pixelsRecords.addOrReplace(pix, x, y, width, height)
p.pixelsRecords.addOrReplace(pix, region)
}
func (p *Pixels) Clear(x, y, width, height int) {
func (p *Pixels) Clear(region image.Rectangle) {
// Note that we don't care whether the region is actually removed or not here. There is an actual case that
// the region is allocated but nothing is rendered. See TestDisposeImmediately at shareable package.
if p.pixelsRecords == nil {
return
}
p.pixelsRecords.clear(x, y, width, height)
p.pixelsRecords.clear(region)
}
func (p *Pixels) ReadPixels(pixels []byte, x, y, width, height, imageWidth, imageHeight int) {
func (p *Pixels) ReadPixels(pixels []byte, region image.Rectangle, imageWidth, imageHeight int) {
if p.pixelsRecords == nil {
for i := range pixels {
pixels[i] = 0
}
return
}
p.pixelsRecords.readPixels(pixels, x, y, width, height, imageWidth, imageHeight)
p.pixelsRecords.readPixels(pixels, region, imageWidth, imageHeight)
}
func (p *Pixels) AppendRegion(regions []image.Rectangle) []image.Rectangle {
@ -247,7 +247,7 @@ func (i *Image) makeStale(rect image.Rectangle) {
if r.Empty() {
continue
}
i.basePixels.Clear(r.Min.X, r.Min.Y, r.Dx(), r.Dy())
i.basePixels.Clear(r)
}
// Don't have to call makeStale recursively here.
@ -257,8 +257,8 @@ func (i *Image) makeStale(rect image.Rectangle) {
}
// ClearPixels clears the specified region by WritePixels.
func (i *Image) ClearPixels(x, y, width, height int) {
i.WritePixels(nil, x, y, width, height)
func (i *Image) ClearPixels(region image.Rectangle) {
i.WritePixels(nil, region)
}
func (i *Image) needsRestoring() bool {
@ -268,13 +268,13 @@ func (i *Image) needsRestoring() bool {
// WritePixels replaces the image pixels with the given pixels slice.
//
// The specified region must not be overlapped with other regions by WritePixels.
func (i *Image) WritePixels(pixels []byte, x, y, width, height int) {
if width <= 0 || height <= 0 {
func (i *Image) WritePixels(pixels []byte, region image.Rectangle) {
if region.Dx() <= 0 || region.Dy() <= 0 {
panic("restorable: width/height must be positive")
}
w, h := i.width, i.height
if x < 0 || y < 0 || w <= x || h <= y || x+width <= 0 || y+height <= 0 || w < x+width || h < y+height {
panic(fmt.Sprintf("restorable: out of range x: %d, y: %d, width: %d, height: %d", x, y, width, height))
if !region.In(image.Rect(0, 0, w, h)) {
panic(fmt.Sprintf("restorable: out of range %v", region))
}
// TODO: Avoid making other images stale if possible. (#514)
@ -282,29 +282,29 @@ func (i *Image) WritePixels(pixels []byte, x, y, width, height int) {
theImages.makeStaleIfDependingOn(i)
if pixels != nil {
i.image.WritePixels(pixels, x, y, width, height)
i.image.WritePixels(pixels, region.Min.X, region.Min.Y, region.Dx(), region.Dy())
} else {
// TODO: When pixels == nil, we don't have to care the pixel state there. In such cases, the image
// accepts only WritePixels and not Fill or DrawTriangles.
// TODO: Separate Image struct into two: images for WritePixels-only, and the others.
i.image.WritePixels(make([]byte, 4*width*height), x, y, width, height)
i.image.WritePixels(make([]byte, 4*region.Dx()*region.Dy()), region.Min.X, region.Min.Y, region.Dx(), region.Dy())
}
// Even if the image is already stale, call makeStale to extend the stale region.
if !needsRestoring() || !i.needsRestoring() || i.stale {
i.makeStale(image.Rect(x, y, x+width, y+height))
i.makeStale(region)
return
}
if x == 0 && y == 0 && width == w && height == h {
if region.Eq(image.Rect(0, 0, w, h)) {
if pixels != nil {
// pixels can point to a shared region.
// 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, image.Rect(0, 0, w, h))
} else {
i.basePixels.Clear(0, 0, w, h)
i.basePixels.Clear(image.Rect(0, 0, w, h))
}
i.clearDrawTrianglesHistory()
i.stale = false
@ -314,7 +314,7 @@ func (i *Image) WritePixels(pixels []byte, x, y, width, height int) {
// Records for DrawTriangles cannot come before records for WritePixels.
if len(i.drawTrianglesHistory) > 0 {
i.makeStale(image.Rect(x, y, x+width, y+height))
i.makeStale(region)
return
}
@ -323,9 +323,9 @@ func (i *Image) WritePixels(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, region)
} else {
i.basePixels.Clear(x, y, width, height)
i.basePixels.Clear(region)
}
}
@ -427,9 +427,9 @@ func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics
return nil
}
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, x, y, width, height int) error {
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error {
if AlwaysReadPixelsFromGPU() {
if err := i.image.ReadPixels(graphicsDriver, pixels, x, y, width, height); err != nil {
if err := i.image.ReadPixels(graphicsDriver, pixels, region.Min.X, region.Min.Y, region.Dx(), region.Dy()); err != nil {
return err
}
return nil
@ -438,10 +438,10 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
if err := i.readPixelsFromGPUIfNeeded(graphicsDriver); err != nil {
return err
}
if got, want := len(pixels), 4*width*height; got != want {
if got, want := len(pixels), 4*region.Dx()*region.Dy(); got != want {
return fmt.Errorf("restorable: len(pixels) must be %d but %d at ReadPixels", want, got)
}
i.basePixels.ReadPixels(pixels, x, y, width, height, i.width, i.height)
i.basePixels.ReadPixels(pixels, region, i.width, i.height)
return nil
}
@ -497,7 +497,7 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error
if err := i.image.ReadPixels(graphicsDriver, pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
return err
}
i.basePixels.AddOrReplace(pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy())
i.basePixels.AddOrReplace(pix, r)
}
i.clearDrawTrianglesHistory()
@ -636,7 +636,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
if err := gimg.ReadPixels(graphicsDriver, pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
return err
}
i.basePixels.AddOrReplace(pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy())
i.basePixels.AddOrReplace(pix, r)
}
}

View File

@ -33,7 +33,7 @@ func TestMain(m *testing.M) {
func pixelsToColor(p *restorable.Pixels, i, j, imageWidth, imageHeight int) color.RGBA {
var pix [4]byte
p.ReadPixels(pix[:], i, j, 1, 1, imageWidth, imageHeight)
p.ReadPixels(pix[:], image.Rect(i, j, i+1, j+1), imageWidth, imageHeight)
return color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}
}
@ -61,7 +61,7 @@ func TestRestore(t *testing.T) {
defer img0.Dispose()
clr0 := color.RGBA{A: 0xff}
img0.WritePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, 1, 1)
img0.WritePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, image.Rect(0, 0, 1, 1))
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
}
@ -129,7 +129,7 @@ func TestRestoreChain(t *testing.T) {
}
}()
clr := color.RGBA{A: 0xff}
imgs[0].WritePixels([]byte{clr.R, clr.G, clr.B, clr.A}, 0, 0, 1, 1)
imgs[0].WritePixels([]byte{clr.R, clr.G, clr.B, clr.A}, image.Rect(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{R: 0xff, A: 0xff}
imgs[0].WritePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
imgs[0].WritePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, image.Rect(0, 0, w, h))
clr7 := color.RGBA{G: 0xff, A: 0xff}
imgs[7].WritePixels([]byte{clr7.R, clr7.G, clr7.B, clr7.A}, 0, 0, w, h)
imgs[7].WritePixels([]byte{clr7.R, clr7.G, clr7.B, clr7.A}, image.Rect(0, 0, w, h))
clr8 := color.RGBA{B: 0xff, A: 0xff}
imgs[8].WritePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, 0, 0, w, h)
imgs[8].WritePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, image.Rect(0, 0, w, h))
is := graphics.QuadIndices()
dr := graphicsdriver.Region{
@ -228,7 +228,7 @@ func TestRestoreOverrideSource(t *testing.T) {
}()
clr0 := color.RGBA{A: 0xff}
clr1 := color.RGBA{B: 0x01, A: 0xff}
img1.WritePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
img1.WritePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, image.Rect(0, 0, w, h))
is := graphics.QuadIndices()
dr := graphicsdriver.Region{
X: 0,
@ -238,7 +238,7 @@ func TestRestoreOverrideSource(t *testing.T) {
}
img2.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img1}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img3.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img2}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img0.WritePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
img0.WritePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, image.Rect(0, 0, w, h))
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); 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, restorable.ImageTypeRegular)
img.WritePixels(rgba.Pix, 0, 0, s.X, s.Y)
img.WritePixels(rgba.Pix, image.Rect(0, 0, s.X, s.Y))
return img
}
@ -485,12 +485,12 @@ func TestWritePixels(t *testing.T) {
for i := range pix {
pix[i] = 0xff
}
img.WritePixels(pix, 5, 7, 4, 4)
img.WritePixels(pix, image.Rect(5, 7, 9, 11))
// Check the region (5, 7)-(9, 11). Outside state is indeterminate.
for i := range pix {
pix[i] = 0
}
if err := img.ReadPixels(ui.GraphicsDriverForTesting(), pix, 5, 7, 4, 4); err != nil {
if err := img.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(5, 7, 9, 11)); err != nil {
t.Fatal(err)
}
for j := 7; j < 11; j++ {
@ -509,7 +509,7 @@ func TestWritePixels(t *testing.T) {
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err)
}
if err := img.ReadPixels(ui.GraphicsDriverForTesting(), pix, 5, 7, 4, 4); err != nil {
if err := img.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(5, 7, 9, 11)); err != nil {
t.Fatal(err)
}
for j := 7; j < 11; j++ {
@ -544,7 +544,7 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) {
Height: 1,
}
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img1.WritePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
img1.WritePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, image.Rect(0, 0, 2, 1))
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err)
@ -553,7 +553,7 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) {
t.Fatal(err)
}
var pix [4]byte
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix[:], 0, 0, 1, 1); err != nil {
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix[:], image.Rect(0, 0, 1, 1)); err != nil {
t.Fatal(err)
}
got := color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}
@ -597,7 +597,7 @@ func TestDispose(t *testing.T) {
t.Fatal(err)
}
var pix [4]byte
if err := img0.ReadPixels(ui.GraphicsDriverForTesting(), pix[:], 0, 0, 1, 1); err != nil {
if err := img0.ReadPixels(ui.GraphicsDriverForTesting(), pix[:], image.Rect(0, 0, 1, 1)); err != nil {
t.Fatal(err)
}
got := color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}
@ -615,7 +615,7 @@ func TestWritePixelsPart(t *testing.T) {
img := restorable.NewImage(4, 4, restorable.ImageTypeRegular)
// This doesn't make the image stale. Its base pixels are available.
img.WritePixels(pix, 1, 1, 2, 2)
img.WritePixels(pix, image.Rect(1, 1, 3, 3))
cases := []struct {
i int
@ -690,7 +690,7 @@ func TestWritePixelsOnly(t *testing.T) {
defer img1.Dispose()
for i := 0; i < w*h; i += 5 {
img0.WritePixels([]byte{1, 2, 3, 4}, i%w, i/w, 1, 1)
img0.WritePixels([]byte{1, 2, 3, 4}, image.Rect(i%w, i/w, i%w+1, i/w+1))
}
vs := quadVertices(1, 1, 0, 0)
@ -702,7 +702,7 @@ func TestWritePixelsOnly(t *testing.T) {
Height: 1,
}
img1.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{img0}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
img0.WritePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
img0.WritePixels([]byte{5, 6, 7, 8}, image.Rect(0, 0, 1, 1))
// BasePixelsForTesting is available without GPU accessing.
for j := 0; j < h; j++ {
@ -744,14 +744,14 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
src := restorable.NewImage(w, h, restorable.ImageTypeRegular)
// First, make sure that dst has pixels
dst.WritePixels(make([]byte, 4*w*h), 0, 0, w, h)
dst.WritePixels(make([]byte, 4*w*h), image.Rect(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.WritePixels(pix, 0, 0, w, h)
src.WritePixels(pix, image.Rect(0, 0, w, h))
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
dr := graphicsdriver.Region{
@ -767,7 +767,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
want := byte(0xff)
var result [4]byte
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result[:], 0, 0, 1, 1); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result[:], image.Rect(0, 0, 1, 1)); err != nil {
t.Fatal(err)
}
got := result[0]
@ -790,7 +790,7 @@ func TestAllowWritePixelsAfterDrawTriangles(t *testing.T) {
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
dst.WritePixels(make([]byte, 4*w*h), 0, 0, w, h)
dst.WritePixels(make([]byte, 4*w*h), image.Rect(0, 0, w, h))
// WritePixels for a whole image doesn't panic.
}
@ -803,7 +803,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
for i := range pix {
pix[i] = 0xff
}
src.WritePixels(pix, 0, 0, w, h)
src.WritePixels(pix, image.Rect(0, 0, w, h))
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
@ -814,7 +814,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
dst.WritePixels(make([]byte, 4*2*2), 0, 0, 2, 2)
dst.WritePixels(make([]byte, 4*2*2), image.Rect(0, 0, 2, 2))
// WritePixels for a part of image doesn't panic.
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
@ -825,7 +825,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
}
result := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, 0, 0, w, h); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, image.Rect(0, 0, w, h)); err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {
@ -861,11 +861,11 @@ func TestExtend(t *testing.T) {
}
}
orig.WritePixels(pix, 0, 0, w, h)
orig.WritePixels(pix, image.Rect(0, 0, w, h))
extended := orig.Extend(w*2, h*2) // After this, orig is already disposed.
result := make([]byte, 4*(w*2)*(h*2))
if err := extended.ReadPixels(ui.GraphicsDriverForTesting(), result, 0, 0, w*2, h*2); err != nil {
if err := extended.ReadPixels(ui.GraphicsDriverForTesting(), result, image.Rect(0, 0, w*2, h*2)); err != nil {
t.Fatal(err)
}
for j := 0; j < h*2; j++ {
@ -901,7 +901,7 @@ func TestDrawTrianglesAndExtend(t *testing.T) {
pix[4*idx+3] = v
}
}
src.WritePixels(pix, 0, 0, w, h)
src.WritePixels(pix, image.Rect(0, 0, w, h))
orig := restorable.NewImage(w, h, restorable.ImageTypeRegular)
vs := quadVertices(w, h, 0, 0)
@ -916,7 +916,7 @@ func TestDrawTrianglesAndExtend(t *testing.T) {
extended := orig.Extend(w*2, h*2) // After this, orig is already disposed.
result := make([]byte, 4*(w*2)*(h*2))
if err := extended.ReadPixels(ui.GraphicsDriverForTesting(), result, 0, 0, w*2, h*2); err != nil {
if err := extended.ReadPixels(ui.GraphicsDriverForTesting(), result, image.Rect(0, 0, w*2, h*2)); err != nil {
t.Fatal(err)
}
for j := 0; j < h*2; j++ {
@ -936,13 +936,13 @@ func TestDrawTrianglesAndExtend(t *testing.T) {
func TestClearPixels(t *testing.T) {
const w, h = 16, 16
img := restorable.NewImage(w, h, restorable.ImageTypeRegular)
img.WritePixels(make([]byte, 4*4*4), 0, 0, 4, 4)
img.WritePixels(make([]byte, 4*4*4), 4, 0, 4, 4)
img.ClearPixels(0, 0, 4, 4)
img.ClearPixels(4, 0, 4, 4)
img.WritePixels(make([]byte, 4*4*4), image.Rect(0, 0, 4, 4))
img.WritePixels(make([]byte, 4*4*4), image.Rect(4, 0, 8, 4))
img.ClearPixels(image.Rect(0, 0, 4, 4))
img.ClearPixels(image.Rect(4, 0, 8, 4))
// After clearing, the regions will be available again.
img.WritePixels(make([]byte, 4*8*4), 0, 0, 8, 4)
img.WritePixels(make([]byte, 4*8*4), image.Rect(0, 0, 8, 4))
}
func TestMutateSlices(t *testing.T) {
@ -956,7 +956,7 @@ func TestMutateSlices(t *testing.T) {
pix[4*i+2] = byte(i)
pix[4*i+3] = 0xff
}
src.WritePixels(pix, 0, 0, w, h)
src.WritePixels(pix, image.Rect(0, 0, w, h))
vs := quadVertices(w, h, 0, 0)
is := make([]uint16, len(graphics.QuadIndices()))
@ -982,11 +982,11 @@ func TestMutateSlices(t *testing.T) {
}
srcPix := make([]byte, 4*w*h)
if err := src.ReadPixels(ui.GraphicsDriverForTesting(), srcPix, 0, 0, w, h); err != nil {
if err := src.ReadPixels(ui.GraphicsDriverForTesting(), srcPix, image.Rect(0, 0, w, h)); err != nil {
t.Fatal(err)
}
dstPix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), dstPix, 0, 0, w, h); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), dstPix, image.Rect(0, 0, w, h)); err != nil {
t.Fatal(err)
}
@ -1015,7 +1015,7 @@ func TestOverlappedPixels(t *testing.T) {
pix0[idx+3] = 0xff
}
}
dst.WritePixels(pix0, 0, 0, 2, 2)
dst.WritePixels(pix0, image.Rect(0, 0, 2, 2))
pix1 := make([]byte, 4*2*2)
for j := 0; j < 2; j++ {
@ -1027,7 +1027,7 @@ func TestOverlappedPixels(t *testing.T) {
pix1[idx+3] = 0xff
}
}
dst.WritePixels(pix1, 1, 1, 2, 2)
dst.WritePixels(pix1, image.Rect(1, 1, 3, 3))
wantColors := []color.RGBA{
{0xff, 0, 0, 0xff},
@ -1044,7 +1044,7 @@ func TestOverlappedPixels(t *testing.T) {
}
result := make([]byte, 4*3*3)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, 0, 0, 3, 3); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, image.Rect(0, 0, 3, 3)); err != nil {
t.Fatal(err)
}
for j := 0; j < 3; j++ {
@ -1058,7 +1058,7 @@ func TestOverlappedPixels(t *testing.T) {
}
}
dst.WritePixels(nil, 1, 0, 2, 2)
dst.WritePixels(nil, image.Rect(1, 0, 3, 2))
wantColors = []color.RGBA{
{0xff, 0, 0, 0xff},
@ -1073,7 +1073,7 @@ func TestOverlappedPixels(t *testing.T) {
{0, 0xff, 0, 0xff},
{0, 0xff, 0, 0xff},
}
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, 0, 0, 3, 3); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, image.Rect(0, 0, 3, 3)); err != nil {
t.Fatal(err)
}
for j := 0; j < 3; j++ {
@ -1097,7 +1097,7 @@ func TestOverlappedPixels(t *testing.T) {
pix2[idx+3] = 0xff
}
}
dst.WritePixels(pix2, 1, 1, 2, 2)
dst.WritePixels(pix2, image.Rect(1, 1, 3, 3))
wantColors = []color.RGBA{
{0xff, 0, 0, 0xff},
@ -1112,7 +1112,7 @@ func TestOverlappedPixels(t *testing.T) {
{0, 0, 0xff, 0xff},
{0, 0, 0xff, 0xff},
}
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, 0, 0, 3, 3); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, image.Rect(0, 0, 3, 3)); err != nil {
t.Fatal(err)
}
for j := 0; j < 3; j++ {
@ -1133,7 +1133,7 @@ func TestOverlappedPixels(t *testing.T) {
t.Fatal(err)
}
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, 0, 0, 3, 3); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), result, image.Rect(0, 0, 3, 3)); err != nil {
t.Fatal(err)
}
for j := 0; j < 3; j++ {
@ -1154,7 +1154,7 @@ func TestDrawTrianglesAndReadPixels(t *testing.T) {
src := restorable.NewImage(w, h, restorable.ImageTypeRegular)
dst := restorable.NewImage(w, h, restorable.ImageTypeRegular)
src.WritePixels([]byte{0x80, 0x80, 0x80, 0x80}, 0, 0, 1, 1)
src.WritePixels([]byte{0x80, 0x80, 0x80, 0x80}, image.Rect(0, 0, 1, 1))
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
@ -1167,7 +1167,7 @@ func TestDrawTrianglesAndReadPixels(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*restorable.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, restorable.NearestFilterShader, nil, false)
pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)); err != nil {
t.Fatal(err)
}
if got, want := (color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}), (color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}); !sameColors(got, want, 1) {
@ -1179,10 +1179,10 @@ func TestWritePixelsAndDrawTriangles(t *testing.T) {
src := restorable.NewImage(1, 1, restorable.ImageTypeRegular)
dst := restorable.NewImage(2, 1, restorable.ImageTypeRegular)
src.WritePixels([]byte{0x80, 0x80, 0x80, 0x80}, 0, 0, 1, 1)
src.WritePixels([]byte{0x80, 0x80, 0x80, 0x80}, image.Rect(0, 0, 1, 1))
// Call WritePixels first.
dst.WritePixels([]byte{0x40, 0x40, 0x40, 0x40}, 0, 0, 1, 1)
dst.WritePixels([]byte{0x40, 0x40, 0x40, 0x40}, image.Rect(0, 0, 1, 1))
// Call DrawTriangles at a different region second.
vs := quadVertices(1, 1, 1, 0)
@ -1197,7 +1197,7 @@ func TestWritePixelsAndDrawTriangles(t *testing.T) {
// Get the pixels.
pix := make([]byte, 4*2*1)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, 2, 1); err != nil {
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(0, 0, 2, 1)); err != nil {
t.Fatal(err)
}
if got, want := (color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]}), (color.RGBA{R: 0x40, G: 0x40, B: 0x40, A: 0x40}); !sameColors(got, want, 1) {

View File

@ -42,19 +42,19 @@ func (p *pixelsRecord) clearIfOverlapped(rect image.Rectangle) {
}
}
func (p *pixelsRecord) readPixels(pixels []byte, x, y, width, height, imageWidth, imageHeight int) {
r := p.rect.Intersect(image.Rect(x, y, x+width, y+height)).Intersect(image.Rect(0, 0, imageWidth, imageHeight))
func (p *pixelsRecord) readPixels(pixels []byte, region image.Rectangle, imageWidth, imageHeight int) {
r := p.rect.Intersect(region.Intersect(image.Rect(0, 0, imageWidth, imageHeight)))
if r.Empty() {
return
}
dstBaseX := r.Min.X - x
dstBaseY := r.Min.Y - y
dstBaseX := r.Min.X - region.Min.X
dstBaseY := r.Min.Y - region.Min.Y
srcBaseX := r.Min.X - p.rect.Min.X
srcBaseY := r.Min.Y - p.rect.Min.Y
lineWidth := 4 * r.Dx()
for j := 0; j < r.Dy(); j++ {
dstX := 4 * ((dstBaseY+j)*width + dstBaseX)
dstX := 4 * ((dstBaseY+j)*region.Dx() + dstBaseX)
srcX := 4 * ((srcBaseY+j)*p.rect.Dx() + srcBaseX)
copy(pixels[dstX:dstX+lineWidth], p.pix[srcX:srcX+lineWidth])
}
@ -64,21 +64,19 @@ type pixelsRecords struct {
records []*pixelsRecord
}
func (pr *pixelsRecords) addOrReplace(pixels []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))
func (pr *pixelsRecords) addOrReplace(pixels []byte, region image.Rectangle) {
if len(pixels) != 4*region.Dx()*region.Dy() {
msg := fmt.Sprintf("restorable: len(pixels) must be 4*%d*%d = %d but %d", region.Dx(), region.Dy(), 4*region.Dx()*region.Dy(), len(pixels))
if pixels == nil {
msg += " (nil)"
}
panic(msg)
}
rect := image.Rect(x, y, x+width, y+height)
// Remove or update the duplicated records first.
var n int
for _, r := range pr.records {
if r.rect.In(rect) {
if r.rect.In(region) {
continue
}
pr.records[n] = r
@ -91,19 +89,18 @@ func (pr *pixelsRecords) addOrReplace(pixels []byte, x, y, width, height int) {
// Add the new record.
pr.records = append(pr.records, &pixelsRecord{
rect: rect,
rect: region,
pix: pixels,
})
}
func (pr *pixelsRecords) clear(x, y, width, height int) {
newr := image.Rect(x, y, x+width, y+height)
func (pr *pixelsRecords) clear(region image.Rectangle) {
var n int
for _, r := range pr.records {
if r.rect.In(newr) {
if r.rect.In(region) {
continue
}
r.clearIfOverlapped(newr)
r.clearIfOverlapped(region)
pr.records[n] = r
n++
}
@ -113,12 +110,12 @@ func (pr *pixelsRecords) clear(x, y, width, height int) {
pr.records = pr.records[:n]
}
func (pr *pixelsRecords) readPixels(pixels []byte, x, y, width, height, imageWidth, imageHeight int) {
func (pr *pixelsRecords) readPixels(pixels []byte, region image.Rectangle, imageWidth, imageHeight int) {
for i := range pixels {
pixels[i] = 0
}
for _, r := range pr.records {
r.readPixels(pixels, x, y, width, height, imageWidth, imageHeight)
r.readPixels(pixels, region, imageWidth, imageHeight)
}
}

View File

@ -15,6 +15,7 @@
package restorable_test
import (
"image"
"image/color"
"testing"
@ -89,7 +90,7 @@ func TestShaderChain(t *testing.T) {
imgs = append(imgs, img)
}
imgs[0].WritePixels([]byte{0xff, 0, 0, 0xff}, 0, 0, 1, 1)
imgs[0].WritePixels([]byte{0xff, 0, 0, 0xff}, image.Rect(0, 0, 1, 1))
s := restorable.NewShader(etesting.ShaderProgramImages(1))
for i := 0; i < num-1; i++ {
@ -123,9 +124,9 @@ func TestShaderMultipleSources(t *testing.T) {
for i := range srcs {
srcs[i] = restorable.NewImage(1, 1, restorable.ImageTypeRegular)
}
srcs[0].WritePixels([]byte{0x40, 0, 0, 0xff}, 0, 0, 1, 1)
srcs[1].WritePixels([]byte{0, 0x80, 0, 0xff}, 0, 0, 1, 1)
srcs[2].WritePixels([]byte{0, 0, 0xc0, 0xff}, 0, 0, 1, 1)
srcs[0].WritePixels([]byte{0x40, 0, 0, 0xff}, image.Rect(0, 0, 1, 1))
srcs[1].WritePixels([]byte{0, 0x80, 0, 0xff}, image.Rect(0, 0, 1, 1))
srcs[2].WritePixels([]byte{0, 0, 0xc0, 0xff}, image.Rect(0, 0, 1, 1))
dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular)
@ -162,7 +163,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
0x40, 0, 0, 0xff,
0, 0x80, 0, 0xff,
0, 0, 0xc0, 0xff,
}, 0, 0, 3, 1)
}, image.Rect(0, 0, 3, 1))
srcs := [graphics.ShaderImageCount]*restorable.Image{src, src, src}
dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular)