mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-24 18:02:02 +01:00
ebiten: Remove copying pixels from ReplacePixels and copyImage (renamed to imageToBytes)
This optimization utilizes the fact that copying happens in the 'shareable' package to add paddings. Updates #1222
This commit is contained in:
parent
f7757ae025
commit
02ef92f4cd
@ -15,5 +15,5 @@
|
||||
package ebiten
|
||||
|
||||
var (
|
||||
CopyImage = copyImage
|
||||
ImageToBytes = imageToBytes
|
||||
)
|
||||
|
17
image.go
17
image.go
@ -540,7 +540,7 @@ func (i *Image) Dispose() error {
|
||||
// When the image is disposed, ReplacePixels does nothing.
|
||||
//
|
||||
// ReplacePixels always returns nil as of 1.5.0.
|
||||
func (i *Image) ReplacePixels(pix []byte) error {
|
||||
func (i *Image) ReplacePixels(pixels []byte) error {
|
||||
i.copyCheck()
|
||||
|
||||
if i.isDisposed() {
|
||||
@ -548,12 +548,10 @@ func (i *Image) ReplacePixels(pix []byte) error {
|
||||
}
|
||||
r := i.Bounds()
|
||||
|
||||
// Copy the pixels as restorable package might reuse the pixels later.
|
||||
// TODO: Would it be possible to avoid copying? (#1222)
|
||||
copied := make([]byte, len(pix))
|
||||
copy(copied, pix)
|
||||
|
||||
if err := i.buffered.ReplacePixels(copied, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
|
||||
// Do not need to copy pixels here.
|
||||
// * In internal/buffered, pixels are copied when necessary.
|
||||
// * In internal/shareable, pixels are copied to make its paddings.
|
||||
if err := i.buffered.ReplacePixels(pixels, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
|
||||
theUIContext.setError(err)
|
||||
}
|
||||
return nil
|
||||
@ -637,10 +635,7 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
||||
}
|
||||
i.addr = i
|
||||
|
||||
// Call (*buffered.Image).ReplacePixels directly to avoid copying.
|
||||
if err := i.buffered.ReplacePixels(copyImage(source), 0, 0, width, height); err != nil {
|
||||
theUIContext.setError(err)
|
||||
}
|
||||
i.ReplacePixels(imageToBytes(source))
|
||||
return i, nil
|
||||
}
|
||||
|
||||
|
@ -20,17 +20,20 @@ import (
|
||||
"image/draw"
|
||||
)
|
||||
|
||||
// copyImage copies img to a new RGBA image.
|
||||
// imageToBytes gets RGBA bytes from img.
|
||||
//
|
||||
// Basically copyImage just calls draw.Draw.
|
||||
// Basically imageToBytes just calls draw.Draw.
|
||||
// If img is a paletted image, an optimized copying method is used.
|
||||
func copyImage(img image.Image) []byte {
|
||||
//
|
||||
// If img is *image.RGBA and its length is same as 4*width*height, imageToBytes returns its Pix.
|
||||
func imageToBytes(img image.Image) []byte {
|
||||
size := img.Bounds().Size()
|
||||
w, h := size.X, size.Y
|
||||
bs := make([]byte, 4*w*h)
|
||||
|
||||
switch img := img.(type) {
|
||||
case *image.Paletted:
|
||||
bs := make([]byte, 4*w*h)
|
||||
|
||||
b := img.Bounds()
|
||||
x0 := b.Min.X
|
||||
y0 := b.Min.Y
|
||||
@ -61,13 +64,27 @@ func copyImage(img image.Image) []byte {
|
||||
}
|
||||
idx0 += d
|
||||
}
|
||||
default:
|
||||
dstImg := &image.RGBA{
|
||||
Pix: bs,
|
||||
Stride: 4 * w,
|
||||
Rect: image.Rect(0, 0, w, h),
|
||||
return bs
|
||||
case *image.RGBA:
|
||||
if len(img.Pix) == 4*w*h {
|
||||
return img.Pix
|
||||
}
|
||||
draw.Draw(dstImg, image.Rect(0, 0, w, h), img, img.Bounds().Min, draw.Src)
|
||||
return imageToBytesSlow(img)
|
||||
default:
|
||||
return imageToBytesSlow(img)
|
||||
}
|
||||
}
|
||||
|
||||
func imageToBytesSlow(img image.Image) []byte {
|
||||
size := img.Bounds().Size()
|
||||
w, h := size.X, size.Y
|
||||
bs := make([]byte, 4*w*h)
|
||||
|
||||
dstImg := &image.RGBA{
|
||||
Pix: bs,
|
||||
Stride: 4 * w,
|
||||
Rect: image.Rect(0, 0, w, h),
|
||||
}
|
||||
draw.Draw(dstImg, image.Rect(0, 0, w, h), img, img.Bounds().Min, draw.Src)
|
||||
return bs
|
||||
}
|
@ -24,7 +24,7 @@ import (
|
||||
. "github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
func TestCopyImage(t *testing.T) {
|
||||
func TestImageToBytes(t *testing.T) {
|
||||
pal := make(color.Palette, 256)
|
||||
for i := range pal {
|
||||
pal[i] = color.White
|
||||
@ -93,7 +93,7 @@ func TestCopyImage(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
got := CopyImage(c.In)
|
||||
got := ImageToBytes(c.In)
|
||||
want := c.Out
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("Test %d: got: %v, want: %v", i, got, want)
|
||||
@ -101,26 +101,26 @@ func TestCopyImage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCopyImageRGBA(b *testing.B) {
|
||||
func BenchmarkImageToBytesRGBA(b *testing.B) {
|
||||
img := image.NewRGBA(image.Rect(0, 0, 4096, 4096))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
CopyImage(img)
|
||||
ImageToBytes(img)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCopyImageNRGBA(b *testing.B) {
|
||||
func BenchmarkImageToBytesNRGBA(b *testing.B) {
|
||||
img := image.NewNRGBA(image.Rect(0, 0, 4096, 4096))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
CopyImage(img)
|
||||
ImageToBytes(img)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCopyImagePaletted(b *testing.B) {
|
||||
func BenchmarkImageToBytesPaletted(b *testing.B) {
|
||||
img := image.NewPaletted(image.Rect(0, 0, 4096, 4096), palette.Plan9)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
CopyImage(img)
|
||||
ImageToBytes(img)
|
||||
}
|
||||
}
|
@ -192,8 +192,10 @@ func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error {
|
||||
}
|
||||
|
||||
if maybeCanAddDelayedCommand() {
|
||||
copied := make([]byte, len(pix))
|
||||
copy(copied, pix)
|
||||
if tryAddDelayedCommand(func() error {
|
||||
i.ReplacePixels(pix, x, y, width, height)
|
||||
i.ReplacePixels(copied, x, y, width, height)
|
||||
return nil
|
||||
}) {
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user