internal/graphicsdriver: refactoring: use image.Rectangle

This commit is contained in:
Hajime Hoshi 2023-04-28 00:00:51 +09:00
parent d2c991b774
commit e98acd3dc7
11 changed files with 92 additions and 117 deletions

View File

@ -16,6 +16,7 @@ package graphicscommand
import ( import (
"fmt" "fmt"
"image"
"math" "math"
"strings" "strings"
@ -427,15 +428,12 @@ func (c *writePixelsCommand) Exec(graphicsDriver graphicsdriver.Graphics, indexO
type readPixelsCommand struct { type readPixelsCommand struct {
result []byte result []byte
img *Image img *Image
x int region image.Rectangle
y int
width int
height int
} }
// Exec executes a readPixelsCommand. // Exec executes a readPixelsCommand.
func (c *readPixelsCommand) Exec(graphicsDriver graphicsdriver.Graphics, indexOffset int) error { func (c *readPixelsCommand) Exec(graphicsDriver graphicsdriver.Graphics, indexOffset int) error {
if err := c.img.image.ReadPixels(c.result, c.x, c.y, c.width, c.height); err != nil { if err := c.img.image.ReadPixels(c.result, c.region); err != nil {
return err return err
} }
return nil return nil

View File

@ -163,14 +163,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, offsets [g
// ReadPixels reads the image's pixels. // ReadPixels reads the image's pixels.
// ReadPixels returns an error when an error happens in the graphics driver. // ReadPixels returns an error when an error happens in the graphics driver.
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte, x, y, width, height int) error { func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte, region image.Rectangle) error {
i.flushBufferedWritePixels() i.flushBufferedWritePixels()
c := &readPixelsCommand{ c := &readPixelsCommand{
img: i, img: i,
x: x, region: region,
y: y,
width: width,
height: height,
result: buf, result: buf,
} }
theCommandQueue.Enqueue(c) theCommandQueue.Enqueue(c)
@ -180,13 +177,10 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte, x
return nil return nil
} }
func (i *Image) WritePixels(pixels []byte, x, y, width, height int) { func (i *Image) WritePixels(pixels []byte, region image.Rectangle) {
i.bufferedWP = append(i.bufferedWP, &graphicsdriver.WritePixelsArgs{ i.bufferedWP = append(i.bufferedWP, &graphicsdriver.WritePixelsArgs{
Pixels: pixels, Pixels: pixels,
X: x, Region: region,
Y: y,
Width: width,
Height: height,
}) })
addImageWithBuffer(i) addImageWithBuffer(i)
} }
@ -228,7 +222,7 @@ func (i *Image) dumpTo(w io.Writer, graphicsDriver graphicsdriver.Graphics, blac
} }
pix := make([]byte, 4*i.width*i.height) pix := make([]byte, 4*i.width*i.height)
if err := i.ReadPixels(graphicsDriver, pix, 0, 0, i.width, i.height); err != nil { if err := i.ReadPixels(graphicsDriver, pix, image.Rect(0, 0, i.width, i.height)); err != nil {
return err return err
} }

View File

@ -16,6 +16,7 @@ package graphicscommand_test
import ( import (
"fmt" "fmt"
"image"
"image/color" "image/color"
"testing" "testing"
@ -66,7 +67,7 @@ func TestClear(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, 0, 0, w, h); err != nil { if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < h/2; j++ { for j := 0; j < h/2; j++ {
@ -96,7 +97,7 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) {
} }
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendClear, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false)
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, nearestFilterShader, nil, false)
dst.WritePixels(make([]byte, 4), 0, 0, 1, 1) dst.WritePixels(make([]byte, 4), image.Rect(0, 0, 1, 1))
// TODO: Check the result. // TODO: Check the result.
} }
@ -120,7 +121,7 @@ func TestShader(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, s, nil, false) dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, [graphics.ShaderImageCount - 1][2]float32{}, vs, is, graphicsdriver.BlendSourceOver, dr, graphicsdriver.Region{}, s, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(g, pix, 0, 0, w, h); err != nil { if err := dst.ReadPixels(g, pix, image.Rect(0, 0, w, h)); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < h; j++ { for j := 0; j < h; j++ {

View File

@ -16,6 +16,7 @@ package directx
import ( import (
"fmt" "fmt"
"image"
"unsafe" "unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
@ -79,10 +80,10 @@ func (i *image11) IsInvalidated() bool {
return false return false
} }
func (i *image11) ReadPixels(buf []byte, x, y, width, height int) error { func (i *image11) ReadPixels(buf []byte, region image.Rectangle) error {
staging, err := i.graphics.device.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{ staging, err := i.graphics.device.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
Width: uint32(width), Width: uint32(region.Dx()),
Height: uint32(height), Height: uint32(region.Dy()),
MipLevels: 0, MipLevels: 0,
ArraySize: 1, ArraySize: 1,
Format: _DXGI_FORMAT_R8G8B8A8_UNORM, Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
@ -101,11 +102,11 @@ func (i *image11) ReadPixels(buf []byte, x, y, width, height int) error {
defer staging.Release() defer staging.Release()
i.graphics.deviceContext.CopySubresourceRegion(unsafe.Pointer(staging), 0, 0, 0, 0, unsafe.Pointer(i.texture), 0, &_D3D11_BOX{ i.graphics.deviceContext.CopySubresourceRegion(unsafe.Pointer(staging), 0, 0, 0, 0, unsafe.Pointer(i.texture), 0, &_D3D11_BOX{
left: uint32(x), left: uint32(region.Min.X),
top: uint32(y), top: uint32(region.Min.Y),
front: 0, front: 0,
right: uint32(x + width), right: uint32(region.Max.X),
bottom: uint32(y + height), bottom: uint32(region.Max.Y),
back: 1, back: 1,
}) })
@ -115,12 +116,12 @@ func (i *image11) ReadPixels(buf []byte, x, y, width, height int) error {
} }
stride := int(mapped.RowPitch) stride := int(mapped.RowPitch)
src := unsafe.Slice((*byte)(mapped.pData), stride*height) src := unsafe.Slice((*byte)(mapped.pData), stride*region.Dy())
if stride == 4*width { if stride == 4*region.Dx() {
copy(buf, src) copy(buf, src)
} else { } else {
for j := 0; j < height; j++ { for j := 0; j < region.Dy(); j++ {
copy(buf[j*4*width:(j+1)*4*width], src[j*stride:j*stride+4*width]) copy(buf[j*4*region.Dx():(j+1)*4*region.Dx()], src[j*stride:j*stride+4*region.Dx()])
} }
} }
@ -132,13 +133,13 @@ func (i *image11) ReadPixels(buf []byte, x, y, width, height int) error {
func (i *image11) WritePixels(args []*graphicsdriver.WritePixelsArgs) error { func (i *image11) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
for _, a := range args { for _, a := range args {
i.graphics.deviceContext.UpdateSubresource(unsafe.Pointer(i.texture), 0, &_D3D11_BOX{ i.graphics.deviceContext.UpdateSubresource(unsafe.Pointer(i.texture), 0, &_D3D11_BOX{
left: uint32(a.X), left: uint32(a.Region.Min.X),
top: uint32(a.Y), top: uint32(a.Region.Min.Y),
front: 0, front: 0,
right: uint32(a.X + a.Width), right: uint32(a.Region.Max.X),
bottom: uint32(a.Y + a.Height), bottom: uint32(a.Region.Max.Y),
back: 1, back: 1,
}, unsafe.Pointer(&a.Pixels[0]), uint32(4*a.Width), 0) }, unsafe.Pointer(&a.Pixels[0]), uint32(4*a.Region.Dx()), 0)
} }
return nil return nil
} }

View File

@ -17,6 +17,7 @@ package directx
import ( import (
"errors" "errors"
"fmt" "fmt"
"image"
"unsafe" "unsafe"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
@ -71,7 +72,7 @@ func (*image12) IsInvalidated() bool {
return false return false
} }
func (i *image12) ReadPixels(buf []byte, x, y, width, height int) error { func (i *image12) ReadPixels(buf []byte, region image.Rectangle) error {
if i.screen { if i.screen {
return errors.New("directx: Pixels cannot be called on the screen") return errors.New("directx: Pixels cannot be called on the screen")
} }
@ -83,8 +84,8 @@ func (i *image12) ReadPixels(buf []byte, x, y, width, height int) error {
desc := _D3D12_RESOURCE_DESC{ desc := _D3D12_RESOURCE_DESC{
Dimension: _D3D12_RESOURCE_DIMENSION_TEXTURE2D, Dimension: _D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Alignment: 0, Alignment: 0,
Width: uint64(width), Width: uint64(region.Dx()),
Height: uint32(height), Height: uint32(region.Dy()),
DepthOrArraySize: 1, DepthOrArraySize: 1,
MipLevels: 0, MipLevels: 0,
Format: _DXGI_FORMAT_R8G8B8A8_UNORM, Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
@ -126,11 +127,11 @@ func (i *image12) ReadPixels(buf []byte, x, y, width, height int) error {
i.graphics.needFlushCopyCommandList = true i.graphics.needFlushCopyCommandList = true
i.graphics.copyCommandList.CopyTextureRegion_PlacedFootPrint_SubresourceIndex( i.graphics.copyCommandList.CopyTextureRegion_PlacedFootPrint_SubresourceIndex(
&dst, 0, 0, 0, &src, &_D3D12_BOX{ &dst, 0, 0, 0, &src, &_D3D12_BOX{
left: uint32(x), left: uint32(region.Min.X),
top: uint32(y), top: uint32(region.Min.Y),
front: 0, front: 0,
right: uint32(x + width), right: uint32(region.Max.X),
bottom: uint32(y + height), bottom: uint32(region.Max.Y),
back: 1, back: 1,
}) })
@ -139,8 +140,8 @@ func (i *image12) ReadPixels(buf []byte, x, y, width, height int) error {
} }
dstBytes := unsafe.Slice((*byte)(unsafe.Pointer(m)), totalBytes) dstBytes := unsafe.Slice((*byte)(unsafe.Pointer(m)), totalBytes)
for j := 0; j < height; j++ { for j := 0; j < region.Dy(); j++ {
copy(buf[j*width*4:(j+1)*width*4], dstBytes[j*int(layouts.Footprint.RowPitch):]) copy(buf[j*region.Dx()*4:(j+1)*region.Dx()*4], dstBytes[j*int(layouts.Footprint.RowPitch):])
} }
readingStagingBuffer.Unmap(0, nil) readingStagingBuffer.Unmap(0, nil)
@ -157,30 +158,16 @@ func (i *image12) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
return err return err
} }
minX := i.width var region image.Rectangle
minY := i.height
maxX := 0
maxY := 0
for _, a := range args { for _, a := range args {
if minX > a.X { region = region.Union(a.Region)
minX = a.X
}
if minY > a.Y {
minY = a.Y
}
if maxX < a.X+a.Width {
maxX = a.X + a.Width
}
if maxY < a.Y+a.Height {
maxY = a.Y + a.Height
}
} }
desc := _D3D12_RESOURCE_DESC{ desc := _D3D12_RESOURCE_DESC{
Dimension: _D3D12_RESOURCE_DIMENSION_TEXTURE2D, Dimension: _D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Alignment: 0, Alignment: 0,
Width: uint64(maxX - minX), Width: uint64(region.Dx()),
Height: uint32(maxY - minY), Height: uint32(region.Dy()),
DepthOrArraySize: 1, DepthOrArraySize: 1,
MipLevels: 0, MipLevels: 0,
Format: _DXGI_FORMAT_R8G8B8A8_UNORM, Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
@ -211,8 +198,8 @@ func (i *image12) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
srcBytes := unsafe.Slice((*byte)(unsafe.Pointer(m)), totalBytes) srcBytes := unsafe.Slice((*byte)(unsafe.Pointer(m)), totalBytes)
for _, a := range args { for _, a := range args {
for j := 0; j < a.Height; j++ { for j := 0; j < a.Region.Dy(); j++ {
copy(srcBytes[((a.Y-minY)+j)*int(layouts.Footprint.RowPitch)+(a.X-minX)*4:], a.Pixels[j*a.Width*4:(j+1)*a.Width*4]) copy(srcBytes[((a.Region.Min.Y-region.Min.Y)+j)*int(layouts.Footprint.RowPitch)+(a.Region.Min.X-region.Min.X)*4:], a.Pixels[j*a.Region.Dx()*4:(j+1)*a.Region.Dx()*4])
} }
} }
@ -228,12 +215,12 @@ func (i *image12) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
PlacedFootprint: layouts, PlacedFootprint: layouts,
} }
i.graphics.copyCommandList.CopyTextureRegion_SubresourceIndex_PlacedFootPrint( i.graphics.copyCommandList.CopyTextureRegion_SubresourceIndex_PlacedFootPrint(
&dst, uint32(a.X), uint32(a.Y), 0, &src, &_D3D12_BOX{ &dst, uint32(a.Region.Min.X), uint32(a.Region.Min.Y), 0, &src, &_D3D12_BOX{
left: uint32(a.X - minX), left: uint32(a.Region.Min.X - region.Min.X),
top: uint32(a.Y - minY), top: uint32(a.Region.Min.Y - region.Min.Y),
front: 0, front: 0,
right: uint32(a.X - minX + a.Width), right: uint32(a.Region.Max.X - region.Min.X),
bottom: uint32(a.Y - minY + a.Height), bottom: uint32(a.Region.Max.Y - region.Min.Y),
back: 1, back: 1,
}) })
} }

View File

@ -15,6 +15,8 @@
package graphicsdriver package graphicsdriver
import ( import (
"image"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir"
) )
@ -65,7 +67,7 @@ type Image interface {
ID() ImageID ID() ImageID
Dispose() Dispose()
IsInvalidated() bool IsInvalidated() bool
ReadPixels(buf []byte, x, y, width, height int) error ReadPixels(buf []byte, region image.Rectangle) error
WritePixels(args []*WritePixelsArgs) error WritePixels(args []*WritePixelsArgs) error
} }
@ -73,10 +75,7 @@ type ImageID int
type WritePixelsArgs struct { type WritePixelsArgs struct {
Pixels []byte Pixels []byte
X int Region image.Rectangle
Y int
Width int
Height int
} }
type Shader interface { type Shader interface {

View File

@ -16,6 +16,7 @@ package metal
import ( import (
"fmt" "fmt"
"image"
"math" "math"
"runtime" "runtime"
"sort" "sort"
@ -815,17 +816,17 @@ func (i *Image) syncTexture() {
cb.WaitUntilCompleted() cb.WaitUntilCompleted()
} }
func (i *Image) ReadPixels(buf []byte, x, y, width, height int) error { func (i *Image) ReadPixels(buf []byte, region image.Rectangle) error {
if got, want := len(buf), 4*width*height; got != want { if got, want := len(buf), 4*region.Dx()*region.Dy(); got != want {
return fmt.Errorf("metal: len(buf) must be %d but %d at ReadPixels", want, got) return fmt.Errorf("metal: len(buf) must be %d but %d at ReadPixels", want, got)
} }
i.graphics.flushIfNeeded(false) i.graphics.flushIfNeeded(false)
i.syncTexture() i.syncTexture()
i.texture.GetBytes(&buf[0], uintptr(4*width), mtl.Region{ i.texture.GetBytes(&buf[0], uintptr(4*region.Dx()), mtl.Region{
Origin: mtl.Origin{X: x, Y: y}, Origin: mtl.Origin{X: region.Min.X, Y: region.Min.Y},
Size: mtl.Size{Width: width, Height: height, Depth: 1}, Size: mtl.Size{Width: region.Dx(), Height: region.Dy(), Depth: 1},
}, 0) }, 0)
return nil return nil
} }
@ -836,26 +837,10 @@ func (i *Image) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
g.flushRenderCommandEncoderIfNeeded() g.flushRenderCommandEncoderIfNeeded()
// Calculate the smallest texture size to include all the values in args. // Calculate the smallest texture size to include all the values in args.
minX := math.MaxInt32 var region image.Rectangle
minY := math.MaxInt32
maxX := 0
maxY := 0
for _, a := range args { for _, a := range args {
if minX > a.X { region = region.Union(a.Region)
minX = a.X
} }
if maxX < a.X+a.Width {
maxX = a.X + a.Width
}
if minY > a.Y {
minY = a.Y
}
if maxY < a.Y+a.Height {
maxY = a.Y + a.Height
}
}
w := maxX - minX
h := maxY - minY
// Use a temporary texture to send pixels asynchronously, whichever the memory is shared (e.g., iOS) or // Use a temporary texture to send pixels asynchronously, whichever the memory is shared (e.g., iOS) or
// managed (e.g., macOS). A temporary texture is needed since ReplaceRegion tries to sync the pixel // managed (e.g., macOS). A temporary texture is needed since ReplaceRegion tries to sync the pixel
@ -864,8 +849,8 @@ func (i *Image) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
td := mtl.TextureDescriptor{ td := mtl.TextureDescriptor{
TextureType: mtl.TextureType2D, TextureType: mtl.TextureType2D,
PixelFormat: mtl.PixelFormatRGBA8UNorm, PixelFormat: mtl.PixelFormatRGBA8UNorm,
Width: w, Width: region.Dx(),
Height: h, Height: region.Dy(),
StorageMode: storageMode, StorageMode: storageMode,
Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget, Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget,
} }
@ -874,9 +859,9 @@ func (i *Image) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
for _, a := range args { for _, a := range args {
t.ReplaceRegion(mtl.Region{ t.ReplaceRegion(mtl.Region{
Origin: mtl.Origin{X: a.X - minX, Y: a.Y - minY, Z: 0}, Origin: mtl.Origin{X: a.Region.Min.X - region.Min.X, Y: a.Region.Min.Y - region.Min.Y, Z: 0},
Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1}, Size: mtl.Size{Width: a.Region.Dx(), Height: a.Region.Dy(), Depth: 1},
}, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width) }, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Region.Dx())
} }
if g.cb == (mtl.CommandBuffer{}) { if g.cb == (mtl.CommandBuffer{}) {
@ -884,9 +869,9 @@ func (i *Image) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
} }
bce := g.cb.MakeBlitCommandEncoder() bce := g.cb.MakeBlitCommandEncoder()
for _, a := range args { for _, a := range args {
so := mtl.Origin{X: a.X - minX, Y: a.Y - minY, Z: 0} so := mtl.Origin{X: a.Region.Min.X - region.Min.X, Y: a.Region.Min.Y - region.Min.Y, Z: 0}
ss := mtl.Size{Width: a.Width, Height: a.Height, Depth: 1} ss := mtl.Size{Width: a.Region.Dx(), Height: a.Region.Dy(), Depth: 1}
do := mtl.Origin{X: a.X, Y: a.Y, Z: 0} do := mtl.Origin{X: a.Region.Min.X, Y: a.Region.Min.Y, Z: 0}
bce.CopyFromTexture(t, 0, 0, so, ss, i.texture, 0, 0, do) bce.CopyFromTexture(t, 0, 0, so, ss, i.texture, 0, 0, do)
} }
bce.EndEncoding() bce.EndEncoding()

View File

@ -17,6 +17,7 @@ package opengl
import ( import (
"errors" "errors"
"fmt" "fmt"
"image"
"sync" "sync"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
@ -242,14 +243,18 @@ func (c *context) newTexture(width, height int) (textureNative, error) {
return textureNative(t), nil return textureNative(t), nil
} }
func (c *context) framebufferPixels(buf []byte, f *framebuffer, x, y, width, height int) error { func (c *context) framebufferPixels(buf []byte, f *framebuffer, region image.Rectangle) error {
if got, want := len(buf), 4*width*height; got != want { if got, want := len(buf), 4*region.Dx()*region.Dy(); got != want {
return fmt.Errorf("opengl: len(buf) must be %d but was %d at framebufferPixels", got, want) return fmt.Errorf("opengl: len(buf) must be %d but was %d at framebufferPixels", got, want)
} }
c.ctx.Flush() c.ctx.Flush()
c.bindFramebuffer(f.native) c.bindFramebuffer(f.native)
c.ctx.ReadPixels(buf, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE) x := int32(region.Min.X)
y := int32(region.Min.Y)
width := int32(region.Dx())
height := int32(region.Dy())
c.ctx.ReadPixels(buf, x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE)
return nil return nil
} }

View File

@ -16,6 +16,7 @@ package opengl
import ( import (
"errors" "errors"
"image"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
@ -71,11 +72,11 @@ func (i *Image) setViewport() error {
return nil return nil
} }
func (i *Image) ReadPixels(buf []byte, x, y, width, height int) error { func (i *Image) ReadPixels(buf []byte, region image.Rectangle) error {
if err := i.ensureFramebuffer(); err != nil { if err := i.ensureFramebuffer(); err != nil {
return err return err
} }
if err := i.graphics.context.framebufferPixels(buf, i.framebuffer, x, y, width, height); err != nil { if err := i.graphics.context.framebufferPixels(buf, i.framebuffer, region); err != nil {
return err return err
} }
return nil return nil
@ -147,7 +148,11 @@ func (i *Image) WritePixels(args []*graphicsdriver.WritePixelsArgs) error {
i.graphics.context.bindTexture(i.texture) i.graphics.context.bindTexture(i.texture)
for _, a := range args { for _, a := range args {
i.graphics.context.ctx.TexSubImage2D(gl.TEXTURE_2D, 0, int32(a.X), int32(a.Y), int32(a.Width), int32(a.Height), gl.RGBA, gl.UNSIGNED_BYTE, a.Pixels) x := int32(a.Region.Min.X)
y := int32(a.Region.Min.Y)
width := int32(a.Region.Dx())
height := int32(a.Region.Dy())
i.graphics.context.ctx.TexSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, a.Pixels)
} }
return nil return nil

View File

@ -282,12 +282,12 @@ func (i *Image) WritePixels(pixels []byte, region image.Rectangle) {
theImages.makeStaleIfDependingOn(i) theImages.makeStaleIfDependingOn(i)
if pixels != nil { if pixels != nil {
i.image.WritePixels(pixels, region.Min.X, region.Min.Y, region.Dx(), region.Dy()) i.image.WritePixels(pixels, region)
} else { } else {
// TODO: When pixels == nil, we don't have to care the pixel state there. In such cases, the image // TODO: When pixels == nil, we don't have to care the pixel state there. In such cases, the image
// accepts only WritePixels and not Fill or DrawTriangles. // accepts only WritePixels and not Fill or DrawTriangles.
// TODO: Separate Image struct into two: images for WritePixels-only, and the others. // TODO: Separate Image struct into two: images for WritePixels-only, and the others.
i.image.WritePixels(make([]byte, 4*region.Dx()*region.Dy()), region.Min.X, region.Min.Y, region.Dx(), region.Dy()) i.image.WritePixels(make([]byte, 4*region.Dx()*region.Dy()), region)
} }
// Even if the image is already stale, call makeStale to extend the stale region. // Even if the image is already stale, call makeStale to extend the stale region.
@ -429,7 +429,7 @@ func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error { func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error {
if AlwaysReadPixelsFromGPU() { if AlwaysReadPixelsFromGPU() {
if err := i.image.ReadPixels(graphicsDriver, pixels, region.Min.X, region.Min.Y, region.Dx(), region.Dy()); err != nil { if err := i.image.ReadPixels(graphicsDriver, pixels, region); err != nil {
return err return err
} }
return nil return nil
@ -494,7 +494,7 @@ func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error
pix = make([]byte, 4*r.Dx()*r.Dy()) pix = make([]byte, 4*r.Dx()*r.Dy())
i.pixelsCache[r] = pix i.pixelsCache[r] = pix
} }
if err := i.image.ReadPixels(graphicsDriver, pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil { if err := i.image.ReadPixels(graphicsDriver, pix, r); err != nil {
return err return err
} }
i.basePixels.AddOrReplace(pix, r) i.basePixels.AddOrReplace(pix, r)
@ -633,7 +633,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
pix = make([]byte, 4*r.Dx()*r.Dy()) pix = make([]byte, 4*r.Dx()*r.Dy())
i.pixelsCache[r] = pix i.pixelsCache[r] = pix
} }
if err := gimg.ReadPixels(graphicsDriver, pix, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil { if err := gimg.ReadPixels(graphicsDriver, pix, r); err != nil {
return err return err
} }
i.basePixels.AddOrReplace(pix, r) i.basePixels.AddOrReplace(pix, r)

View File

@ -122,7 +122,7 @@ func (pr *pixelsRecords) readPixels(pixels []byte, region image.Rectangle, image
func (pr *pixelsRecords) apply(img *graphicscommand.Image) { func (pr *pixelsRecords) apply(img *graphicscommand.Image) {
// TODO: Isn't this too heavy? Can we merge the operations? // TODO: Isn't this too heavy? Can we merge the operations?
for _, r := range pr.records { for _, r := range pr.records {
img.WritePixels(r.pix, r.rect.Min.X, r.rect.Min.Y, r.rect.Dx(), r.rect.Dy()) img.WritePixels(r.pix, r.rect)
} }
} }