graphics: Refactoring: Unify PutVertex and PutQuadVertices implementation

This commit is contained in:
Hajime Hoshi 2019-06-21 11:09:48 +09:00
parent 8dc2301e54
commit 69ebc481e8
5 changed files with 55 additions and 120 deletions

View File

@ -15,7 +15,6 @@
package graphics
import (
"fmt"
"sync"
)
@ -62,105 +61,19 @@ func VertexSlice(n int) []float32 {
return theVerticesBackend.slice(n)
}
func PutQuadVertices(vs []float32, width, height int, sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) {
// width and height are the source image's size.
// For performance reason, graphics.InternalImageSize is not applied to width/height here.
if !isInternalImageSize(width) {
panic(fmt.Sprintf("graphics: width must be an internal image size at QuadVertices: %d", width))
}
if !isInternalImageSize(height) {
panic(fmt.Sprintf("graphics: height must be an internal image size at QuadVertices: %d", height))
}
if sx0 >= sx1 || sy0 >= sy1 {
// Do not modify vs. Here, it is assumed that vs is initialized with zero values.
return
}
if sx1 <= 0 || sy1 <= 0 {
// Do not modify vs. Here, it is assumed that vs is initialized with zero values.
return
}
wf := float32(width)
hf := float32(height)
u0, v0, u1, v1 := float32(sx0)/wf, float32(sy0)/hf, float32(sx1)/wf, float32(sy1)/hf
putQuadVerticesImpl(vs, wf, hf, float32(sx1-sx0), float32(sy1-sy0), u0, v0, u1, v1, a, b, c, d, tx, ty, cr, cg, cb, ca)
type VertexPutter interface {
PutVertex(dst []float32, dx, dy, sx, sy float32, bx0, by0, bx1, by1 float32, cr, cg, cb, ca float32)
}
const TexelAdjustmentFactor = 512.0
func putQuadVerticesImpl(vs []float32, sw, sh, x, y, u0, v0, u1, v1, a, b, c, d, tx, ty, cr, cg, cb, ca float32) {
// Specifying a range explicitly here is redundant but this helps optimization
// to eliminate boundary checks.
//
// 4*VertexFloatNum is better than 48 in terms of code maintenanceability, but in GopherJS, optimization
// might not work.
vs = vs[0:48]
func PutQuadVertices(dst []float32, putter VertexPutter, sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) {
x := float32(sx1 - sx0)
y := float32(sy1 - sy0)
ax, by, cx, dy := a*x, b*y, c*x, d*y
du := 1.0 / sw / TexelAdjustmentFactor
dv := 1.0 / sh / TexelAdjustmentFactor
// Vertex coordinates
vs[0] = tx
vs[1] = ty
// Texture coordinates: first 2 values indicates the actual coodinate, and
// the second indicates diagonally opposite coodinates.
// The second is needed to calculate source rectangle size in shader programs.
vs[2] = u0
vs[3] = v0
vs[4] = u0
vs[5] = v0
vs[6] = u1 - du
vs[7] = v1 - dv
vs[8] = cr
vs[9] = cg
vs[10] = cb
vs[11] = ca
// and the same for the other three coordinates
vs[12] = ax + tx
vs[13] = cx + ty
vs[14] = u1
vs[15] = v0
vs[16] = u0
vs[17] = v0
vs[18] = u1 - du
vs[19] = v1 - dv
vs[20] = cr
vs[21] = cg
vs[22] = cb
vs[23] = ca
vs[24] = by + tx
vs[25] = dy + ty
vs[26] = u0
vs[27] = v1
vs[28] = u0
vs[29] = v0
vs[30] = u1 - du
vs[31] = v1 - dv
vs[32] = cr
vs[33] = cg
vs[34] = cb
vs[35] = ca
vs[36] = ax + by + tx
vs[37] = cx + dy + ty
vs[38] = u1
vs[39] = v1
vs[40] = u0
vs[41] = v0
vs[42] = u1 - du
vs[43] = v1 - dv
vs[44] = cr
vs[45] = cg
vs[46] = cb
vs[47] = ca
u0, v0, u1, v1 := float32(sx0), float32(sy0), float32(sx1), float32(sy1)
putter.PutVertex(dst[:VertexFloatNum], tx, ty, u0, v0, u0, v0, u1, v1, cr, cg, cb, ca)
putter.PutVertex(dst[VertexFloatNum:2*VertexFloatNum], ax+tx, cx+ty, u1, v0, u0, v0, u1, v1, cr, cg, cb, ca)
putter.PutVertex(dst[2*VertexFloatNum:3*VertexFloatNum], by+tx, dy+ty, u0, v1, u0, v0, u1, v1, cr, cg, cb, ca)
putter.PutVertex(dst[3*VertexFloatNum:4*VertexFloatNum], ax+by+tx, cx+dy+ty, u1, v1, u0, v0, u1, v1, cr, cg, cb, ca)
}
var (

View File

@ -42,13 +42,35 @@ func TestMain(m *testing.M) {
os.Exit(code)
}
type testVertexPutter struct {
w float32
h float32
}
func (t *testVertexPutter) PutVertex(vs []float32, dx, dy, sx, sy float32, bx0, by0, bx1, by1 float32, cr, cg, cb, ca float32) {
// The implementation is basically same as restorable.(*Image).PutVertex.
// This doesn't adjust texels, but this is fine as long as the rectangle is not rotated or scaled.
vs[0] = dx
vs[1] = dy
vs[2] = sx / t.w
vs[3] = sy / t.h
vs[4] = bx0 / t.w
vs[5] = by0 / t.h
vs[6] = bx1 / t.w
vs[7] = by1 / t.h
vs[8] = cr
vs[9] = cg
vs[10] = cb
vs[11] = ca
}
func TestClear(t *testing.T) {
const w, h = 1024, 1024
src := NewImage(w/2, h/2)
dst := NewImage(w, h)
vs := graphics.VertexSlice(4)
graphics.PutQuadVertices(vs, w/2, h/2, 0, 0, w/2, h/2, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
graphics.PutQuadVertices(vs, &testVertexPutter{w / 2, h / 2}, 0, 0, w/2, h/2, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, graphics.CompositeModeClear, graphics.FilterNearest, graphics.AddressClampToZero)
@ -76,7 +98,7 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
src := NewImage(16, 16)
dst := NewImage(w, h)
vs := graphics.VertexSlice(4)
graphics.PutQuadVertices(vs, 16, 16, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
graphics.PutQuadVertices(vs, &testVertexPutter{w / 2, h / 2}, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
is := graphics.QuadIndices()
dst.DrawTriangles(clr, vs, is, nil, graphics.CompositeModeClear, graphics.FilterNearest, graphics.AddressClampToZero)
dst.DrawTriangles(src, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)

View File

@ -196,7 +196,7 @@ func (i *Image) fill(r, g, b, a uint8) {
dw, dh := i.internalSize()
sw, sh := emptyImage.Size()
vs := graphics.VertexSlice(4)
graphics.PutQuadVertices(vs, dw, dh, 0, 0, sw, sh,
graphics.PutQuadVertices(vs, i, 0, 0, sw, sh,
float32(dw)/float32(sw), 0, 0, float32(dh)/float32(sh), 0, 0,
rf, gf, bf, af)
is := graphics.QuadIndices()
@ -239,12 +239,16 @@ func (i *Image) internalSize() (int, int) {
return i.w2, i.h2
}
func (i *Image) PutQuadVertices(vs []float32, sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) {
w, h := i.internalSize()
graphics.PutQuadVertices(vs, w, h, sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, cr, cg, cb, ca)
}
func (i *Image) PutVertex(vs []float32, dx, dy, sx, sy float32, bx0, by0, bx1, by1 float32, cr, cg, cb, ca float32) {
const texelAdjustmentFactor = 512.0
// Specifying a range explicitly here is redundant but this helps optimization
// to eliminate boundary checks.
//
// VertexFloatNum is better than 12 in terms of code maintenanceability, but in GopherJS, optimization
// might not work.
vs = vs[0:12]
w, h := i.internalSize()
vs[0] = dx
vs[1] = dy
@ -252,8 +256,8 @@ func (i *Image) PutVertex(vs []float32, dx, dy, sx, sy float32, bx0, by0, bx1, b
vs[3] = sy / float32(h)
vs[4] = bx0 / float32(w)
vs[5] = by0 / float32(h)
vs[6] = bx1/float32(w) - 1.0/float32(w)/graphics.TexelAdjustmentFactor
vs[7] = by1/float32(h) - 1.0/float32(h)/graphics.TexelAdjustmentFactor
vs[6] = bx1/float32(w) - 1.0/float32(w)/texelAdjustmentFactor
vs[7] = by1/float32(h) - 1.0/float32(h)/texelAdjustmentFactor
vs[8] = cr
vs[9] = cg
vs[10] = cb

View File

@ -107,7 +107,7 @@ func TestRestoreWithoutDraw(t *testing.T) {
func quadVertices(src *Image, sw, sh, x, y int) []float32 {
vs := graphics.VertexSlice(4)
src.PutQuadVertices(vs, 0, 0, sw, sh,
graphics.PutQuadVertices(vs, src, 0, 0, sw, sh,
1, 0, 0, 1, float32(x), float32(y),
1, 1, 1, 1)
return vs

View File

@ -169,10 +169,10 @@ func (i *Image) ensureNotShared() {
return
}
x, y, w, h := i.region()
_, _, w, h := i.region()
newImg := restorable.NewImage(w, h)
vs := graphics.VertexSlice(4)
i.backend.restorable.PutQuadVertices(vs, x, y, x+w, y+h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
i.PutQuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
is := graphics.QuadIndices()
newImg.DrawTriangles(i.backend.restorable, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
@ -227,26 +227,22 @@ func (i *Image) Size() (width, height int) {
return i.width, i.height
}
// PutQuadVertices puts the given dest with vertices for rendering a quad.
//
// PutQuadVertices is highly optimized for rendering quads, and that's the most significant difference from
// PutVertices.
func (i *Image) PutQuadVertices(vs []float32, sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) {
// PutQuadVertices puts the given dst with vertices for rendering a quad.
func (i *Image) PutQuadVertices(dst []float32, sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) {
if i.backend == nil {
i.allocate(true)
}
ox, oy, _, _ := i.region()
i.backend.restorable.PutQuadVertices(vs, sx0+ox, sy0+oy, sx1+ox, sy1+oy, a, b, c, d, tx, ty, cr, cg, cb, ca)
graphics.PutQuadVertices(dst, i, sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, cr, cg, cb, ca)
}
// PutVertices puts the given dest with vertices that can be passed to DrawTriangles.
func (i *Image) PutVertex(dest []float32, dx, dy, sx, sy float32, bx0, by0, bx1, by1 float32, cr, cg, cb, ca float32) {
// PutVertices puts the given dst with vertices that can be passed to DrawTriangles.
func (i *Image) PutVertex(dst []float32, dx, dy, sx, sy float32, bx0, by0, bx1, by1 float32, cr, cg, cb, ca float32) {
if i.backend == nil {
i.allocate(true)
}
ox, oy, _, _ := i.region()
oxf, oyf := float32(ox), float32(oy)
i.backend.restorable.PutVertex(dest, dx, dy, sx+oxf, sy+oyf, bx0+oxf, by0+oyf, bx1+oxf, by1+oyf, cr, cg, cb, ca)
i.backend.restorable.PutVertex(dst, dx, dy, sx+oxf, sy+oyf, bx0+oxf, by0+oyf, bx1+oxf, by1+oyf, cr, cg, cb, ca)
}
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter, address graphics.Address) {