all: refactoring: use image.Rectangle

This commit is contained in:
Hajime Hoshi 2023-04-28 00:55:10 +09:00
parent cdcffd7d4f
commit d2c991b774
8 changed files with 88 additions and 84 deletions

View File

@ -94,7 +94,7 @@ func (i *Image) Fill(clr color.Color) {
caf = float32(ca) / 0xffff caf = float32(ca) / 0xffff
b := i.Bounds() b := i.Bounds()
x, y := i.adjustPosition(b.Min.X, b.Min.Y) x, y := i.adjustPosition(b.Min.X, b.Min.Y)
i.image.Fill(crf, cgf, cbf, caf, x, y, b.Dx(), b.Dy()) i.image.Fill(crf, cgf, cbf, caf, image.Rect(x, y, x+b.Dx(), y+b.Dy()))
} }
func canSkipMipmap(geom GeoM, filter builtinshader.Filter) bool { func canSkipMipmap(geom GeoM, filter builtinshader.Filter) bool {
@ -881,7 +881,7 @@ func (i *Image) ReadPixels(pixels []byte) {
} }
x, y := i.adjustPosition(b.Min.X, b.Min.Y) x, y := i.adjustPosition(b.Min.X, b.Min.Y)
i.image.ReadPixels(pixels, x, y, b.Dx(), b.Dy()) i.image.ReadPixels(pixels, image.Rect(x, y, x+b.Dx(), y+b.Dy()))
} }
// At returns the color of the image at (x, y). // At returns the color of the image at (x, y).
@ -927,7 +927,7 @@ func (i *Image) at(x, y int) (r, g, b, a byte) {
x, y = i.adjustPosition(x, y) x, y = i.adjustPosition(x, y)
var pix [4]byte var pix [4]byte
i.image.ReadPixels(pix[:], x, y, 1, 1) i.image.ReadPixels(pix[:], image.Rect(x, y, x+1, y+1))
return pix[0], pix[1], pix[2], pix[3] return pix[0], pix[1], pix[2], pix[3]
} }
@ -950,7 +950,7 @@ func (i *Image) Set(x, y int, clr color.Color) {
dx, dy := i.adjustPosition(x, y) dx, dy := i.adjustPosition(x, y)
cr, cg, cb, ca := clr.RGBA() cr, cg, cb, ca := clr.RGBA()
i.image.WritePixels([]byte{byte(cr / 0x101), byte(cg / 0x101), byte(cb / 0x101), byte(ca / 0x101)}, dx, dy, 1, 1) i.image.WritePixels([]byte{byte(cr / 0x101), byte(cg / 0x101), byte(cb / 0x101), byte(ca / 0x101)}, image.Rect(dx, dy, dx+1, dy+1))
} }
// Dispose disposes the image data. // Dispose disposes the image data.
@ -997,7 +997,7 @@ func (i *Image) WritePixels(pixels []byte) {
// Do not need to copy pixels here. // Do not need to copy pixels here.
// * In internal/mipmap, pixels are copied when necessary. // * In internal/mipmap, pixels are copied when necessary.
// * In internal/atlas, pixels are copied to make its paddings. // * In internal/atlas, pixels are copied to make its paddings.
i.image.WritePixels(pixels, x, y, r.Dx(), r.Dy()) i.image.WritePixels(pixels, image.Rect(x, y, x+r.Dx(), y+r.Dy()))
} }
// ReplacePixels replaces the pixels of the image. // ReplacePixels replaces the pixels of the image.

View File

