diff --git a/image.go b/image.go index 7f51a3e34..146c1a13c 100644 --- a/image.go +++ b/image.go @@ -247,7 +247,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { colorm, cr, cg, cb, ca := colorMToScale(options.ColorM.affineColorM()) cr, cg, cb, ca = options.ColorScale.apply(cr, cg, cb, ca) vs := i.ensureTmpVertices(4 * graphics.VertexFloatCount) - graphics.QuadVertices(vs, float32(sx0), float32(sy0), float32(sx1), float32(sy1), a, b, c, d, tx, ty, cr, cg, cb, ca) + graphics.QuadVerticesFromSrcAndMatrix(vs, float32(sx0), float32(sy0), float32(sx1), float32(sy1), a, b, c, d, tx, ty, cr, cg, cb, ca) is := graphics.QuadIndices() srcs := [graphics.ShaderSrcImageCount]*ui.Image{img.image} @@ -829,7 +829,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR vs := i.ensureTmpVertices(4 * graphics.VertexFloatCount) // Do not use srcRegions[0].Dx() and srcRegions[0].Dy() as these might be empty. - graphics.QuadVertices(vs, + graphics.QuadVerticesFromSrcAndMatrix(vs, float32(srcRegions[0].Min.X), float32(srcRegions[0].Min.Y), float32(srcRegions[0].Min.X+width), float32(srcRegions[0].Min.Y+height), a, b, c, d, tx, ty, cr, cg, cb, ca) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index da6943789..0aed86376 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -43,16 +43,6 @@ func min(a, b int) int { return b } -// quadVertices returns vertices to render a quad. These values are passed to graphicscommand.Image. -func quadVertices(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32) []float32 { - return []float32{ - dx0, dy0, sx0, sy0, cr, cg, cb, ca, - dx1, dy0, sx1, sy0, cr, cg, cb, ca, - dx0, dy1, sx0, sy1, cr, cg, cb, ca, - dx1, dy1, sx1, sy1, cr, cg, cb, ca, - } -} - func appendDeferred(f func()) { deferredM.Lock() defer deferredM.Unlock() @@ -149,7 +139,8 @@ func (b *backend) extendIfNeeded(width, height int) { srcs := [graphics.ShaderSrcImageCount]*graphicscommand.Image{b.image} sw, sh := b.image.InternalSize() - vs := quadVertices(0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) + vs := make([]float32, 4*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) @@ -174,7 +165,8 @@ func newClearedImage(width, height int, screen bool) *graphicscommand.Image { } func clearImage(i *graphicscommand.Image, region image.Rectangle) { - vs := quadVertices(float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) + vs := make([]float32, 4*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) is := graphics.QuadIndices() i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) } @@ -353,7 +345,7 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) { w, h := float32(i.width), float32(i.height) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) @@ -384,7 +376,7 @@ func (i *Image) putOnSourceBackend() { w, h := float32(i.width), float32(i.height) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 51d829a38..3c5c351ab 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -50,12 +50,9 @@ func quadVertices(sw, sh, x, y int, scalex float32) []float32 { sy0 := float32(0) sx1 := float32(sw) sy1 := float32(sh) - return []float32{ - dx0, dy0, sx0, sy0, 1, 1, 1, 1, - dx1, dy0, sx1, sy0, 1, 1, 1, 1, - dx0, dy1, sx0, sy1, 1, 1, 1, 1, - dx1, dy1, sx1, sy1, 1, 1, 1, 1, - } + vs := make([]float32, 4*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, 1, 1, 1, 1) + return vs } const bigSize = 2049 diff --git a/internal/buffered/image_test.go b/internal/buffered/image_test.go index cb027129a..2dbd8cd71 100644 --- a/internal/buffered/image_test.go +++ b/internal/buffered/image_test.go @@ -52,7 +52,7 @@ func TestUnsyncedPixels(t *testing.T) { // Flush unsynced pixel cache. src := buffered.NewImage(16, 16, atlas.ImageTypeRegular) vs := make([]float32, 4*graphics.VertexFloatCount) - graphics.QuadVertices(vs, 0, 0, 16, 16, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, 16, 16, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, 16, 16) sr := [graphics.ShaderSrcImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)} diff --git a/internal/graphics/vertex.go b/internal/graphics/vertex.go index 680c25afa..95d7768b1 100644 --- a/internal/graphics/vertex.go +++ b/internal/graphics/vertex.go @@ -50,16 +50,16 @@ func QuadIndices() []uint32 { return quadIndices } -// QuadVertices sets a float32 slice for a quadrangle. -// QuadVertices sets a slice that never overlaps with other slices returned this function, -// and users can do optimization based on this fact. -func QuadVertices(dst []float32, sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) { +// QuadVerticesFromSrcAndMatrix sets a float32 slice for a quadrangle. +func QuadVerticesFromSrcAndMatrix(dst []float32, sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) { x := sx1 - sx0 y := sy1 - sy0 ax, by, cx, dy := a*x, b*y, c*x, d*y u0, v0, u1, v1 := sx0, sy0, sx1, sy1 // This function is very performance-sensitive and implement in a very dumb way. + + // Remove the boundary check. dst = dst[:4*VertexFloatCount] dst[0] = adjustDestinationPixel(tx) @@ -99,6 +99,53 @@ func QuadVertices(dst []float32, sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty dst[3*VertexFloatCount+7] = ca } +// QuadVerticesFromDstAndSrc sets a float32 slice for a quadrangle. +func QuadVerticesFromDstAndSrc(dst []float32, dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, cr, cg, cb, ca float32) { + dx0 = adjustDestinationPixel(dx0) + dy0 = adjustDestinationPixel(dy0) + dx1 = adjustDestinationPixel(dx1) + dy1 = adjustDestinationPixel(dy1) + + // Remove the boundary check. + dst = dst[:4*VertexFloatCount] + + dst[0] = dx0 + dst[1] = dy0 + dst[2] = sx0 + dst[3] = sy0 + dst[4] = cr + dst[5] = cg + dst[6] = cb + dst[7] = ca + + dst[VertexFloatCount] = dx1 + dst[VertexFloatCount+1] = dy0 + dst[VertexFloatCount+2] = sx1 + dst[VertexFloatCount+3] = sy0 + dst[VertexFloatCount+4] = cr + dst[VertexFloatCount+5] = cg + dst[VertexFloatCount+6] = cb + dst[VertexFloatCount+7] = ca + + dst[2*VertexFloatCount] = dx0 + dst[2*VertexFloatCount+1] = dy1 + dst[2*VertexFloatCount+2] = sx0 + dst[2*VertexFloatCount+3] = sy1 + dst[2*VertexFloatCount+4] = cr + dst[2*VertexFloatCount+5] = cg + dst[2*VertexFloatCount+6] = cb + dst[2*VertexFloatCount+7] = ca + + dst[3*VertexFloatCount] = dx1 + dst[3*VertexFloatCount+1] = dy1 + dst[3*VertexFloatCount+2] = sx1 + dst[3*VertexFloatCount+3] = sy1 + dst[3*VertexFloatCount+4] = cr + dst[3*VertexFloatCount+5] = cg + dst[3*VertexFloatCount+6] = cb + dst[3*VertexFloatCount+7] = ca +} + func adjustDestinationPixel(x float32) float32 { // Avoid the center of the pixel, which is problematic (#929, #1171). // Instead, align the vertices with about 1/3 pixels. diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index a60175edb..946d9c736 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -43,12 +43,9 @@ func TestMain(m *testing.M) { } func quadVertices(w, h float32) []float32 { - return []float32{ - 0, 0, 0, 0, 1, 1, 1, 1, - w, 0, w, 0, 1, 1, 1, 1, - 0, w, 0, h, 1, 1, 1, 1, - w, h, w, h, 1, 1, 1, 1, - } + vs := make([]float32, 8*graphics.VertexFloatCount) + graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1) + return vs } func TestClear(t *testing.T) { diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 2fe10cca7..2145f2e66 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -152,7 +152,7 @@ func (m *Mipmap) level(level int) *buffered.Image { switch { case level == 1: src = m.orig - graphics.QuadVertices(vs, 0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) case level > 1: src = m.level(level - 1) if src == nil { @@ -161,7 +161,7 @@ func (m *Mipmap) level(level int) *buffered.Image { } w := sizeForLevel(m.width, level-1) h := sizeForLevel(m.height, level-1) - graphics.QuadVertices(vs, 0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) + graphics.QuadVerticesFromSrcAndMatrix(vs, 0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) default: panic(fmt.Sprintf("mipmap: invalid level: %d", level)) } diff --git a/internal/ui/image.go b/internal/ui/image.go index c49d854cd..987d0bcc3 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -158,7 +158,7 @@ func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) { i.tmpVerticesForFill = make([]float32, 4*graphics.VertexFloatCount) } // i.tmpVerticesForFill can be reused as this is sent to DrawTriangles immediately. - graphics.QuadVertices( + graphics.QuadVerticesFromSrcAndMatrix( i.tmpVerticesForFill, 1, 1, float32(i.ui.whiteImage.width-1), float32(i.ui.whiteImage.height-1), float32(i.width), 0, 0, float32(i.height), 0, 0, @@ -235,7 +235,7 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Im i.tmpVerticesForCopying = make([]float32, 4*graphics.VertexFloatCount) } // i.tmpVerticesForCopying can be reused as this is sent to DrawTriangles immediately. - graphics.QuadVertices( + graphics.QuadVerticesFromSrcAndMatrix( i.tmpVerticesForCopying, float32(i.region.Min.X), float32(i.region.Min.Y), float32(i.region.Max.X), float32(i.region.Max.Y), bigOffscreenScale, 0, 0, bigOffscreenScale, 0, 0, @@ -279,7 +279,7 @@ func (i *bigOffscreenImage) flush() { i.tmpVerticesForFlushing = make([]float32, 4*graphics.VertexFloatCount) } // i.tmpVerticesForFlushing can be reused as this is sent to DrawTriangles in this function. - graphics.QuadVertices( + graphics.QuadVerticesFromSrcAndMatrix( i.tmpVerticesForFlushing, 0, 0, float32(i.region.Dx()*bigOffscreenScale), float32(i.region.Dy()*bigOffscreenScale), 1.0/bigOffscreenScale, 0, 0, 1.0/bigOffscreenScale, float32(i.region.Min.X), float32(i.region.Min.Y),