graphics: Improve Set speed

On GopherJS, copying a struct is very heavy. This change avoids
copying (color) structs when possible.
This commit is contained in:
Hajime Hoshi 2019-01-14 04:25:39 +09:00
parent 6b110bf99e
commit 107dfe5074
6 changed files with 45 additions and 37 deletions

View File

@ -542,7 +542,8 @@ func (i *Image) At(x, y int) color.Color {
return color.RGBA{}
}
i.resolvePixelsToSet(true)
return i.mipmap.original().At(x, y)
r, g, b, a := i.mipmap.original().At(x, y)
return color.RGBA{r, g, b, a}
}
// Set sets the color at (x, y).
@ -570,11 +571,11 @@ func (img *Image) Set(x, y int, clr color.Color) {
idx := 0
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
c := img.mipmap.original().At(i, j)
pix[4*idx] = c.R
pix[4*idx+1] = c.G
pix[4*idx+2] = c.B
pix[4*idx+3] = c.A
r, g, b, a := img.mipmap.original().At(i, j)
pix[4*idx] = r
pix[4*idx+1] = g
pix[4*idx+2] = b
pix[4*idx+3] = a
idx++
}
}

View File

@ -17,7 +17,6 @@ package restorable
import (
"errors"
"fmt"
"image/color"
"github.com/hajimehoshi/ebiten/internal/affine"
"github.com/hajimehoshi/ebiten/internal/graphics"
@ -278,22 +277,21 @@ func (i *Image) readPixelsFromGPUIfNeeded() {
// At returns a color value at (x, y).
//
// Note that this must not be called until context is available.
func (i *Image) At(x, y int) color.RGBA {
func (i *Image) At(x, y int) (byte, byte, byte, byte) {
w, h := i.image.Size()
if x < 0 || y < 0 || w <= x || h <= y {
return color.RGBA{}
return 0, 0, 0, 0
}
i.readPixelsFromGPUIfNeeded()
// Even after readPixelsFromGPU, basePixels might be nil when OpenGL error happens.
if i.basePixels == nil {
return color.RGBA{}
return 0, 0, 0, 0
}
idx := 4*x + 4*y*w
r, g, b, a := i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3]
return color.RGBA{r, g, b, a}
return i.basePixels[idx], i.basePixels[idx+1], i.basePixels[idx+2], i.basePixels[idx+3]
}
// makeStaleIfDependingOn makes the image stale if the image depends on target.

View File

@ -85,11 +85,11 @@ func Images() []image.Image {
pix := make([]byte, 4*w*h)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
c := img.At(i, j)
pix[4*(i+j*w)] = byte(c.R)
pix[4*(i+j*w)+1] = byte(c.G)
pix[4*(i+j*w)+2] = byte(c.B)
pix[4*(i+j*w)+3] = byte(c.A)
r, g, b, a := img.At(i, j)
pix[4*(i+j*w)] = r
pix[4*(i+j*w)+1] = g
pix[4*(i+j*w)+2] = b
pix[4*(i+j*w)+3] = a
}
}
imgs = append(imgs, &image.RGBA{

View File

@ -458,7 +458,8 @@ func TestReplacePixels(t *testing.T) {
// Check the region (5, 7)-(9, 11). Outside state is indeterministic.
for j := 7; j < 11; j++ {
for i := 5; i < 9; i++ {
got := img.At(i, j)
r, g, b, a := img.At(i, j)
got := color.RGBA{r, g, b, a}
want := color.RGBA{0xff, 0xff, 0xff, 0xff}
if got != want {
t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
@ -471,7 +472,8 @@ func TestReplacePixels(t *testing.T) {
}
for j := 7; j < 11; j++ {
for i := 5; i < 9; i++ {
got := img.At(i, j)
r, g, b, a := img.At(i, j)
got := color.RGBA{r, g, b, a}
want := color.RGBA{0xff, 0xff, 0xff, 0xff}
if got != want {
t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
@ -500,7 +502,8 @@ func TestDrawImageAndReplacePixels(t *testing.T) {
if err := Restore(); err != nil {
t.Fatal(err)
}
got := img1.At(0, 0)
r, g, b, a := img1.At(0, 0)
got := color.RGBA{r, g, b, a}
want := color.RGBA{0xff, 0xff, 0xff, 0xff}
if !sameColors(got, want, 1) {
t.Errorf("got: %v, want: %v", got, want)
@ -533,7 +536,8 @@ func TestDispose(t *testing.T) {
if err := Restore(); err != nil {
t.Fatal(err)
}
got := img0.At(0, 0)
r, g, b, a := img0.At(0, 0)
got := color.RGBA{r, g, b, a}
want := color.RGBA{0xff, 0xff, 0xff, 0xff}
if !sameColors(got, want, 1) {
t.Errorf("got: %v, want: %v", got, want)

View File

@ -16,7 +16,6 @@ package shareable
import (
"image"
"image/color"
"runtime"
"sync"
@ -162,11 +161,11 @@ func (i *Image) forceShared() {
pixels := make([]byte, 4*i.width*i.height)
for y := 0; y < i.height; y++ {
for x := 0; x < i.width; x++ {
c := i.at(x, y)
pixels[4*(x+i.width*y)] = c.R
pixels[4*(x+i.width*y)+1] = c.G
pixels[4*(x+i.width*y)+2] = c.B
pixels[4*(x+i.width*y)+3] = c.A
r, g, b, a := i.at(x, y)
pixels[4*(x+i.width*y)] = r
pixels[4*(x+i.width*y)+1] = g
pixels[4*(x+i.width*y)+2] = b
pixels[4*(x+i.width*y)+3] = a
}
}
newI.replacePixels(pixels)
@ -315,20 +314,20 @@ func (i *Image) replacePixels(p []byte) {
i.backend.restorable.ReplacePixels(p, x, y, w, h)
}
func (i *Image) At(x, y int) color.RGBA {
func (i *Image) At(x, y int) (byte, byte, byte, byte) {
backendsM.Lock()
defer backendsM.Unlock()
return i.at(x, y)
}
func (i *Image) at(x, y int) color.RGBA {
func (i *Image) at(x, y int) (byte, byte, byte, byte) {
if i.backend == nil {
return color.RGBA{}
return 0, 0, 0, 0
}
ox, oy, w, h := i.region()
if x < 0 || y < 0 || x >= w || y >= h {
return color.RGBA{}
return 0, 0, 0, 0
}
return i.backend.restorable.At(x+ox, y+oy)

View File

@ -94,7 +94,8 @@ func TestEnsureNotShared(t *testing.T) {
for j := 0; j < size; j++ {
for i := 0; i < size; i++ {
got := img4.At(i, j)
r, g, b, a := img4.At(i, j)
got := color.RGBA{r, g, b, a}
var want color.RGBA
if i < dx0 || dx1 <= i || j < dy0 || dy1 <= j {
c := byte(i + j)
@ -168,7 +169,8 @@ func Disabled_TestReshared(t *testing.T) {
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)}
got := img1.At(i, j)
r, g, b, a := img1.At(i, j)
got := color.RGBA{r, g, b, a}
if got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -184,7 +186,8 @@ func Disabled_TestReshared(t *testing.T) {
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)}
got := img1.At(i, j)
r, g, b, a := img1.At(i, j)
got := color.RGBA{r, g, b, a}
if got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -231,7 +234,8 @@ func TestExtend(t *testing.T) {
for j := 0; j < h0; j++ {
for i := 0; i < w0; i++ {
got := img0.At(i, j)
r, g, b, a := img0.At(i, j)
got := color.RGBA{r, g, b, a}
c := byte(i + w0*j)
want := color.RGBA{c, c, c, c}
if got != want {
@ -242,7 +246,8 @@ func TestExtend(t *testing.T) {
for j := 0; j < h1; j++ {
for i := 0; i < w1; i++ {
got := img1.At(i, j)
r, g, b, a := img1.At(i, j)
got := color.RGBA{r, g, b, a}
c := byte(i + w1*j)
want := color.RGBA{c, c, c, c}
if got != want {
@ -278,7 +283,8 @@ func TestReplacePixelsAfterDrawImage(t *testing.T) {
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
got := dst.At(i, j)
r, g, b, a := dst.At(i, j)
got := color.RGBA{r, g, b, a}
c := byte(i + w*j)
want := color.RGBA{c, c, c, c}
if got != want {