mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-02-03 22:44:28 +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
|
package ebiten
|
||||||
|
|
||||||
var (
|
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.
|
// When the image is disposed, ReplacePixels does nothing.
|
||||||
//
|
//
|
||||||
// ReplacePixels always returns nil as of 1.5.0.
|
// 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()
|
i.copyCheck()
|
||||||
|
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
@ -548,12 +548,10 @@ func (i *Image) ReplacePixels(pix []byte) error {
|
|||||||
}
|
}
|
||||||
r := i.Bounds()
|
r := i.Bounds()
|
||||||
|
|
||||||
// Copy the pixels as restorable package might reuse the pixels later.
|
// Do not need to copy pixels here.
|
||||||
// TODO: Would it be possible to avoid copying? (#1222)
|
// * In internal/buffered, pixels are copied when necessary.
|
||||||
copied := make([]byte, len(pix))
|
// * In internal/shareable, pixels are copied to make its paddings.
|
||||||
copy(copied, pix)
|
if err := i.buffered.ReplacePixels(pixels, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
|
||||||
|
|
||||||
if err := i.buffered.ReplacePixels(copied, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil {
|
|
||||||
theUIContext.setError(err)
|
theUIContext.setError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -637,10 +635,7 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
|||||||
}
|
}
|
||||||
i.addr = i
|
i.addr = i
|
||||||
|
|
||||||
// Call (*buffered.Image).ReplacePixels directly to avoid copying.
|
i.ReplacePixels(imageToBytes(source))
|
||||||
if err := i.buffered.ReplacePixels(copyImage(source), 0, 0, width, height); err != nil {
|
|
||||||
theUIContext.setError(err)
|
|
||||||
}
|
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,17 +20,20 @@ import (
|
|||||||
"image/draw"
|
"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.
|
// 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()
|
size := img.Bounds().Size()
|
||||||
w, h := size.X, size.Y
|
w, h := size.X, size.Y
|
||||||
bs := make([]byte, 4*w*h)
|
|
||||||
|
|
||||||
switch img := img.(type) {
|
switch img := img.(type) {
|
||||||
case *image.Paletted:
|
case *image.Paletted:
|
||||||
|
bs := make([]byte, 4*w*h)
|
||||||
|
|
||||||
b := img.Bounds()
|
b := img.Bounds()
|
||||||
x0 := b.Min.X
|
x0 := b.Min.X
|
||||||
y0 := b.Min.Y
|
y0 := b.Min.Y
|
||||||
@ -61,13 +64,27 @@ func copyImage(img image.Image) []byte {
|
|||||||
}
|
}
|
||||||
idx0 += d
|
idx0 += d
|
||||||
}
|
}
|
||||||
default:
|
return bs
|
||||||
dstImg := &image.RGBA{
|
case *image.RGBA:
|
||||||
Pix: bs,
|
if len(img.Pix) == 4*w*h {
|
||||||
Stride: 4 * w,
|
return img.Pix
|
||||||
Rect: image.Rect(0, 0, w, h),
|
|
||||||
}
|
}
|
||||||
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
|
return bs
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ import (
|
|||||||
. "github.com/hajimehoshi/ebiten"
|
. "github.com/hajimehoshi/ebiten"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCopyImage(t *testing.T) {
|
func TestImageToBytes(t *testing.T) {
|
||||||
pal := make(color.Palette, 256)
|
pal := make(color.Palette, 256)
|
||||||
for i := range pal {
|
for i := range pal {
|
||||||
pal[i] = color.White
|
pal[i] = color.White
|
||||||
@ -93,7 +93,7 @@ func TestCopyImage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
got := CopyImage(c.In)
|
got := ImageToBytes(c.In)
|
||||||
want := c.Out
|
want := c.Out
|
||||||
if !bytes.Equal(got, want) {
|
if !bytes.Equal(got, want) {
|
||||||
t.Errorf("Test %d: got: %v, want: %v", i, 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))
|
img := image.NewRGBA(image.Rect(0, 0, 4096, 4096))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
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))
|
img := image.NewNRGBA(image.Rect(0, 0, 4096, 4096))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
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)
|
img := image.NewPaletted(image.Rect(0, 0, 4096, 4096), palette.Plan9)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
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() {
|
if maybeCanAddDelayedCommand() {
|
||||||
|
copied := make([]byte, len(pix))
|
||||||
|
copy(copied, pix)
|
||||||
if tryAddDelayedCommand(func() error {
|
if tryAddDelayedCommand(func() error {
|
||||||
i.ReplacePixels(pix, x, y, width, height)
|
i.ReplacePixels(copied, x, y, width, height)
|
||||||
return nil
|
return nil
|
||||||
}) {
|
}) {
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user