graphics: Specify rect to glTexSubImage2D

This commit is contained in:
Hajime Hoshi 2018-03-01 00:27:55 +09:00
parent 9f6fd0db9a
commit 127f6c09c8
8 changed files with 81 additions and 34 deletions

View File

@ -283,12 +283,7 @@ func (i *Image) ReplacePixels(p []byte) error {
if l := 4 * w * h; len(p) != l {
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l))
}
// Copy the pixels so that this works even p is modified just after ReplacePixels.
pix := make([]byte, len(p))
copy(pix, p)
i.restorable.ReplacePixels(pix)
i.restorable.ReplacePixels(p, 0, 0, w, h)
return nil
}

View File

@ -269,6 +269,10 @@ func (c *drawImageCommand) quadsNum() int {
type replacePixelsCommand struct {
dst *Image
pixels []byte
x int
y int
width int
height int
}
// Exec executes the replacePixelsCommand.
@ -283,7 +287,7 @@ func (c *replacePixelsCommand) Exec(indexOffsetInBytes int) error {
// glTexSubImage2D didn't work without this hack at least on Nexus 5x and NuAns NEO [Reloaded] (#211).
opengl.GetContext().Flush()
opengl.GetContext().BindTexture(c.dst.texture.native)
opengl.GetContext().TexSubImage2D(c.pixels, c.dst.width, c.dst.height)
opengl.GetContext().TexSubImage2D(c.pixels, c.x, c.y, c.width, c.height)
return nil
}

View File

@ -86,12 +86,16 @@ func (i *Image) Pixels() ([]byte, error) {
return opengl.GetContext().FramebufferPixels(f.native, i.width, i.height)
}
func (i *Image) ReplacePixels(p []byte) {
func (i *Image) ReplacePixels(p []byte, x, y, width, height int) {
pixels := make([]byte, len(p))
copy(pixels, p)
c := &replacePixelsCommand{
dst: i,
pixels: pixels,
x: x,
y: y,
width: width,
height: height,
}
theCommandQueue.Enqueue(c)
}

View File

@ -165,8 +165,8 @@ func (c *Context) bindFramebufferImpl(f Framebuffer) {
})
}
func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8, error) {
var pixels []uint8
func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]byte, error) {
var pixels []byte
if err := c.runOnContextThread(func() error {
gl.Flush()
return nil
@ -175,7 +175,7 @@ func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8,
}
c.bindFramebuffer(f)
if err := c.runOnContextThread(func() error {
pixels = make([]uint8, 4*width*height)
pixels = make([]byte, 4*width*height)
gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels))
if e := gl.GetError(); e != gl.NO_ERROR {
pixels = nil
@ -218,9 +218,9 @@ func (c *Context) IsTexture(t Texture) bool {
return r
}
func (c *Context) TexSubImage2D(p []uint8, width, height int) {
func (c *Context) TexSubImage2D(p []byte, x, y, width, height int) {
_ = c.runOnContextThread(func() error {
gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(p))
gl.TexSubImage2D(gl.TEXTURE_2D, 0, int32(x), int32(y), int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(p))
return nil
})
}
@ -312,10 +312,10 @@ func (c *Context) NewShader(shaderType ShaderType, source string) (Shader, error
var v int32
gl.GetShaderiv(s, gl.COMPILE_STATUS, &v)
if v == gl.FALSE {
log := []uint8{}
log := []byte{}
gl.GetShaderiv(uint32(s), gl.INFO_LOG_LENGTH, &v)
if v != 0 {
log = make([]uint8, int(v))
log = make([]byte, int(v))
gl.GetShaderInfoLog(uint32(s), v, nil, (*uint8)(gl.Ptr(log)))
}
return fmt.Errorf("opengl: shader compile failed: %s", log)

View File

@ -143,8 +143,6 @@ func (c *Context) NewTexture(width, height int) (Texture, error) {
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
// TODO: Can we use glTexSubImage2D with linear filtering?
// void texImage2D(GLenum target, GLint level, GLenum internalformat,
// GLsizei width, GLsizei height, GLint border, GLenum format,
// GLenum type, ArrayBufferView? pixels);
@ -158,7 +156,7 @@ func (c *Context) bindFramebufferImpl(f Framebuffer) {
gl.BindFramebuffer(gl.FRAMEBUFFER, f.(*js.Object))
}
func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8, error) {
func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]byte, error) {
gl := c.gl
c.bindFramebuffer(f)
@ -168,7 +166,7 @@ func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8,
if e := gl.GetError(); e != gl.NO_ERROR {
return nil, errors.New(fmt.Sprintf("opengl: error: %d", e))
}
return pixels.Interface().([]uint8), nil
return pixels.Interface().([]byte), nil
}
func (c *Context) bindTextureImpl(t Texture) {
@ -193,12 +191,12 @@ func (c *Context) IsTexture(t Texture) bool {
return b
}
func (c *Context) TexSubImage2D(p []uint8, width, height int) {
func (c *Context) TexSubImage2D(p []byte, x, y, width, height int) {
gl := c.gl
// void texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
// GLsizei width, GLsizei height,
// GLenum format, GLenum type, ArrayBufferView? pixels);
gl.Call("texSubImage2D", gl.TEXTURE_2D, 0, 0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, p)
gl.Call("texSubImage2D", gl.TEXTURE_2D, 0, x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, p)
}
func (c *Context) NewFramebuffer(t Texture) (Framebuffer, error) {

View File

@ -144,13 +144,13 @@ func (c *Context) bindFramebufferImpl(f Framebuffer) {
gl.BindFramebuffer(mgl.FRAMEBUFFER, mgl.Framebuffer(f))
}
func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]uint8, error) {
func (c *Context) FramebufferPixels(f Framebuffer, width, height int) ([]byte, error) {
gl := c.gl
gl.Flush()
c.bindFramebuffer(f)
pixels := make([]uint8, 4*width*height)
pixels := make([]byte, 4*width*height)
gl.ReadPixels(pixels, 0, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE)
if e := gl.GetError(); e != mgl.NO_ERROR {
return nil, fmt.Errorf("opengl: glReadPixels: %d", e)
@ -179,9 +179,9 @@ func (c *Context) IsTexture(t Texture) bool {
return gl.IsTexture(mgl.Texture(t))
}
func (c *Context) TexSubImage2D(p []uint8, width, height int) {
func (c *Context) TexSubImage2D(p []byte, x, y, width, height int) {
gl := c.gl
gl.TexSubImage2D(mgl.TEXTURE_2D, 0, 0, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE, p)
gl.TexSubImage2D(mgl.TEXTURE_2D, 0, x, y, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE, p)
}
func (c *Context) NewFramebuffer(texture Texture) (Framebuffer, error) {
@ -351,9 +351,9 @@ func (c *Context) DisableVertexAttribArray(p Program, location string) {
gl.DisableVertexAttribArray(mgl.Attrib(l))
}
func uint16ToBytes(v []uint16) []uint8 {
func uint16ToBytes(v []uint16) []byte {
// TODO: Consider endian?
b := make([]uint8, len(v)*2)
b := make([]byte, len(v)*2)
for i, x := range v {
b[2*i] = uint8(x)
b[2*i+1] = uint8(x >> 8)

View File

@ -16,6 +16,7 @@ package restorable
import (
"errors"
"fmt"
"image/color"
"runtime"
@ -155,10 +156,27 @@ func (i *Image) ClearFramebuffer() {
}
// ReplacePixels replaces the image pixels with the given pixels slice.
func (i *Image) ReplacePixels(pixels []byte) {
func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
w, h := i.image.Size()
if width <= 0 || height <= 0 {
panic("restorable: width/height must be positive")
}
if x < 0 || y < 0 || w <= x || h <= y || x+width <= 0 || y+height <= 0 || w < x+width || h < y+height {
panic(fmt.Sprintf("restorable: out of range x: %d, y: %d, width: %d, height: %d", x, y, width, height))
}
theImages.makeStaleIfDependingOn(i)
i.image.ReplacePixels(pixels)
i.basePixels = pixels
i.image.ReplacePixels(pixels, x, y, width, height)
// Copy the pixels so that this works even p is modified just after ReplacePixels.
if i.basePixels == nil {
w, h := i.image.Size()
i.basePixels = make([]byte, 4*w*h)
}
idx := 4 * (y*w + x)
for j := 0; j < height; j++ {
copy(i.basePixels[idx:idx+4*width], pixels[4*j*width:4*(j+1)*width])
idx += 4 * w
}
i.drawImageHistory = nil
i.stale = false
}
@ -316,11 +334,11 @@ func (i *Image) restore() error {
}
gimg := graphics.NewImage(w, h)
if i.basePixels != nil {
gimg.ReplacePixels(i.basePixels)
gimg.ReplacePixels(i.basePixels, 0, 0, w, h)
} else {
// Clear the image explicitly.
pix := make([]uint8, w*h*4)
gimg.ReplacePixels(pix)
gimg.ReplacePixels(pix, 0, 0, w, h)
}
for _, c := range i.drawImageHistory {
// All dependencies must be already resolved.

View File

@ -75,7 +75,7 @@ func fill(img *Image, r, g, b, a uint8) {
pix[4*i+2] = b
pix[4*i+3] = a
}
img.ReplacePixels(pix)
img.ReplacePixels(pix, 0, 0, w, h)
}
func TestRestore(t *testing.T) {
@ -326,7 +326,7 @@ func TestRestoreComplexGraph(t *testing.T) {
func newImageFromImage(rgba *image.RGBA) *Image {
s := rgba.Bounds().Size()
img := NewImage(s.X, s.Y, false)
img.ReplacePixels(rgba.Pix)
img.ReplacePixels(rgba.Pix, 0, 0, s.X, s.Y)
return img
}
@ -381,4 +381,32 @@ func TestRestoreRecursive(t *testing.T) {
}
}
func TestReplacePixels(t *testing.T) {
const (
w = 17
h = 31
)
img := NewImage(17, 31, false)
pix := make([]byte, 4*4*4)
for i := range pix {
pix[i] = 0xff
}
img.ReplacePixels(pix, 5, 7, 4, 4)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
got, err := img.At(i, j)
if err != nil {
t.Fatal(err)
}
want := color.RGBA{}
if 5 <= i && i < 9 && 7 <= j && j < 11 {
want = color.RGBA{0xff, 0xff, 0xff, 0xff}
}
if got != want {
t.Errorf("img.At(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
}
// TODO: How about volatile/screen images?