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) { func (i *Image) WritePixels(pix []byte, x, y, width, height int) {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() 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 { if i.disposed {
panic("atlas: the image must not be disposed at writePixels") 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))) 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() r := i.regionWithPadding()
if x != 0 || y != 0 || width != i.width || height != i.height || i.paddingSize() == 0 { if region.Min.X != 0 || region.Min.Y != 0 || region.Dx() != i.width || region.Dy() != i.height || i.paddingSize() == 0 {
x += r.Min.X region = region.Add(r.Min)
y += r.Min.Y
if pix == nil { if pix == nil {
i.backend.restorable.WritePixels(nil, x, y, width, height) i.backend.restorable.WritePixels(nil, region)
return return
} }
// Copy pixels in the case when pix is modified before the graphics command is executed. // Copy pixels in the case when pix is modified before the graphics command is executed.
pix2 := theTemporaryBytes.alloc(len(pix)) pix2 := theTemporaryBytes.alloc(len(pix))
copy(pix2, pix) copy(pix2, pix)
i.backend.restorable.WritePixels(pix2, x, y, width, height) i.backend.restorable.WritePixels(pix2, region)
return return
} }
@ -579,11 +578,11 @@ func (i *Image) writePixels(pix []byte, x, y, width, height int) {
} }
// Copy the content. // Copy the content.
for j := 0; j < height; j++ { for j := 0; j < region.Dy(); j++ {
copy(pixb[4*j*r.Dx():], pix[4*j*width:4*(j+1)*width]) 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 { 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() r := i.regionWithPadding()
x += r.Min.X x += r.Min.X
y += r.Min.Y 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. // 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() { if !i.backend.page.IsEmpty() {
// As this part can be reused, this should be cleared explicitly. // As this part can be reused, this should be cleared explicitly.
r := i.regionWithPadding() r := i.regionWithPadding()
i.backend.restorable.ClearPixels(r.Min.X, r.Min.Y, r.Dx(), r.Dy()) i.backend.restorable.ClearPixels(r)
return return
} }

View File

@ -38,30 +38,30 @@ func (p *Pixels) Apply(img *graphicscommand.Image) {
p.pixelsRecords.apply(img) 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 { if p.pixelsRecords == nil {
p.pixelsRecords = &pixelsRecords{} 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 // 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. // the region is allocated but nothing is rendered. See TestDisposeImmediately at shareable package.
if p.pixelsRecords == nil { if p.pixelsRecords == nil {
return 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 { if p.pixelsRecords == nil {
for i := range pixels { for i := range pixels {
pixels[i] = 0 pixels[i] = 0
} }
return 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 { func (p *Pixels) AppendRegion(regions []image.Rectangle) []image.Rectangle {
@ -247,7 +247,7 @@ func (i *Image) makeStale(rect image.Rectangle) {
if r.Empty() { if r.Empty() {
continue 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. // 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. // ClearPixels clears the specified region by WritePixels.
func (i *Image) ClearPixels(x, y, width, height int) { func (i *Image) ClearPixels(region image.Rectangle) {
i.WritePixels(nil, x, y, width, height) i.WritePixels(nil, region)
} }
func (i *Image) needsRestoring() bool { func (i *Image) needsRestoring() bool {
@ -268,13 +268,13 @@ func (i *Image) needsRestoring() bool {
// WritePixels replaces the image pixels with the given pixels slice. // WritePixels replaces the image pixels with the given pixels slice.
// //
// The specified region must not be overlapped with other regions by WritePixels. // The specified region must not be overlapped with other regions by WritePixels.
func (i *Image) WritePixels(pixels []byte, x, y, width, height int) { func (i *Image) WritePixels(pixels []byte, region image.Rectangle) {
if width <= 0 || height <= 0 { if region.Dx() <= 0 || region.Dy() <= 0 {
panic("restorable: width/height must be positive") panic("restorable: width/height must be positive")
} }
w, h := i.width, i.height 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 { if !region.In(image.Rect(0, 0, w, h)) {
panic(fmt.Sprintf("restorable: out of range x: %d, y: %d, width: %d, height: %d", x, y, width, height)) panic(fmt.Sprintf("restorable: out of range %v", region))
} }
// TODO: Avoid making other images stale if possible. (#514) // 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) theImages.makeStaleIfDependingOn(i)
if pixels != nil { 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 { } else {
// TODO: When pixels == nil, we don't have to care the pixel state there. In such cases, the image // 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. // accepts only WritePixels and not Fill or DrawTriangles.
// TODO: Separate Image struct into two: images for WritePixels-only, and the others. // 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. // Even if the image is already stale, call makeStale to extend the stale region.
if !needsRestoring() || !i.needsRestoring() || i.stale { if !needsRestoring() || !i.needsRestoring() || i.stale {
i.makeStale(image.Rect(x, y, x+width, y+height)) i.makeStale(region)
return return
} }
if x == 0 && y == 0 && width == w && height == h { if region.Eq(image.Rect(0, 0, w, h)) {
if pixels != nil { if pixels != nil {
// pixels can point to a shared region. // pixels can point to a shared region.
// This function is responsible to copy this. // This function is responsible to copy this.
copiedPixels := make([]byte, len(pixels)) copiedPixels := make([]byte, len(pixels))
copy(copiedPixels, pixels) copy(copiedPixels, pixels)
i.basePixels.AddOrReplace(copiedPixels, 0, 0, w, h) i.basePixels.AddOrReplace(copiedPixels, image.Rect(0, 0, w, h))
} else { } else {
i.basePixels.Clear(0, 0, w, h) i.basePixels.Clear(image.Rect(0, 0, w, h))
} }
i.clearDrawTrianglesHistory() i.clearDrawTrianglesHistory()
i.stale = false 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. // Records for DrawTriangles cannot come before records for WritePixels.
if len(i.drawTrianglesHistory) > 0 { if len(i.drawTrianglesHistory) > 0 {
i.makeStale(image.Rect(x, y, x+width, y+height)) i.makeStale(region)
return return
} }
@ -323,9 +323,9 @@ func (i *Image) WritePixels(pixels []byte, x, y, width, height int) {
// This function is responsible to copy this. // This function is responsible to copy this.
copiedPixels := make([]byte, len(pixels)) copiedPixels := make([]byte, len(pixels))
copy(copiedPixels, pixels) copy(copiedPixels, pixels)
i.basePixels.AddOrReplace(copiedPixels, x, y, width, height) i.basePixels.AddOrReplace(copiedPixels, region)
} else { } 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 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 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 err
} }
return nil return nil
@ -438,10 +438,10 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
if err := i.readPixelsFromGPUIfNeeded(graphicsDriver); err != nil { if err := i.readPixelsFromGPUIfNeeded(graphicsDriver); err != nil {
return err 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) 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 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 { if err := i.image.ReadPixels(graphicsDriver, pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
return err return err
} }
i.basePixels.AddOrReplace(pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy()) i.basePixels.AddOrReplace(pix, r)
} }
i.clearDrawTrianglesHistory() 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 { if err := gimg.ReadPixels(graphicsDriver, pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
return err 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 { func pixelsToColor(p *restorable.Pixels, i, j, imageWidth, imageHeight int) color.RGBA {
var pix [4]byte 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]} 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() defer img0.Dispose()
clr0 := color.RGBA{A: 0xff} 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 { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -129,7 +129,7 @@ func TestRestoreChain(t *testing.T) {
} }
}() }()
clr := color.RGBA{A: 0xff} 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++ { for i := 0; i < num-1; i++ {
vs := quadVertices(1, 1, 0, 0) vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
@ -174,11 +174,11 @@ func TestRestoreChain2(t *testing.T) {
}() }()
clr0 := color.RGBA{R: 0xff, A: 0xff} 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} 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} 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() is := graphics.QuadIndices()
dr := graphicsdriver.Region{ dr := graphicsdriver.Region{
@ -228,7 +228,7 @@ func TestRestoreOverrideSource(t *testing.T) {
}() }()
clr0 := color.RGBA{A: 0xff} clr0 := color.RGBA{A: 0xff}
clr1 := color.RGBA{B: 0x01, 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() is := graphics.QuadIndices()
dr := graphicsdriver.Region{ dr := graphicsdriver.Region{
X: 0, 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) 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) 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) 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 { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err) t.Fatal(err)
@ -411,7 +411,7 @@ func TestRestoreComplexGraph(t *testing.T) {
func newImageFromImage(rgba *image.RGBA) *restorable.Image { func newImageFromImage(rgba *image.RGBA) *restorable.Image {
s := rgba.Bounds().Size() s := rgba.Bounds().Size()
img := restorable.NewImage(s.X, s.Y, restorable.ImageTypeRegular) 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 return img
} }
@ -485,12 +485,12 @@ func TestWritePixels(t *testing.T) {
for i := range pix { for i := range pix {
pix[i] = 0xff 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. // Check the region (5, 7)-(9, 11). Outside state is indeterminate.
for i := range pix { for i := range pix {
pix[i] = 0 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) t.Fatal(err)
} }
for j := 7; j < 11; j++ { for j := 7; j < 11; j++ {
@ -509,7 +509,7 @@ func TestWritePixels(t *testing.T) {
if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) 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) t.Fatal(err)
} }
for j := 7; j < 11; j++ { for j := 7; j < 11; j++ {
@ -544,7 +544,7 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) {
Height: 1, 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.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 { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
t.Fatal(err) t.Fatal(err)
@ -553,7 +553,7 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
var pix [4]byte 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) t.Fatal(err)
} }
got := color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]} 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) t.Fatal(err)
} }
var pix [4]byte 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) t.Fatal(err)
} }
got := color.RGBA{R: pix[0], G: pix[1], B: pix[2], A: pix[3]} 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) img := restorable.NewImage(4, 4, restorable.ImageTypeRegular)
// This doesn't make the image stale. Its base pixels are available. // 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 { cases := []struct {
i int i int
@ -690,7 +690,7 @@ func TestWritePixelsOnly(t *testing.T) {
defer img1.Dispose() defer img1.Dispose()
for i := 0; i < w*h; i += 5 { 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) vs := quadVertices(1, 1, 0, 0)
@ -702,7 +702,7 @@ func TestWritePixelsOnly(t *testing.T) {
Height: 1, 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.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. // BasePixelsForTesting is available without GPU accessing.
for j := 0; j < h; j++ { for j := 0; j < h; j++ {
@ -744,14 +744,14 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
src := restorable.NewImage(w, h, restorable.ImageTypeRegular) src := restorable.NewImage(w, h, restorable.ImageTypeRegular)
// First, make sure that dst has pixels // 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. // Second, draw src to dst. If the implementation is correct, dst becomes stale.
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
for i := range pix { for i := range pix {
pix[i] = 0xff 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) vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
dr := graphicsdriver.Region{ dr := graphicsdriver.Region{
@ -767,7 +767,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
want := byte(0xff) want := byte(0xff)
var result [4]byte 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) t.Fatal(err)
} }
got := result[0] got := result[0]
@ -790,7 +790,7 @@ func TestAllowWritePixelsAfterDrawTriangles(t *testing.T) {
Height: h, 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.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. // WritePixels for a whole image doesn't panic.
} }
@ -803,7 +803,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
for i := range pix { for i := range pix {
pix[i] = 0xff 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) vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
@ -814,7 +814,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
Height: h, 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.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. // WritePixels for a part of image doesn't panic.
if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting(), false); err != nil {
@ -825,7 +825,7 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
} }
result := make([]byte, 4*w*h) 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) t.Fatal(err)
} }
for j := 0; j < h; j++ { 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. extended := orig.Extend(w*2, h*2) // After this, orig is already disposed.
result := make([]byte, 4*(w*2)*(h*2)) 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) t.Fatal(err)
} }
for j := 0; j < h*2; j++ { for j := 0; j < h*2; j++ {
@ -901,7 +901,7 @@ func TestDrawTrianglesAndExtend(t *testing.T) {
pix[4*idx+3] = v 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) orig := restorable.NewImage(w, h, restorable.ImageTypeRegular)
vs := quadVertices(w, h, 0, 0) 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. extended := orig.Extend(w*2, h*2) // After this, orig is already disposed.
result := make([]byte, 4*(w*2)*(h*2)) 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) t.Fatal(err)
} }
for j := 0; j < h*2; j++ { for j := 0; j < h*2; j++ {
@ -936,13 +936,13 @@ func TestDrawTrianglesAndExtend(t *testing.T) {
func TestClearPixels(t *testing.T) { func TestClearPixels(t *testing.T) {
const w, h = 16, 16 const w, h = 16, 16
img := restorable.NewImage(w, h, restorable.ImageTypeRegular) 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), image.Rect(0, 0, 4, 4))
img.WritePixels(make([]byte, 4*4*4), 4, 0, 4, 4) img.WritePixels(make([]byte, 4*4*4), image.Rect(4, 0, 8, 4))
img.ClearPixels(0, 0, 4, 4) img.ClearPixels(image.Rect(0, 0, 4, 4))
img.ClearPixels(4, 0, 4, 4) img.ClearPixels(image.Rect(4, 0, 8, 4))
// After clearing, the regions will be available again. // 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) { func TestMutateSlices(t *testing.T) {
@ -956,7 +956,7 @@ func TestMutateSlices(t *testing.T) {
pix[4*i+2] = byte(i) pix[4*i+2] = byte(i)
pix[4*i+3] = 0xff 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) vs := quadVertices(w, h, 0, 0)
is := make([]uint16, len(graphics.QuadIndices())) is := make([]uint16, len(graphics.QuadIndices()))
@ -982,11 +982,11 @@ func TestMutateSlices(t *testing.T) {
} }
srcPix := make([]byte, 4*w*h) 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) t.Fatal(err)
} }
dstPix := make([]byte, 4*w*h) 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) t.Fatal(err)
} }
@ -1015,7 +1015,7 @@ func TestOverlappedPixels(t *testing.T) {
pix0[idx+3] = 0xff 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) pix1 := make([]byte, 4*2*2)
for j := 0; j < 2; j++ { for j := 0; j < 2; j++ {
@ -1027,7 +1027,7 @@ func TestOverlappedPixels(t *testing.T) {
pix1[idx+3] = 0xff pix1[idx+3] = 0xff
} }
} }
dst.WritePixels(pix1, 1, 1, 2, 2) dst.WritePixels(pix1, image.Rect(1, 1, 3, 3))
wantColors := []color.RGBA{ wantColors := []color.RGBA{
{0xff, 0, 0, 0xff}, {0xff, 0, 0, 0xff},
@ -1044,7 +1044,7 @@ func TestOverlappedPixels(t *testing.T) {
} }
result := make([]byte, 4*3*3) 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) t.Fatal(err)
} }
for j := 0; j < 3; j++ { 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{ wantColors = []color.RGBA{
{0xff, 0, 0, 0xff}, {0xff, 0, 0, 0xff},
@ -1073,7 +1073,7 @@ func TestOverlappedPixels(t *testing.T) {
{0, 0xff, 0, 0xff}, {0, 0xff, 0, 0xff},
{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) t.Fatal(err)
} }
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
@ -1097,7 +1097,7 @@ func TestOverlappedPixels(t *testing.T) {
pix2[idx+3] = 0xff pix2[idx+3] = 0xff
} }
} }
dst.WritePixels(pix2, 1, 1, 2, 2) dst.WritePixels(pix2, image.Rect(1, 1, 3, 3))
wantColors = []color.RGBA{ wantColors = []color.RGBA{
{0xff, 0, 0, 0xff}, {0xff, 0, 0, 0xff},
@ -1112,7 +1112,7 @@ func TestOverlappedPixels(t *testing.T) {
{0, 0, 0xff, 0xff}, {0, 0, 0xff, 0xff},
{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) t.Fatal(err)
} }
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
@ -1133,7 +1133,7 @@ func TestOverlappedPixels(t *testing.T) {
t.Fatal(err) 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) t.Fatal(err)
} }
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
@ -1154,7 +1154,7 @@ func TestDrawTrianglesAndReadPixels(t *testing.T) {
src := restorable.NewImage(w, h, restorable.ImageTypeRegular) src := restorable.NewImage(w, h, restorable.ImageTypeRegular)
dst := 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) vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices() 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) 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) 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) 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) { 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) src := restorable.NewImage(1, 1, restorable.ImageTypeRegular)
dst := restorable.NewImage(2, 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. // 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. // Call DrawTriangles at a different region second.
vs := quadVertices(1, 1, 1, 0) vs := quadVertices(1, 1, 1, 0)
@ -1197,7 +1197,7 @@ func TestWritePixelsAndDrawTriangles(t *testing.T) {
// Get the pixels. // Get the pixels.
pix := make([]byte, 4*2*1) 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) 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) { 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) { func (p *pixelsRecord) readPixels(pixels []byte, region image.Rectangle, imageWidth, imageHeight int) {
r := p.rect.Intersect(image.Rect(x, y, x+width, y+height)).Intersect(image.Rect(0, 0, imageWidth, imageHeight)) r := p.rect.Intersect(region.Intersect(image.Rect(0, 0, imageWidth, imageHeight)))
if r.Empty() { if r.Empty() {
return return
} }
dstBaseX := r.Min.X - x dstBaseX := r.Min.X - region.Min.X
dstBaseY := r.Min.Y - y dstBaseY := r.Min.Y - region.Min.Y
srcBaseX := r.Min.X - p.rect.Min.X srcBaseX := r.Min.X - p.rect.Min.X
srcBaseY := r.Min.Y - p.rect.Min.Y srcBaseY := r.Min.Y - p.rect.Min.Y
lineWidth := 4 * r.Dx() lineWidth := 4 * r.Dx()
for j := 0; j < r.Dy(); j++ { 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) srcX := 4 * ((srcBaseY+j)*p.rect.Dx() + srcBaseX)
copy(pixels[dstX:dstX+lineWidth], p.pix[srcX:srcX+lineWidth]) copy(pixels[dstX:dstX+lineWidth], p.pix[srcX:srcX+lineWidth])
} }
@ -64,21 +64,19 @@ type pixelsRecords struct {
records []*pixelsRecord records []*pixelsRecord
} }
func (pr *pixelsRecords) addOrReplace(pixels []byte, x, y, width, height int) { func (pr *pixelsRecords) addOrReplace(pixels []byte, region image.Rectangle) {
if len(pixels) != 4*width*height { if len(pixels) != 4*region.Dx()*region.Dy() {
msg := fmt.Sprintf("restorable: len(pixels) must be 4*%d*%d = %d but %d", width, height, 4*width*height, len(pixels)) 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 { if pixels == nil {
msg += " (nil)" msg += " (nil)"
} }
panic(msg) panic(msg)
} }
rect := image.Rect(x, y, x+width, y+height)
// Remove or update the duplicated records first. // Remove or update the duplicated records first.
var n int var n int
for _, r := range pr.records { for _, r := range pr.records {
if r.rect.In(rect) { if r.rect.In(region) {
continue continue
} }
pr.records[n] = r pr.records[n] = r
@ -91,19 +89,18 @@ func (pr *pixelsRecords) addOrReplace(pixels []byte, x, y, width, height int) {
// Add the new record. // Add the new record.
pr.records = append(pr.records, &pixelsRecord{ pr.records = append(pr.records, &pixelsRecord{
rect: rect, rect: region,
pix: pixels, pix: pixels,
}) })
} }
func (pr *pixelsRecords) clear(x, y, width, height int) { func (pr *pixelsRecords) clear(region image.Rectangle) {
newr := image.Rect(x, y, x+width, y+height)
var n int var n int
for _, r := range pr.records { for _, r := range pr.records {
if r.rect.In(newr) { if r.rect.In(region) {
continue continue
} }
r.clearIfOverlapped(newr) r.clearIfOverlapped(region)
pr.records[n] = r pr.records[n] = r
n++ n++
} }
@ -113,12 +110,12 @@ func (pr *pixelsRecords) clear(x, y, width, height int) {
pr.records = pr.records[:n] 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 { for i := range pixels {
pixels[i] = 0 pixels[i] = 0
} }
for _, r := range pr.records { 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 package restorable_test
import ( import (
"image"
"image/color" "image/color"
"testing" "testing"
@ -89,7 +90,7 @@ func TestShaderChain(t *testing.T) {
imgs = append(imgs, img) 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)) s := restorable.NewShader(etesting.ShaderProgramImages(1))
for i := 0; i < num-1; i++ { for i := 0; i < num-1; i++ {
@ -123,9 +124,9 @@ func TestShaderMultipleSources(t *testing.T) {
for i := range srcs { for i := range srcs {
srcs[i] = restorable.NewImage(1, 1, restorable.ImageTypeRegular) srcs[i] = restorable.NewImage(1, 1, restorable.ImageTypeRegular)
} }
srcs[0].WritePixels([]byte{0x40, 0, 0, 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}, 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}, 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) dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular)
@ -162,7 +163,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
0x40, 0, 0, 0xff, 0x40, 0, 0, 0xff,
0, 0x80, 0, 0xff, 0, 0x80, 0, 0xff,
0, 0, 0xc0, 0xff, 0, 0, 0xc0, 0xff,
}, 0, 0, 3, 1) }, image.Rect(0, 0, 3, 1))
srcs := [graphics.ShaderImageCount]*restorable.Image{src, src, src} srcs := [graphics.ShaderImageCount]*restorable.Image{src, src, src}
dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular) dst := restorable.NewImage(1, 1, restorable.ImageTypeRegular)