From f927e09f561654463ef762bf0975ed7b2a0f19d1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 16 Jul 2020 01:53:46 +0900 Subject: [PATCH] mipmap: Unify DrawImage and DrawTriangles Fixes #909 --- image.go | 9 ++- image_test.go | 6 +- internal/buffered/image.go | 33 --------- internal/{mipmap => graphics}/vertex.go | 24 ++++-- internal/graphics/vertices.go | 28 ------- internal/mipmap/mipmap.go | 97 +------------------------ 6 files changed, 34 insertions(+), 163 deletions(-) rename internal/{mipmap => graphics}/vertex.go (84%) delete mode 100644 internal/graphics/vertices.go diff --git a/image.go b/image.go index 54dfbc747..b9fb7244a 100644 --- a/image.go +++ b/image.go @@ -198,7 +198,14 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error { } a, b, c, d, tx, ty := geom.elements32() - i.buffered.DrawImage(img.buffered, img.Bounds(), a, b, c, d, tx, ty, options.ColorM.impl, mode, filter) + + sx0 := float32(bounds.Min.X) + sy0 := float32(bounds.Min.Y) + sx1 := float32(bounds.Max.X) + sy1 := float32(bounds.Max.Y) + vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1, filter == driver.FilterScreen) + is := graphics.QuadIndices() + i.buffered.DrawTriangles(img.buffered, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, driver.Region{}, nil, nil, nil) return nil } diff --git a/image_test.go b/image_test.go index d188134cc..7481bf249 100644 --- a/image_test.go +++ b/image_test.go @@ -978,7 +978,8 @@ func TestImageSprites(t *testing.T) { } } -func TestImageMipmap(t *testing.T) { +// Disabled: it does not make sense to expect deterministic mipmap results (#909). +func Disabled_TestImageMipmap(t *testing.T) { src, _, err := openEbitenImage() if err != nil { t.Fatal(err) @@ -1022,7 +1023,8 @@ func TestImageMipmap(t *testing.T) { } } -func TestImageMipmapNegativeDet(t *testing.T) { +// Disabled: it does not make sense to expect deterministic mipmap results (#909). +func Disabled_TestImageMipmapNegativeDet(t *testing.T) { src, _, err := openEbitenImage() if err != nil { t.Fatal(err) diff --git a/internal/buffered/image.go b/internal/buffered/image.go index 81b35b523..e6265c76c 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -232,39 +232,6 @@ func (i *Image) replacePendingPixels(pix []byte, x, y, width, height int) { i.needsToResolvePixels = true } -func (i *Image) DrawImage(src *Image, bounds image.Rectangle, a, b, c, d, tx, ty float32, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter) { - if i == src { - panic("buffered: Image.DrawImage: src must be different from the receiver") - } - - g := mipmap.GeoM{ - A: a, - B: b, - C: c, - D: d, - Tx: tx, - Ty: ty, - } - - if maybeCanAddDelayedCommand() { - if tryAddDelayedCommand(func() error { - i.drawImage(src, bounds, g, colorm, mode, filter) - return nil - }) { - return - } - } - - i.drawImage(src, bounds, g, colorm, mode, filter) -} - -func (i *Image) drawImage(src *Image, bounds image.Rectangle, g mipmap.GeoM, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter) { - src.resolvePendingPixels(true) - i.resolvePendingPixels(false) - i.img.DrawImage(src.img, bounds, g, colorm, mode, filter) - i.invalidatePendingPixels() -} - // DrawTriangles draws the src image with the given vertices. // // Copying vertices and indices is the caller's responsibility. diff --git a/internal/mipmap/vertex.go b/internal/graphics/vertex.go similarity index 84% rename from internal/mipmap/vertex.go rename to internal/graphics/vertex.go index de57e7ecf..98dda8e57 100644 --- a/internal/mipmap/vertex.go +++ b/internal/graphics/vertex.go @@ -12,16 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -package mipmap +package graphics import ( - "github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/web" ) +const ( + IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles. + VertexFloatNum = 8 +) + +var ( + quadIndices = []uint16{0, 1, 2, 1, 2, 3} +) + +func QuadIndices() []uint16 { + return quadIndices +} + var ( theVerticesBackend = &verticesBackend{ - backend: make([]float32, graphics.VertexFloatNum*1024), + backend: make([]float32, VertexFloatNum*1024), } ) @@ -33,7 +45,7 @@ type verticesBackend struct { func (v *verticesBackend) slice(n int, last bool) []float32 { // As this is called only from GopherJS, mutex is not required. - need := n * graphics.VertexFloatNum + need := n * VertexFloatNum if l := len(v.backend); v.head+need > l { for v.head+need > l { l *= 2 @@ -57,10 +69,10 @@ func vertexSlice(n int, last bool) []float32 { // In GopherJS, allocating memory by make is expensive. Use the backend instead. return theVerticesBackend.slice(n, last) } - return make([]float32, n*graphics.VertexFloatNum) + return make([]float32, n*VertexFloatNum) } -func quadVertices(sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32, last bool) []float32 { +func QuadVertices(sx0, sy0, sx1, sy1 float32, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32, last bool) []float32 { x := sx1 - sx0 y := sy1 - sy0 ax, by, cx, dy := a*x, b*y, c*x, d*y diff --git a/internal/graphics/vertices.go b/internal/graphics/vertices.go deleted file mode 100644 index 7e6a8254b..000000000 --- a/internal/graphics/vertices.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 The Ebiten Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package graphics - -const ( - IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles. - VertexFloatNum = 8 -) - -var ( - quadIndices = []uint16{0, 1, 2, 1, 2, 3} -) - -func QuadIndices() []uint16 { - return quadIndices -} diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index edfdb7e17..ef193858f 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -16,7 +16,6 @@ package mipmap import ( "fmt" - "image" "image/color" "math" @@ -41,19 +40,6 @@ func EndFrame() error { return shareable.EndFrame() } -type GeoM struct { - A float32 - B float32 - C float32 - D float32 - Tx float32 - Ty float32 -} - -func (g *GeoM) det() float32 { - return g.A*g.D - g.B*g.C -} - // Mipmap is a set of shareable.Image sorted by the order of mipmap level. // The level 0 image is a regular image and higher-level images are used for mipmap. type Mipmap struct { @@ -101,56 +87,6 @@ func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) { return m.orig.Pixels(x, y, width, height) } -func (m *Mipmap) DrawImage(src *Mipmap, bounds image.Rectangle, geom GeoM, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter) { - if det := geom.det(); det == 0 { - return - } else if math.IsNaN(float64(det)) { - return - } - - level := src.mipmapLevelFromGeoM(&geom, float32(bounds.Dx()), float32(bounds.Dy()), filter) - - cr, cg, cb, ca := float32(1), float32(1), float32(1), float32(1) - if colorm != nil && colorm.ScaleOnly() { - body, _ := colorm.UnsafeElements() - cr = body[0] - cg = body[5] - cb = body[10] - ca = body[15] - colorm = nil - } - - screen := filter == driver.FilterScreen - if screen && level != 0 { - panic("ebiten: Mipmap must not be used when the filter is FilterScreen") - } - - a, b, c, d, tx, ty := geom.A, geom.B, geom.C, geom.D, geom.Tx, geom.Ty - if level == 0 { - sx0 := float32(bounds.Min.X) - sy0 := float32(bounds.Min.Y) - sx1 := float32(bounds.Max.X) - sy1 := float32(bounds.Max.Y) - vs := quadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, cr, cg, cb, ca, screen) - is := graphics.QuadIndices() - m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, driver.AddressUnsafe, driver.Region{}, nil, nil, nil) - } else if buf := src.level(level); buf != nil { - s := pow2(level) - sx0 := float32(sizeForLevel(bounds.Min.X, level)) - sy0 := float32(sizeForLevel(bounds.Min.Y, level)) - sx1 := float32(sizeForLevel(bounds.Max.X, level)) - sy1 := float32(sizeForLevel(bounds.Max.Y, level)) - a *= s - b *= s - c *= s - d *= s - vs := quadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, cr, cg, cb, ca, false) - is := graphics.QuadIndices() - m.orig.DrawTriangles(buf, vs, is, colorm, mode, filter, driver.AddressUnsafe, driver.Region{}, nil, nil, nil) - } - m.disposeMipmaps() -} - func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, images []*Mipmap) { level := math.MaxInt32 for i := 0; i < len(indices)/3; i++ { @@ -245,7 +181,7 @@ func (m *Mipmap) level(level int) *shareable.Image { switch { case level == 1: src = m.orig - vs = quadVertices(0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false) + vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false) filter = driver.FilterLinear case level > 1: src = m.level(level - 1) @@ -255,11 +191,11 @@ func (m *Mipmap) level(level int) *shareable.Image { } w := sizeForLevel(m.width, level-1) h := sizeForLevel(m.height, level-1) - vs = quadVertices(0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false) + vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1, false) filter = driver.FilterLinear case level == -1: src = m.orig - vs = quadVertices(0, 0, float32(m.width), float32(m.height), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false) + vs = graphics.QuadVertices(0, 0, float32(m.width), float32(m.height), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false) filter = driver.FilterNearest case level < -1: src = m.level(level + 1) @@ -269,7 +205,7 @@ func (m *Mipmap) level(level int) *shareable.Image { } w := sizeForLevel(m.width, level-1) h := sizeForLevel(m.height, level-1) - vs = quadVertices(0, 0, float32(w), float32(h), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false) + vs = graphics.QuadVertices(0, 0, float32(w), float32(h), 2, 0, 0, 2, 0, 0, 1, 1, 1, 1, false) filter = driver.FilterNearest default: panic(fmt.Sprintf("ebiten: invalid level: %d", level)) @@ -320,31 +256,6 @@ func (m *Mipmap) disposeMipmaps() { } } -func (m *Mipmap) mipmapLevelFromGeoM(geom *GeoM, sw, sh float32, filter driver.Filter) int { - sx0 := float32(0) - sy0 := float32(0) - sx1 := sw - sy1 := float32(0) - sx2 := float32(0) - sy2 := sh - - a, b, c, d := geom.A, geom.B, geom.C, geom.D - dx0 := float32(0) - dy0 := float32(0) - dx1 := sx1*a + sy1*b - dy1 := sx1*c + sy1*d - dx2 := sx2*a + sy2*b - dy2 := sx2*c + sy2*d - - l0 := m.mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1, filter) - l1 := m.mipmapLevelFromDistance(dx0, dy0, dx2, dy2, sx0, sy0, sx2, sy2, filter) - - if l0 < l1 { - return l0 - } - return l1 -} - // mipmapLevel returns an appropriate mipmap level for the given distance. func (m *Mipmap) mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, filter driver.Filter) int { if filter == driver.FilterScreen {