diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 62069ff10..d96ff9d0b 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -605,11 +605,9 @@ type pixelsCommand struct { // Exec executes a pixelsCommand. func (c *pixelsCommand) Exec(indexOffset int) error { - p, err := c.img.image.Pixels() - if err != nil { + if err := c.img.image.ReadPixels(c.result); err != nil { return err } - c.result = p return nil } diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 01485779f..450c5674f 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -165,18 +165,19 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, dstRegion, srcRegion, shader, uniforms, evenOdd) } -// Pixels returns the image's pixels. -// Pixels might return nil when OpenGL error happens. -func (i *Image) Pixels() ([]byte, error) { +// ReadPixels reads the image's pixels. +// ReadPixels returns an error when an error happens in the graphics driver. +func (i *Image) ReadPixels(buf []byte) error { i.resolveBufferedReplacePixels() c := &pixelsCommand{ - img: i, + img: i, + result: buf, } theCommandQueue.Enqueue(c) if err := theCommandQueue.Flush(); err != nil { - return nil, err + return err } - return c.result, nil + return nil } func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { @@ -222,8 +223,9 @@ func (i *Image) Dump(path string, blackbg bool, rect image.Rectangle) error { } defer f.Close() - pix, err := i.Pixels() - if err != nil { + w, h := i.InternalSize() + pix := make([]byte, 4*w*h) + if err := i.ReadPixels(pix); err != nil { return err } diff --git a/internal/graphicsdriver/graphics.go b/internal/graphicsdriver/graphics.go index ef0364ba8..d5673f0f3 100644 --- a/internal/graphicsdriver/graphics.go +++ b/internal/graphicsdriver/graphics.go @@ -80,7 +80,7 @@ type Image interface { ID() ImageID Dispose() IsInvalidated() bool - Pixels() ([]byte, error) + ReadPixels(buf []byte) error ReplacePixels(args []*ReplacePixelsArgs) } diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 6a53eddd2..8acf297e0 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -1186,15 +1186,18 @@ func (i *Image) syncTexture() { cb.WaitUntilCompleted() } -func (i *Image) Pixels() ([]byte, error) { +func (i *Image) ReadPixels(buf []byte) error { + if got, want := len(buf), 4*i.width*i.height; got != want { + return fmt.Errorf("metal: len(buf) must be %d but %d at ReadPixels", want, got) + } + i.graphics.flushIfNeeded(false) i.syncTexture() - b := make([]byte, 4*i.width*i.height) - i.texture.GetBytes(&b[0], uintptr(4*i.width), mtl.Region{ + i.texture.GetBytes(&buf[0], uintptr(4*i.width), mtl.Region{ Size: mtl.Size{Width: i.width, Height: i.height, Depth: 1}, }, 0) - return b, nil + return nil } func (i *Image) ReplacePixels(args []*graphicsdriver.ReplacePixelsArgs) { diff --git a/internal/graphicsdriver/opengl/context_desktop.go b/internal/graphicsdriver/opengl/context_desktop.go index 974f02b83..c8f9c92f3 100644 --- a/internal/graphicsdriver/opengl/context_desktop.go +++ b/internal/graphicsdriver/opengl/context_desktop.go @@ -162,12 +162,10 @@ func (c *context) bindFramebufferImpl(f framebufferNative) { gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f)) } -func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { +func (c *context) framebufferPixels(buf []byte, f *framebuffer, width, height int) { gl.Flush() c.bindFramebuffer(f.native) - pixels := make([]byte, 4*width*height) - gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels)) - return pixels + gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(buf)) } func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) { diff --git a/internal/graphicsdriver/opengl/context_js.go b/internal/graphicsdriver/opengl/context_js.go index 2dfcbe95d..ee9ec59c5 100644 --- a/internal/graphicsdriver/opengl/context_js.go +++ b/internal/graphicsdriver/opengl/context_js.go @@ -236,7 +236,7 @@ func (c *context) bindFramebufferImpl(f framebufferNative) { gl.bindFramebuffer.Invoke(gles.FRAMEBUFFER, js.Value(f)) } -func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { +func (c *context) framebufferPixels(buf []byte, f *framebuffer, width, height int) { gl := c.gl c.bindFramebuffer(f.native) @@ -244,8 +244,7 @@ func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { l := 4 * width * height p := jsutil.TemporaryUint8ArrayFromUint8Slice(l, nil) gl.readPixels.Invoke(0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, p) - - return uint8ArrayToSlice(p, l) + copy(buf, uint8ArrayToSlice(p, l)) } func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) { diff --git a/internal/graphicsdriver/opengl/context_mobile.go b/internal/graphicsdriver/opengl/context_mobile.go index 7280dbcb8..0b88a6dc8 100644 --- a/internal/graphicsdriver/opengl/context_mobile.go +++ b/internal/graphicsdriver/opengl/context_mobile.go @@ -148,14 +148,12 @@ func (c *context) bindFramebufferImpl(f framebufferNative) { c.ctx.BindFramebuffer(gles.FRAMEBUFFER, uint32(f)) } -func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { +func (c *context) framebufferPixels(buf []byte, f *framebuffer, width, height int) { c.ctx.Flush() c.bindFramebuffer(f.native) - pixels := make([]byte, 4*width*height) - c.ctx.ReadPixels(pixels, 0, 0, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE) - return pixels + c.ctx.ReadPixels(buf, 0, 0, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE) } func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) { diff --git a/internal/graphicsdriver/opengl/image.go b/internal/graphicsdriver/opengl/image.go index 58b24e782..d6d46ed97 100644 --- a/internal/graphicsdriver/opengl/image.go +++ b/internal/graphicsdriver/opengl/image.go @@ -60,13 +60,13 @@ func (i *Image) setViewport() error { return nil } -func (i *Image) Pixels() ([]byte, error) { +func (i *Image) ReadPixels(buf []byte) error { if err := i.ensureFramebuffer(); err != nil { - return nil, err + return err } - p := i.graphics.context.framebufferPixels(i.framebuffer, i.width, i.height) - return p, nil + i.graphics.context.framebufferPixels(buf, i.framebuffer, i.width, i.height) + return nil } func (i *Image) framebufferSize() (int, int) { diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 1b9e6b41f..9cbed5b3e 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -497,8 +497,8 @@ func (i *Image) makeStaleIfDependingOnShader(shader *Shader) { // readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state. func (i *Image) readPixelsFromGPU() error { - pix, err := i.image.Pixels() - if err != nil { + pix := make([]byte, 4*i.width*i.height) + if err := i.image.ReadPixels(pix); err != nil { return err } i.basePixels = Pixels{} @@ -626,8 +626,8 @@ func (i *Image) restore() error { if len(i.drawTrianglesHistory) > 0 { i.basePixels = Pixels{} - pix, err := gimg.Pixels() - if err != nil { + pix := make([]byte, 4*w*h) + if err := gimg.ReadPixels(pix); err != nil { return err } i.basePixels.AddOrReplace(pix, 0, 0, w, h)