mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +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")
|
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.
|
// 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).
|
// Resolving them needs to access GPU and is expensive (#1137).
|
||||||
if i.hasFill {
|
if i.hasFill {
|
||||||
return i.fillColor.R, i.fillColor.G, i.fillColor.B, i.fillColor.A, nil
|
return i.fillColor.R, i.fillColor.G, i.fillColor.B, i.fillColor.A, nil
|
||||||
}
|
}
|
||||||
if i.pixels != nil {
|
|
||||||
idx := i.width*y + x
|
if i.pixels == nil {
|
||||||
return i.pixels[4*idx], i.pixels[4*idx+1], i.pixels[4*idx+2], i.pixels[4*idx+3], 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 {
|
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?
|
// TODO: Can we use (*restorable.Image).ReplacePixels?
|
||||||
if i.pixels == nil {
|
if i.pixels == nil {
|
||||||
pix := make([]byte, 4*i.width*i.height)
|
pix, err := i.img.Pixels(0, 0, i.width, i.height)
|
||||||
idx := 0
|
if err != nil {
|
||||||
img := i.img
|
return err
|
||||||
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++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
i.pixels = pix
|
i.pixels = pix
|
||||||
}
|
}
|
||||||
|
@ -92,8 +92,8 @@ func (m *Mipmap) ReplacePixels(pix []byte) {
|
|||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mipmap) At(x, y int) (r, g, b, a byte, err error) {
|
func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
|
||||||
return m.orig.At(x, y)
|
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) {
|
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)
|
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()
|
backendsM.Lock()
|
||||||
r, g, b, a, err := i.at(x, y)
|
defer backendsM.Unlock()
|
||||||
backendsM.Unlock()
|
|
||||||
return r, g, b, a, err
|
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) {
|
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)
|
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 j := 0; j < size; j++ {
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
r, g, b, a, err := img4.At(i, j)
|
r := pix[4*(size*j+i)]
|
||||||
if err != nil {
|
g := pix[4*(size*j+i)+1]
|
||||||
t.Fatal(err)
|
b := pix[4*(size*j+i)+2]
|
||||||
}
|
a := pix[4*(size*j+i)+3]
|
||||||
got := color.RGBA{r, g, b, a}
|
got := color.RGBA{r, g, b, a}
|
||||||
var want color.RGBA
|
var want color.RGBA
|
||||||
if i < dx0 || dx1 <= i || j < dy0 || dy1 <= j {
|
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}
|
want = color.RGBA{c, c, c, c}
|
||||||
}
|
}
|
||||||
if got != want {
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pix, err := img1.Pixels(0, 0, size, size)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
for j := 0; j < size; j++ {
|
for j := 0; j < size; j++ {
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
want := color.RGBA{byte(i + j), byte(i + j), byte(i + j), byte(i + j)}
|
want := color.RGBA{byte(i + j), byte(i + j), byte(i + j), byte(i + j)}
|
||||||
r, g, b, a, err := img1.At(i, j)
|
r := pix[4*(size*j+i)]
|
||||||
if err != nil {
|
g := pix[4*(size*j+i)+1]
|
||||||
t.Fatal(err)
|
b := pix[4*(size*j+i)+2]
|
||||||
}
|
a := pix[4*(size*j+i)+3]
|
||||||
got := color.RGBA{r, g, b, a}
|
got := color.RGBA{r, g, b, a}
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("got: %v, want: %v", 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)
|
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 j := 0; j < size; j++ {
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
want := color.RGBA{byte(i + j), byte(i + j), byte(i + j), byte(i + j)}
|
want := color.RGBA{byte(i + j), byte(i + j), byte(i + j), byte(i + j)}
|
||||||
r, g, b, a, err := img1.At(i, j)
|
r := pix[4*(size*j+i)]
|
||||||
if err != nil {
|
g := pix[4*(size*j+i)+1]
|
||||||
t.Fatal(err)
|
b := pix[4*(size*j+i)+2]
|
||||||
}
|
a := pix[4*(size*j+i)+3]
|
||||||
got := color.RGBA{r, g, b, a}
|
got := color.RGBA{r, g, b, a}
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
@ -247,32 +259,40 @@ func TestExtend(t *testing.T) {
|
|||||||
// Ensure to allocate
|
// Ensure to allocate
|
||||||
img1.ReplacePixels(p1)
|
img1.ReplacePixels(p1)
|
||||||
|
|
||||||
|
pix0, err := img0.Pixels(0, 0, w0, h0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
for j := 0; j < h0; j++ {
|
for j := 0; j < h0; j++ {
|
||||||
for i := 0; i < w0; i++ {
|
for i := 0; i < w0; i++ {
|
||||||
r, g, b, a, err := img0.At(i, j)
|
r := pix0[4*(w0*j+i)]
|
||||||
if err != nil {
|
g := pix0[4*(w0*j+i)+1]
|
||||||
t.Fatal(err)
|
b := pix0[4*(w0*j+i)+2]
|
||||||
}
|
a := pix0[4*(w0*j+i)+3]
|
||||||
got := color.RGBA{r, g, b, a}
|
got := color.RGBA{r, g, b, a}
|
||||||
c := byte(i + w0*j)
|
c := byte(i + w0*j)
|
||||||
want := color.RGBA{c, c, c, c}
|
want := color.RGBA{c, c, c, c}
|
||||||
if got != want {
|
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 j := 0; j < h1; j++ {
|
||||||
for i := 0; i < w1; i++ {
|
for i := 0; i < w1; i++ {
|
||||||
r, g, b, a, err := img1.At(i, j)
|
r := pix1[4*(w1*j+i)]
|
||||||
if err != nil {
|
g := pix1[4*(w1*j+i)+1]
|
||||||
t.Fatal(err)
|
b := pix1[4*(w1*j+i)+2]
|
||||||
}
|
a := pix1[4*(w1*j+i)+3]
|
||||||
got := color.RGBA{r, g, b, a}
|
got := color.RGBA{r, g, b, a}
|
||||||
c := byte(i + w1*j)
|
c := byte(i + w1*j)
|
||||||
want := color.RGBA{c, c, c, c}
|
want := color.RGBA{c, c, c, c}
|
||||||
if got != want {
|
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.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
|
||||||
dst.ReplacePixels(pix)
|
dst.ReplacePixels(pix)
|
||||||
|
|
||||||
|
pix, err := dst.Pixels(0, 0, w, h)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
for j := 0; j < h; j++ {
|
for j := 0; j < h; j++ {
|
||||||
for i := 0; i < w; i++ {
|
for i := 0; i < w; i++ {
|
||||||
r, g, b, a, err := dst.At(i, j)
|
r := pix[4*(w*j+i)]
|
||||||
if err != nil {
|
g := pix[4*(w*j+i)+1]
|
||||||
t.Fatal(err)
|
b := pix[4*(w*j+i)+2]
|
||||||
}
|
a := pix[4*(w*j+i)+3]
|
||||||
got := color.RGBA{r, g, b, a}
|
got := color.RGBA{r, g, b, a}
|
||||||
c := byte(i + w*j)
|
c := byte(i + w*j)
|
||||||
want := color.RGBA{c, c, c, c}
|
want := color.RGBA{c, c, c, c}
|
||||||
if got != want {
|
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()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)
|
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 j := 0; j < h; j++ {
|
||||||
for i := 0; i < w; i++ {
|
for i := 0; i < w; i++ {
|
||||||
r, _, _, a, err := dst.At(i, j)
|
r := pix[4*(w*j+i)]
|
||||||
if err != nil {
|
a := pix[4*(w*j+i)+3]
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if got, want := r, byte(0xff); got != want {
|
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 {
|
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
|
const w, h = 1, 6
|
||||||
src := NewImage(w, h, false)
|
src := NewImage(w, h, false)
|
||||||
defer src.MarkDisposed()
|
defer src.MarkDisposed()
|
||||||
dst := NewImage(256, 256, false)
|
|
||||||
|
const dstW, dstH = 256, 256
|
||||||
|
dst := NewImage(dstW, dstH, false)
|
||||||
defer dst.MarkDisposed()
|
defer dst.MarkDisposed()
|
||||||
|
|
||||||
pix := make([]byte, 4*w*h)
|
pix := make([]byte, 4*w*h)
|
||||||
@ -377,17 +405,19 @@ func TestLongImages(t *testing.T) {
|
|||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)
|
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 j := 0; j < h; j++ {
|
||||||
for i := 0; i < w*scale; i++ {
|
for i := 0; i < w*scale; i++ {
|
||||||
r, _, _, a, err := dst.At(i, j)
|
r := pix[4*(dstW*j+i)]
|
||||||
if err != nil {
|
a := pix[4*(dstW*j+i)+3]
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if got, want := r, byte(0xff); got != want {
|
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 {
|
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