@ -515,10 +515,10 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
} }
// WritePixels replaces the pixels on the image. // WritePixels replaces the pixels on the image.
func (i *Image) WritePixels(pix []byte, x, y, width, height int) { func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
i.writePixels(pix, image.Rect(x, y, x+width, y+height)) i.writePixels(pix, region)
} }
func (i *Image) writePixels(pix []byte, region image.Rectangle) { func (i *Image) writePixels(pix []byte, region image.Rectangle) {
@ -585,7 +585,7 @@ func (i *Image) writePixels(pix []byte, region image.Rectangle) {
i.backend.restorable.WritePixels(pixb, r) 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, region image.Rectangle) error {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
@ -601,9 +601,7 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
} }
r := i.regionWithPadding() r := i.regionWithPadding()
x += r.Min.X return i.backend.restorable.ReadPixels(graphicsDriver, pixels, region.Add(r.Min))
y += r.Min.Y
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.

View File

@ -15,6 +15,7 @@
package atlas_test package atlas_test
import ( import (
"image"
"image/color" "image/color"
"runtime" "runtime"
"testing" "testing"
@ -63,17 +64,17 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
img1 := atlas.NewImage(bigSize, 100, atlas.ImageTypeRegular) img1 := atlas.NewImage(bigSize, 100, atlas.ImageTypeRegular)
defer img1.MarkDisposed() defer img1.MarkDisposed()
// Ensure img1's region is allocated. // Ensure img1's region is allocated.
img1.WritePixels(make([]byte, 4*bigSize*100), 0, 0, bigSize, 100) img1.WritePixels(make([]byte, 4*bigSize*100), image.Rect(0, 0, bigSize, 100))
img2 := atlas.NewImage(100, bigSize, atlas.ImageTypeRegular) img2 := atlas.NewImage(100, bigSize, atlas.ImageTypeRegular)
defer img2.MarkDisposed() defer img2.MarkDisposed()
img2.WritePixels(make([]byte, 4*100*bigSize), 0, 0, 100, bigSize) img2.WritePixels(make([]byte, 4*100*bigSize), image.Rect(0, 0, 100, bigSize))
const size = 32 const size = 32
img3 := atlas.NewImage(size/2, size/2, atlas.ImageTypeRegular) img3 := atlas.NewImage(size/2, size/2, atlas.ImageTypeRegular)
defer img3.MarkDisposed() defer img3.MarkDisposed()
img3.WritePixels(make([]byte, (size/2)*(size/2)*4), 0, 0, size/2, size/2) img3.WritePixels(make([]byte, (size/2)*(size/2)*4), image.Rect(0, 0, size/2, size/2))
img4 := atlas.NewImage(size, size, atlas.ImageTypeRegular) img4 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
defer img4.MarkDisposed() defer img4.MarkDisposed()
@ -90,7 +91,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
pix[4*(i+j*size)+3] = byte(i + j) pix[4*(i+j*size)+3] = byte(i + j)
} }
} }
img4.WritePixels(pix, 0, 0, size, size) img4.WritePixels(pix, image.Rect(0, 0, size, size))
const ( const (
dx0 = size / 4 dx0 = size / 4
@ -126,7 +127,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
} }
pix = make([]byte, 4*size*size) pix = make([]byte, 4*size*size)
if err := img4.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, size, size); err != nil { if err := img4.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(0, 0, size, size)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < size; j++ { for j := 0; j < size; j++ {
@ -158,11 +159,11 @@ func TestReputOnSourceBackend(t *testing.T) {
img0 := atlas.NewImage(size, size, atlas.ImageTypeRegular) img0 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
defer img0.MarkDisposed() defer img0.MarkDisposed()
img0.WritePixels(make([]byte, 4*size*size), 0, 0, size, size) img0.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size))
img1 := atlas.NewImage(size, size, atlas.ImageTypeRegular) img1 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
defer img1.MarkDisposed() defer img1.MarkDisposed()
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size) img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size))
if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { if got, want := img1.IsOnSourceBackendForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -178,12 +179,12 @@ func TestReputOnSourceBackend(t *testing.T) {
pix[4*(i+j*size)+3] = byte(i + j) pix[4*(i+j*size)+3] = byte(i + j)
} }
} }
img2.WritePixels(pix, 0, 0, size, size) img2.WritePixels(pix, image.Rect(0, 0, size, size))
// Create a volatile image. This should always be on a non-source backend. // Create a volatile image. This should always be on a non-source backend.
img3 := atlas.NewImage(size, size, atlas.ImageTypeVolatile) img3 := atlas.NewImage(size, size, atlas.ImageTypeVolatile)
defer img3.MarkDisposed() defer img3.MarkDisposed()
img3.WritePixels(make([]byte, 4*size*size), 0, 0, size, size) img3.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size))
if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { if got, want := img3.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -232,7 +233,7 @@ func TestReputOnSourceBackend(t *testing.T) {
} }
pix = make([]byte, 4*size*size) pix = make([]byte, 4*size*size)
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, size, size); err != nil { if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(0, 0, size, size)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < size; j++ { for j := 0; j < size; j++ {
@ -256,7 +257,7 @@ func TestReputOnSourceBackend(t *testing.T) {
} }
pix = make([]byte, 4*size*size) pix = make([]byte, 4*size*size)
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, size, size); err != nil { if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(0, 0, size, size)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < size; j++ { for j := 0; j < size; j++ {
@ -288,7 +289,7 @@ func TestReputOnSourceBackend(t *testing.T) {
if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil { if err := atlas.PutImagesOnSourceBackendForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
img1.WritePixels(make([]byte, 4*size*size), 0, 0, size, size) img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size))
vs := quadVertices(size, size, 0, 0, 1) vs := quadVertices(size, size, 0, 0, 1)
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
@ -333,7 +334,7 @@ func TestExtend(t *testing.T) {
p0[4*i+2] = byte(i) p0[4*i+2] = byte(i)
p0[4*i+3] = byte(i) p0[4*i+3] = byte(i)
} }
img0.WritePixels(p0, 0, 0, w0, h0) img0.WritePixels(p0, image.Rect(0, 0, w0, h0))
const w1, h1 = minSourceImageSizeForTesting + 1, 100 const w1, h1 = minSourceImageSizeForTesting + 1, 100
img1 := atlas.NewImage(w1, h1, atlas.ImageTypeRegular) img1 := atlas.NewImage(w1, h1, atlas.ImageTypeRegular)
@ -347,10 +348,10 @@ func TestExtend(t *testing.T) {
p1[4*i+3] = byte(i) p1[4*i+3] = byte(i)
} }
// Ensure to allocate // Ensure to allocate
img1.WritePixels(p1, 0, 0, w1, h1) img1.WritePixels(p1, image.Rect(0, 0, w1, h1))
pix0 := make([]byte, 4*w0*h0) pix0 := make([]byte, 4*w0*h0)
if err := img0.ReadPixels(ui.GraphicsDriverForTesting(), pix0, 0, 0, w0, h0); err != nil { if err := img0.ReadPixels(ui.GraphicsDriverForTesting(), pix0, image.Rect(0, 0, w0, h0)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < h0; j++ { for j := 0; j < h0; j++ {
@ -369,7 +370,7 @@ func TestExtend(t *testing.T) {
} }
pix1 := make([]byte, 4*w1*h1) pix1 := make([]byte, 4*w1*h1)
if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix1, 0, 0, w1, h1); err != nil { if err := img1.ReadPixels(ui.GraphicsDriverForTesting(), pix1, image.Rect(0, 0, w1, h1)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < h1; j++ { for j := 0; j < h1; j++ {
@ -402,7 +403,7 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) {
pix[4*i+2] = byte(i) pix[4*i+2] = byte(i)
pix[4*i+3] = byte(i) pix[4*i+3] = byte(i)
} }
src.WritePixels(pix, 0, 0, w, h) src.WritePixels(pix, image.Rect(0, 0, w, h))
vs := quadVertices(w, h, 0, 0, 1) vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
@ -413,10 +414,10 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) {
Height: h, Height: h,
} }
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
dst.WritePixels(pix, 0, 0, w, h) dst.WritePixels(pix, image.Rect(0, 0, w, h))
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)
} }
for j := 0; j < h; j++ { for j := 0; j < h; j++ {
@ -450,7 +451,7 @@ func TestSmallImages(t *testing.T) {
pix[4*i+2] = 0xff pix[4*i+2] = 0xff
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, 1) vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
@ -463,7 +464,7 @@ func TestSmallImages(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.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)
} }
for j := 0; j < h; j++ { for j := 0; j < h; j++ {
@ -497,7 +498,7 @@ func TestLongImages(t *testing.T) {
pix[4*i+2] = 0xff pix[4*i+2] = 0xff
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))
const scale = 120 const scale = 120
vs := quadVertices(w, h, 0, 0, scale) vs := quadVertices(w, h, 0, 0, scale)
@ -511,7 +512,7 @@ func TestLongImages(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false)
pix = make([]byte, 4*dstW*dstH) pix = make([]byte, 4*dstW*dstH)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, dstW, dstH); err != nil { if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(0, 0, dstW, dstH)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < h; j++ { for j := 0; j < h; j++ {
@ -547,12 +548,12 @@ func TestExtendWithBigImage(t *testing.T) {
img0 := atlas.NewImage(1, 1, atlas.ImageTypeRegular) img0 := atlas.NewImage(1, 1, atlas.ImageTypeRegular)
defer img0.MarkDisposed() defer img0.MarkDisposed()
img0.WritePixels(make([]byte, 4*1*1), 0, 0, 1, 1) img0.WritePixels(make([]byte, 4*1*1), image.Rect(0, 0, 1, 1))
img1 := atlas.NewImage(minSourceImageSizeForTesting+1, minSourceImageSizeForTesting+1, atlas.ImageTypeRegular) img1 := atlas.NewImage(minSourceImageSizeForTesting+1, minSourceImageSizeForTesting+1, atlas.ImageTypeRegular)
defer img1.MarkDisposed() defer img1.MarkDisposed()
img1.WritePixels(make([]byte, 4*(minSourceImageSizeForTesting+1)*(minSourceImageSizeForTesting+1)), 0, 0, minSourceImageSizeForTesting+1, minSourceImageSizeForTesting+1) img1.WritePixels(make([]byte, 4*(minSourceImageSizeForTesting+1)*(minSourceImageSizeForTesting+1)), image.Rect(0, 0, minSourceImageSizeForTesting+1, minSourceImageSizeForTesting+1))
} }
// Issue #1217 // Issue #1217
@ -565,7 +566,7 @@ func TestMaxImageSize(t *testing.T) {
s := maxImageSizeForTesting - 2*paddingSize s := maxImageSizeForTesting - 2*paddingSize
img1 := atlas.NewImage(s, s, atlas.ImageTypeRegular) img1 := atlas.NewImage(s, s, atlas.ImageTypeRegular)
defer img1.MarkDisposed() defer img1.MarkDisposed()
img1.WritePixels(make([]byte, 4*s*s), 0, 0, s, s) img1.WritePixels(make([]byte, 4*s*s), image.Rect(0, 0, s, s))
} }
// Issue #1217 (disabled) // Issue #1217 (disabled)
@ -578,7 +579,7 @@ func Disable_TestMinImageSize(t *testing.T) {
s := minSourceImageSizeForTesting s := minSourceImageSizeForTesting
img := atlas.NewImage(s, s, atlas.ImageTypeRegular) img := atlas.NewImage(s, s, atlas.ImageTypeRegular)
defer img.MarkDisposed() defer img.MarkDisposed()
img.WritePixels(make([]byte, 4*s*s), 0, 0, s, s) img.WritePixels(make([]byte, 4*s*s), image.Rect(0, 0, s, s))
} }
func TestMaxImageSizeJust(t *testing.T) { func TestMaxImageSizeJust(t *testing.T) {
@ -587,7 +588,7 @@ func TestMaxImageSizeJust(t *testing.T) {
// TODO: Should we allow such this size for ImageTypeRegular? // TODO: Should we allow such this size for ImageTypeRegular?
img := atlas.NewImage(s, s, atlas.ImageTypeUnmanaged) img := atlas.NewImage(s, s, atlas.ImageTypeUnmanaged)
defer img.MarkDisposed() defer img.MarkDisposed()
img.WritePixels(make([]byte, 4*s*s), 0, 0, s, s) img.WritePixels(make([]byte, 4*s*s), image.Rect(0, 0, s, s))
} }
func TestMaxImageSizeExceeded(t *testing.T) { func TestMaxImageSizeExceeded(t *testing.T) {
@ -602,7 +603,7 @@ func TestMaxImageSizeExceeded(t *testing.T) {
} }
}() }()
img.WritePixels(make([]byte, 4*(s+1)*s), 0, 0, s+1, s) img.WritePixels(make([]byte, 4*(s+1)*s), image.Rect(0, 0, s+1, s))
} }
// Issue #1421 // Issue #1421
@ -726,7 +727,7 @@ func TestImageWritePixelsModify(t *testing.T) {
pix[4*(i+j*size)+3] = byte(i + j) pix[4*(i+j*size)+3] = byte(i + j)
} }
} }
img.WritePixels(pix, 0, 0, size, size) img.WritePixels(pix, image.Rect(0, 0, size, size))
// Modify pix after WritePixels. // Modify pix after WritePixels.
for j := 0; j < size; j++ { for j := 0; j < size; j++ {
@ -740,7 +741,7 @@ func TestImageWritePixelsModify(t *testing.T) {
// Check the pixels are the original ones. // Check the pixels are the original ones.
pix = make([]byte, 4*size*size) pix = make([]byte, 4*size*size)
if err := img.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, size, size); err != nil { if err := img.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(0, 0, size, size)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < size; j++ { for j := 0; j < size; j++ {

View File

@ -15,6 +15,7 @@
package atlas_test package atlas_test
import ( import (
"image"
"image/color" "image/color"
"testing" "testing"
@ -48,7 +49,7 @@ func TestShaderFillTwice(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, s1, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, s1, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(g, pix, 0, 0, w, h); err != nil { if err := dst.ReadPixels(g, pix, image.Rect(0, 0, w, h)); err != nil {
t.Error(err) t.Error(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: 0xff}); got != want { 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: 0xff}); got != want {
@ -61,9 +62,9 @@ func TestImageDrawTwice(t *testing.T) {
dst := atlas.NewImage(w, h, atlas.ImageTypeRegular) dst := atlas.NewImage(w, h, atlas.ImageTypeRegular)
src0 := atlas.NewImage(w, h, atlas.ImageTypeRegular) src0 := atlas.NewImage(w, h, atlas.ImageTypeRegular)
src0.WritePixels([]byte{0xff, 0xff, 0xff, 0xff}, 0, 0, w, h) src0.WritePixels([]byte{0xff, 0xff, 0xff, 0xff}, image.Rect(0, 0, w, h))
src1 := atlas.NewImage(w, h, atlas.ImageTypeRegular) src1 := atlas.NewImage(w, h, atlas.ImageTypeRegular)
src1.WritePixels([]byte{0x80, 0x80, 0x80, 0xff}, 0, 0, w, h) src1.WritePixels([]byte{0x80, 0x80, 0x80, 0xff}, image.Rect(0, 0, w, h))
vs := quadVertices(w, h, 0, 0, 1) vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
@ -80,7 +81,7 @@ func TestImageDrawTwice(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.NearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, graphicsdriver.Region{}, [graphics.ShaderImageCount - 1][2]float32{}, atlas.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.Error(err) t.Error(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: 0xff}); got != want { 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: 0xff}); got != want {

View File

@ -16,6 +16,7 @@ package buffered
import ( import (
"fmt" "fmt"
"image"
"github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
@ -89,12 +90,12 @@ func (i *Image) markDisposedImpl() {
i.img.MarkDisposed() i.img.MarkDisposed()
} }
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 {
checkDelayedCommandsFlushed("ReadPixels") checkDelayedCommandsFlushed("ReadPixels")
// If restorable.AlwaysReadPixelsFromGPU() returns false, the pixel data is cached in the restorable package. // If restorable.AlwaysReadPixelsFromGPU() returns false, the pixel data is cached in the restorable package.
if !restorable.AlwaysReadPixelsFromGPU() { if !restorable.AlwaysReadPixelsFromGPU() {
if err := i.img.ReadPixels(graphicsDriver, pixels, x, y, width, height); err != nil { if err := i.img.ReadPixels(graphicsDriver, pixels, region); err != nil {
return err return err
} }
return nil return nil
@ -102,16 +103,16 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
if i.pixels == nil { if i.pixels == nil {
pix := make([]byte, 4*i.width*i.height) pix := make([]byte, 4*i.width*i.height)
if err := i.img.ReadPixels(graphicsDriver, pix, 0, 0, i.width, i.height); err != nil { if err := i.img.ReadPixels(graphicsDriver, pix, image.Rect(0, 0, i.width, i.height)); err != nil {
return err return err
} }
i.pixels = pix i.pixels = pix
} }
lineWidth := 4 * width lineWidth := 4 * region.Dx()
for j := 0; j < height; j++ { for j := 0; j < region.Dy(); j++ {
dstX := 4 * j * width dstX := 4 * j * region.Dx()
srcX := 4 * ((y+j)*i.width + x) srcX := 4 * ((region.Min.Y+j)*i.width + region.Min.X)
copy(pixels[dstX:dstX+lineWidth], i.pixels[srcX:srcX+lineWidth]) copy(pixels[dstX:dstX+lineWidth], i.pixels[srcX:srcX+lineWidth])
} }
return nil return nil
@ -123,8 +124,8 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name stri
} }
// WritePixels replaces the pixels at the specified region. // WritePixels replaces the pixels at the specified region.
func (i *Image) WritePixels(pix []byte, x, y, width, height int) { func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
if l := 4 * width * height; len(pix) != l { if l := 4 * region.Dx() * region.Dy(); len(pix) != l {
panic(fmt.Sprintf("buffered: len(pix) was %d but must be %d", len(pix), l)) panic(fmt.Sprintf("buffered: len(pix) was %d but must be %d", len(pix), l))
} }
@ -132,17 +133,17 @@ func (i *Image) WritePixels(pix []byte, x, y, width, height int) {
copied := make([]byte, len(pix)) copied := make([]byte, len(pix))
copy(copied, pix) copy(copied, pix)
if tryAddDelayedCommand(func() { if tryAddDelayedCommand(func() {
i.writePixelsImpl(copied, x, y, width, height) i.writePixelsImpl(copied, region)
}) { }) {
return return
} }
} }
i.writePixelsImpl(pix, x, y, width, height) i.writePixelsImpl(pix, region)
} }
func (i *Image) writePixelsImpl(pix []byte, x, y, width, height int) { func (i *Image) writePixelsImpl(pix []byte, region image.Rectangle) {
i.invalidatePixels() i.invalidatePixels()
i.img.WritePixels(pix, x, y, width, height) i.img.WritePixels(pix, region)
} }
// DrawTriangles draws the src image with the given vertices. // DrawTriangles draws the src image with the given vertices.

View File

@ -16,6 +16,7 @@ package mipmap
import ( import (
"fmt" "fmt"
"image"
"math" "math"
"github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/atlas"
@ -56,13 +57,13 @@ func (m *Mipmap) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name str
return m.orig.DumpScreenshot(graphicsDriver, name, blackbg) return m.orig.DumpScreenshot(graphicsDriver, name, blackbg)
} }
func (m *Mipmap) WritePixels(pix []byte, x, y, width, height int) { func (m *Mipmap) WritePixels(pix []byte, region image.Rectangle) {
m.orig.WritePixels(pix, x, y, width, height) m.orig.WritePixels(pix, region)
m.disposeMipmaps() m.disposeMipmaps()
} }
func (m *Mipmap) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, x, y, width, height int) error { func (m *Mipmap) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error {
return m.orig.ReadPixels(graphicsDriver, pixels, x, y, width, height) return m.orig.ReadPixels(graphicsDriver, pixels, region)
} }
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms []uint32, evenOdd bool, canSkipMipmap bool) { func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageCount - 1][2]float32, shader *Shader, uniforms []uint32, evenOdd bool, canSkipMipmap bool) {

View File

@ -16,6 +16,7 @@ package ui
import ( import (
"fmt" "fmt"
"image"
"math" "math"
"github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/atlas"
@ -40,7 +41,7 @@ type Image struct {
height int height int
imageType atlas.ImageType imageType atlas.ImageType
dotsBuffer map[[2]int][4]byte dotsBuffer map[image.Point][4]byte
// bigOffscreenBuffer is a double-sized offscreen for anti-alias rendering. // bigOffscreenBuffer is a double-sized offscreen for anti-alias rendering.
bigOffscreenBuffer *bigOffscreenImage bigOffscreenBuffer *bigOffscreenImage
@ -115,22 +116,22 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
i.mipmap.DrawTriangles(srcMipmaps, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader.shader, uniforms, evenOdd, canSkipMipmap) i.mipmap.DrawTriangles(srcMipmaps, vertices, indices, blend, dstRegion, srcRegion, subimageOffsets, shader.shader, uniforms, evenOdd, canSkipMipmap)
} }
func (i *Image) WritePixels(pix []byte, x, y, width, height int) { func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
if i.modifyCallback != nil { if i.modifyCallback != nil {
i.modifyCallback() i.modifyCallback()
} }
if width == 1 && height == 1 { if region.Dx() == 1 && region.Dy() == 1 {
// Flush the other buffer to make the buffers exclusive. // Flush the other buffer to make the buffers exclusive.
i.flushBigOffscreenBufferIfNeeded() i.flushBigOffscreenBufferIfNeeded()
if i.dotsBuffer == nil { if i.dotsBuffer == nil {
i.dotsBuffer = map[[2]int][4]byte{} i.dotsBuffer = map[image.Point][4]byte{}
} }
var clr [4]byte var clr [4]byte
copy(clr[:], pix) copy(clr[:], pix)
i.dotsBuffer[[2]int{x, y}] = clr i.dotsBuffer[region.Min] = clr
// One square requires 6 indices (= 2 triangles). // One square requires 6 indices (= 2 triangles).
if len(i.dotsBuffer) >= graphics.MaxVerticesCount/6 { if len(i.dotsBuffer) >= graphics.MaxVerticesCount/6 {
@ -140,10 +141,10 @@ func (i *Image) WritePixels(pix []byte, x, y, width, height int) {
} }
i.flushBufferIfNeeded() i.flushBufferIfNeeded()
i.mipmap.WritePixels(pix, x, y, width, height) i.mipmap.WritePixels(pix, region)
} }
func (i *Image) ReadPixels(pixels []byte, x, y, width, height int) { func (i *Image) ReadPixels(pixels []byte, region image.Rectangle) {
// Check the error existence and avoid unnecessary calls. // Check the error existence and avoid unnecessary calls.
if theGlobalState.error() != nil { if theGlobalState.error() != nil {
return return
@ -151,8 +152,8 @@ func (i *Image) ReadPixels(pixels []byte, x, y, width, height int) {
i.flushBigOffscreenBufferIfNeeded() i.flushBigOffscreenBufferIfNeeded()
if width == 1 && height == 1 { if region.Dx() == 1 && region.Dy() == 1 {
if c, ok := i.dotsBuffer[[2]int{x, y}]; ok { if c, ok := i.dotsBuffer[region.Min]; ok {
copy(pixels, c[:]) copy(pixels, c[:])
return return
} }
@ -162,7 +163,7 @@ func (i *Image) ReadPixels(pixels []byte, x, y, width, height int) {
i.flushDotsBufferIfNeeded() i.flushDotsBufferIfNeeded()
} }
if err := theUI.readPixels(i.mipmap, pixels, x, y, width, height); err != nil { if err := theUI.readPixels(i.mipmap, pixels, region); err != nil {
if panicOnErrorOnReadingPixels { if panicOnErrorOnReadingPixels {
panic(err) panic(err)
} }
@ -192,8 +193,8 @@ func (i *Image) flushDotsBufferIfNeeded() {
sx, sy := float32(1), float32(1) sx, sy := float32(1), float32(1)
var idx int var idx int
for p, c := range i.dotsBuffer { for p, c := range i.dotsBuffer {
dx := float32(p[0]) dx := float32(p.X)
dy := float32(p[1]) dy := float32(p.Y)
crf := float32(c[0]) / 0xff crf := float32(c[0]) / 0xff
cgf := float32(c[1]) / 0xff cgf := float32(c[1]) / 0xff
cbf := float32(c[2]) / 0xff cbf := float32(c[2]) / 0xff
@ -273,19 +274,19 @@ func init() {
pix[i] = 0xff pix[i] = 0xff
} }
// As whiteImage is used at Fill, use WritePixels instead. // As whiteImage is used at Fill, use WritePixels instead.
whiteImage.WritePixels(pix, 0, 0, whiteImage.width, whiteImage.height) whiteImage.WritePixels(pix, image.Rect(0, 0, whiteImage.width, whiteImage.height))
} }
func (i *Image) clear() { func (i *Image) clear() {
i.Fill(0, 0, 0, 0, 0, 0, i.width, i.height) i.Fill(0, 0, 0, 0, image.Rect(0, 0, i.width, i.height))
} }
func (i *Image) Fill(r, g, b, a float32, x, y, width, height int) { func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) {
dstRegion := graphicsdriver.Region{ dstRegion := graphicsdriver.Region{
X: float32(x), X: float32(region.Min.X),
Y: float32(y), Y: float32(region.Min.Y),
Width: float32(width), Width: float32(region.Dx()),
Height: float32(height), Height: float32(region.Dy()),
} }
if len(i.tmpVerticesForFill) < 4*graphics.VertexFloatCount { if len(i.tmpVerticesForFill) < 4*graphics.VertexFloatCount {

View File

@ -16,6 +16,7 @@ package ui
import ( import (
"errors" "errors"
"image"
"github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/mipmap" "github.com/hajimehoshi/ebiten/v2/internal/mipmap"
@ -72,8 +73,8 @@ func Get() *UserInterface {
return theUI return theUI
} }
func (u *UserInterface) readPixels(mipmap *mipmap.Mipmap, pixels []byte, x, y, width, height int) error { func (u *UserInterface) readPixels(mipmap *mipmap.Mipmap, pixels []byte, region image.Rectangle) error {
return mipmap.ReadPixels(u.graphicsDriver, pixels, x, y, width, height) return mipmap.ReadPixels(u.graphicsDriver, pixels, region)
} }
func (u *UserInterface) dumpScreenshot(mipmap *mipmap.Mipmap, name string, blackbg bool) (string, error) { func (u *UserInterface) dumpScreenshot(mipmap *mipmap.Mipmap, name string, blackbg bool) (string, error) {