diff --git a/internal/graphics/vertices.go b/internal/graphics/vertices.go index de0caef2f..03e9bf886 100644 --- a/internal/graphics/vertices.go +++ b/internal/graphics/vertices.go @@ -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 ( diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index ec5f85ae9..2171614b7 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -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) diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 2a2cc97fc..f23261db9 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -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 diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index aed385f36..a0d2c2a77 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -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 diff --git a/internal/shareable/shareable.go b/internal/shareable/shareable.go index 3a07504c9..9e1459f07 100644 --- a/internal/shareable/shareable.go +++ b/internal/shareable/shareable.go @@ -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) {