mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 10:48:53 +01:00
shareable: Replace At with Pixels
This change replaces the API At with Pixels to reduce mutex locks. Updates #1137
This commit is contained in:
parent
bbeb0d14e6
commit
3550abef7a
@ -142,16 +142,26 @@ func (i *Image) At(x, y int) (r, g, b, a byte, err error) {
|
||||
panic("buffered: the command queue is not available yet at At")
|
||||
}
|
||||
|
||||
if x < 0 || y < 0 || x >= i.width || y >= i.height {
|
||||
return 0, 0, 0, 0, nil
|
||||
}
|
||||
|
||||
// If there are pixels or pending fillling that needs to be resolved, use this rather than resolving.
|
||||
// Resolving them needs to access GPU and is expensive (#1137).
|
||||
if i.hasFill {
|
||||
return i.fillColor.R, i.fillColor.G, i.fillColor.B, i.fillColor.A, nil
|
||||
}
|
||||
if i.pixels != nil {
|
||||
idx := i.width*y + x
|
||||
return i.pixels[4*idx], i.pixels[4*idx+1], i.pixels[4*idx+2], i.pixels[4*idx+3], nil
|
||||
|
||||
if i.pixels == nil {
|
||||
pix, err := i.img.Pixels(0, 0, i.width, i.height)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
i.pixels = pix
|
||||
}
|
||||
return i.img.At(x, y)
|
||||
|
||||
idx := i.width*y + x
|
||||
return i.pixels[4*idx], i.pixels[4*idx+1], i.pixels[4*idx+2], i.pixels[4*idx+3], nil
|
||||
}
|
||||
|
||||
func (i *Image) Dump(name string, blackbg bool) error {
|
||||
@ -209,22 +219,9 @@ func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error {
|
||||
|
||||
// TODO: Can we use (*restorable.Image).ReplacePixels?
|
||||
if i.pixels == nil {
|
||||
pix := make([]byte, 4*i.width*i.height)
|
||||
idx := 0
|
||||
img := i.img
|
||||
sw, sh := i.width, i.height
|
||||
for j := 0; j < sh; j++ {
|
||||
for i := 0; i < sw; i++ {
|
||||
r, g, b, a, err := img.At(i, j)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pix[4*idx] = r
|
||||
pix[4*idx+1] = g
|
||||
pix[4*idx+2] = b
|
||||
pix[4*idx+3] = a
|
||||
idx++
|
||||
}
|
||||
pix, err := i.img.Pixels(0, 0, i.width, i.height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.pixels = pix
|
||||
}
|
||||
|
@ -92,8 +92,8 @@ func (m *Mipmap) ReplacePixels(pix []byte) {
|
||||
m.disposeMipmaps()
|
||||
}
|
||||
|
||||
func (m *Mipmap) At(x, y int) (r, g, b, a byte, err error) {
|
||||
return m.orig.At(x, y)
|
||||
func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
|
||||
return m.orig.Pixels(x, y, width, height)
|
||||
}
|
||||
|
||||
func (m *Mipmap) DrawImage(src *Mipmap, bounds image.Rectangle, geom GeoM, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter) {
|
||||
|
@ -376,11 +376,26 @@ func (i *Image) replacePixels(p []byte) {
|
||||
i.backend.restorable.ReplacePixels(p, x, y, w, h)
|
||||
}
|
||||
|
||||
func (i *Image) At(x, y int) (byte, byte, byte, byte, error) {
|
||||
func (img *Image) Pixels(x, y, width, height int) ([]byte, error) {
|
||||
backendsM.Lock()
|
||||
r, g, b, a, err := i.at(x, y)
|
||||
backendsM.Unlock()
|
||||
return r, g, b, a, err
|
||||
defer backendsM.Unlock()
|
||||
|
||||
bs := make([]byte, 4*width*height)
|
||||
idx := 0
|
||||
for j := y; j < y+height; j++ {
|
||||
for i := x; i < x+width; i++ {
|
||||
r, g, b, a, err := img.at(i, j)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bs[4*idx] = r
|
||||
bs[4*idx+1] = g
|
||||
bs[4*idx+2] = b
|
||||
bs[4*idx+3] = a
|
||||
idx++
|
||||
}
|
||||
}
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
func (i *Image) at(x, y int) (byte, byte, byte, byte, error) {
|
||||
|
@ -95,12 +95,16 @@ func TestEnsureNotShared(t *testing.T) {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
|
||||
pix, err := img4.Pixels(0, 0, size, size)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < size; j++ {
|
||||
for i := 0; i < size; i++ {
|
||||
r, g, b, a, err := img4.At(i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := pix[4*(size*j+i)]
|
||||
g := pix[4*(size*j+i)+1]
|
||||
b := pix[4*(size*j+i)+2]
|
||||
a := pix[4*(size*j+i)+3]
|
||||
got := color.RGBA{r, g, b, a}
|
||||
var want color.RGBA
|
||||
if i < dx0 || dx1 <= i || j < dy0 || dy1 <= j {
|
||||
@ -108,7 +112,7 @@ func TestEnsureNotShared(t *testing.T) {
|
||||
want = color.RGBA{c, c, c, c}
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("img4.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
t.Errorf("at(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,13 +178,17 @@ func TestReshared(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pix, err := img1.Pixels(0, 0, size, size)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < size; j++ {
|
||||
for i := 0; i < size; i++ {
|
||||
want := color.RGBA{byte(i + j), byte(i + j), byte(i + j), byte(i + j)}
|
||||
r, g, b, a, err := img1.At(i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := pix[4*(size*j+i)]
|
||||
g := pix[4*(size*j+i)+1]
|
||||
b := pix[4*(size*j+i)+2]
|
||||
a := pix[4*(size*j+i)+3]
|
||||
got := color.RGBA{r, g, b, a}
|
||||
if got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
@ -193,13 +201,17 @@ func TestReshared(t *testing.T) {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
|
||||
pix, err = img1.Pixels(0, 0, size, size)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < size; j++ {
|
||||
for i := 0; i < size; i++ {
|
||||
want := color.RGBA{byte(i + j), byte(i + j), byte(i + j), byte(i + j)}
|
||||
r, g, b, a, err := img1.At(i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := pix[4*(size*j+i)]
|
||||
g := pix[4*(size*j+i)+1]
|
||||
b := pix[4*(size*j+i)+2]
|
||||
a := pix[4*(size*j+i)+3]
|
||||
got := color.RGBA{r, g, b, a}
|
||||
if got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
@ -247,32 +259,40 @@ func TestExtend(t *testing.T) {
|
||||
// Ensure to allocate
|
||||
img1.ReplacePixels(p1)
|
||||
|
||||
pix0, err := img0.Pixels(0, 0, w0, h0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < h0; j++ {
|
||||
for i := 0; i < w0; i++ {
|
||||
r, g, b, a, err := img0.At(i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := pix0[4*(w0*j+i)]
|
||||
g := pix0[4*(w0*j+i)+1]
|
||||
b := pix0[4*(w0*j+i)+2]
|
||||
a := pix0[4*(w0*j+i)+3]
|
||||
got := color.RGBA{r, g, b, a}
|
||||
c := byte(i + w0*j)
|
||||
want := color.RGBA{c, c, c, c}
|
||||
if got != want {
|
||||
t.Errorf("img0.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
t.Errorf("at(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pix1, err := img1.Pixels(0, 0, w1, h1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < h1; j++ {
|
||||
for i := 0; i < w1; i++ {
|
||||
r, g, b, a, err := img1.At(i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := pix1[4*(w1*j+i)]
|
||||
g := pix1[4*(w1*j+i)+1]
|
||||
b := pix1[4*(w1*j+i)+2]
|
||||
a := pix1[4*(w1*j+i)+3]
|
||||
got := color.RGBA{r, g, b, a}
|
||||
c := byte(i + w1*j)
|
||||
want := color.RGBA{c, c, c, c}
|
||||
if got != want {
|
||||
t.Errorf("img1.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
t.Errorf("at(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -302,17 +322,21 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
|
||||
dst.ReplacePixels(pix)
|
||||
|
||||
pix, err := dst.Pixels(0, 0, w, h)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < h; j++ {
|
||||
for i := 0; i < w; i++ {
|
||||
r, g, b, a, err := dst.At(i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := pix[4*(w*j+i)]
|
||||
g := pix[4*(w*j+i)+1]
|
||||
b := pix[4*(w*j+i)+2]
|
||||
a := pix[4*(w*j+i)+3]
|
||||
got := color.RGBA{r, g, b, a}
|
||||
c := byte(i + w*j)
|
||||
want := color.RGBA{c, c, c, c}
|
||||
if got != want {
|
||||
t.Errorf("dst.At(%d, %d): got %v, want: %v", i, j, got, want)
|
||||
t.Errorf("at(%d, %d): got %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -339,17 +363,19 @@ func TestSmallImages(t *testing.T) {
|
||||
is := graphics.QuadIndices()
|
||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)
|
||||
|
||||
pix, err := dst.Pixels(0, 0, w, h)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < h; j++ {
|
||||
for i := 0; i < w; i++ {
|
||||
r, _, _, a, err := dst.At(i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := pix[4*(w*j+i)]
|
||||
a := pix[4*(w*j+i)+3]
|
||||
if got, want := r, byte(0xff); got != want {
|
||||
t.Errorf("At(%d, %d) red: got: %d, want: %d", i, j, got, want)
|
||||
t.Errorf("at(%d, %d) red: got: %d, want: %d", i, j, got, want)
|
||||
}
|
||||
if got, want := a, byte(0xff); got != want {
|
||||
t.Errorf("At(%d, %d) alpha: got: %d, want: %d", i, j, got, want)
|
||||
t.Errorf("at(%d, %d) alpha: got: %d, want: %d", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -360,7 +386,9 @@ func TestLongImages(t *testing.T) {
|
||||
const w, h = 1, 6
|
||||
src := NewImage(w, h, false)
|
||||
defer src.MarkDisposed()
|
||||
dst := NewImage(256, 256, false)
|
||||
|
||||
const dstW, dstH = 256, 256
|
||||
dst := NewImage(dstW, dstH, false)
|
||||
defer dst.MarkDisposed()
|
||||
|
||||
pix := make([]byte, 4*w*h)
|
||||
@ -377,17 +405,19 @@ func TestLongImages(t *testing.T) {
|
||||
is := graphics.QuadIndices()
|
||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)
|
||||
|
||||
pix, err := dst.Pixels(0, 0, dstW, dstH)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for j := 0; j < h; j++ {
|
||||
for i := 0; i < w*scale; i++ {
|
||||
r, _, _, a, err := dst.At(i, j)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := pix[4*(dstW*j+i)]
|
||||
a := pix[4*(dstW*j+i)+3]
|
||||
if got, want := r, byte(0xff); got != want {
|
||||
t.Errorf("At(%d, %d) red: got: %d, want: %d", i, j, got, want)
|
||||
t.Errorf("at(%d, %d) red: got: %d, want: %d", i, j, got, want)
|
||||
}
|
||||
if got, want := a, byte(0xff); got != want {
|
||||
t.Errorf("At(%d, %d) alpha: got: %d, want: %d", i, j, got, want)
|
||||
t.Errorf("at(%d, %d) alpha: got: %d, want: %d", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user