From e78f34aa26bd9d4fa56da2afa7120830817df1bf Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 20 Mar 2022 01:24:47 +0900 Subject: [PATCH] internal/ui: add Image This is a preparation for a refactoring. Image will be a proxy to pass a graphics driver to the lower layer. --- gameforui.go | 4 +- image.go | 43 +++++++++---------- imagedumper_desktop.go | 2 +- internal/ui/image.go | 96 ++++++++++++++++++++++++++++++++++++++++++ shader.go | 6 +-- 5 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 internal/ui/image.go diff --git a/gameforui.go b/gameforui.go index ad32248cf..b39662511 100644 --- a/gameforui.go +++ b/gameforui.go @@ -63,7 +63,7 @@ func (c *gameForUI) Layout(outsideWidth, outsideHeight float64, deviceScaleFacto // The shader program for the screen is special and doesn't work well with an image on an atlas. // An image on an atlas is surrounded by a transparent edge, // and the shader program unexpectedly picks the pixel on the edges. - c.offscreen.mipmap.SetIndependent(true) + c.offscreen.image.SetIndependent(true) } return ow, oh @@ -74,7 +74,7 @@ func (c *gameForUI) Update() error { } func (c *gameForUI) Draw(screenScale float64, offsetX, offsetY float64, needsClearingScreen bool, framebufferYDirection graphicsdriver.YDirection, clearScreenEveryFrame, filterEnabled bool) error { - c.offscreen.mipmap.SetVolatile(clearScreenEveryFrame) + c.offscreen.image.SetVolatile(clearScreenEveryFrame) // Even though updateCount == 0, the offscreen is cleared and Draw is called. // Draw should not update the game state and then the screen should not be updated without Update, but diff --git a/image.go b/image.go index c66dc5a90..445ef64a5 100644 --- a/image.go +++ b/image.go @@ -22,7 +22,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/affine" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" - "github.com/hajimehoshi/ebiten/v2/internal/mipmap" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -38,7 +37,7 @@ type Image struct { // See strings.Builder for similar examples. addr *Image - mipmap *mipmap.Mipmap + image *ui.Image bounds image.Rectangle original *Image @@ -58,7 +57,7 @@ func (i *Image) Size() (width, height int) { } func (i *Image) isDisposed() bool { - return i.mipmap == nil + return i.image == nil } func (i *Image) isSubImage() bool { @@ -205,9 +204,9 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1) is := graphics.QuadIndices() - srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap} + srcs := [graphics.ShaderImageNum]*ui.Image{img.image} - i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, graphicsdriver.AddressUnsafe, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, canSkipMipmap(options.GeoM, filter)) + i.image.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, graphicsdriver.AddressUnsafe, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, canSkipMipmap(options.GeoM, filter)) } // Vertex represents a vertex passed to DrawTriangles. @@ -369,9 +368,9 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o is := make([]uint16, len(indices)) copy(is, indices) - srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap} + srcs := [graphics.ShaderImageNum]*ui.Image{img.image} - i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, options.FillRule == EvenOdd, false) + i.image.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, options.FillRule == EvenOdd, false) } // DrawTrianglesShaderOptions represents options for DrawTrianglesShader. @@ -469,7 +468,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader is := make([]uint16, len(indices)) copy(is, indices) - var imgs [graphics.ShaderImageNum]*mipmap.Mipmap + var imgs [graphics.ShaderImageNum]*ui.Image var imgw, imgh int for i, img := range options.Images { if img == nil { @@ -486,7 +485,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader panic("ebiten: all the source images must be the same size with the rectangle") } } - imgs[i] = img.mipmap + imgs[i] = img.image } var sx, sy float32 @@ -519,7 +518,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader us := shader.convertUniforms(options.Uniforms) - i.mipmap.DrawTriangles(imgs, vs, is, affine.ColorMIdentity{}, mode, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, options.FillRule == EvenOdd, false) + i.image.DrawTriangles(imgs, vs, is, affine.ColorMIdentity{}, mode, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, options.FillRule == EvenOdd, false) } // DrawRectShaderOptions represents options for DrawRectShader. @@ -584,7 +583,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR mode := graphicsdriver.CompositeMode(options.CompositeMode) - var imgs [graphics.ShaderImageNum]*mipmap.Mipmap + var imgs [graphics.ShaderImageNum]*ui.Image for i, img := range options.Images { if img == nil { continue @@ -595,7 +594,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR if w, h := img.Size(); width != w || height != h { panic("ebiten: all the source images must be the same size with the rectangle") } - imgs[i] = img.mipmap + imgs[i] = img.image } var sx, sy float32 @@ -631,7 +630,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR } us := shader.convertUniforms(options.Uniforms) - i.mipmap.DrawTriangles(imgs, vs, is, affine.ColorMIdentity{}, mode, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, false, canSkipMipmap(options.GeoM, graphicsdriver.FilterNearest)) + i.image.DrawTriangles(imgs, vs, is, affine.ColorMIdentity{}, mode, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, false, canSkipMipmap(options.GeoM, graphicsdriver.FilterNearest)) } // SubImage returns an image representing the portion of the image p visible through r. @@ -663,7 +662,7 @@ func (i *Image) SubImage(r image.Rectangle) image.Image { } img := &Image{ - mipmap: i.mipmap, + image: i.image, bounds: r, original: orig, } @@ -729,7 +728,7 @@ func (i *Image) at(x, y int) (r, g, b, a uint8) { if !image.Pt(x, y).In(i.Bounds()) { return 0, 0, 0, 0 } - pix, err := i.mipmap.Pixels(x, y, 1, 1) + pix, err := i.image.Pixels(x, y, 1, 1) if err != nil { if panicOnErrorAtImageAt { panic(err) @@ -765,7 +764,7 @@ func (i *Image) Set(x, y int, clr color.Color) { r, g, b, a := clr.RGBA() pix := []byte{byte(r >> 8), byte(g >> 8), byte(b >> 8), byte(a >> 8)} - if err := i.mipmap.ReplacePixels(pix, x, y, 1, 1); err != nil { + if err := i.image.ReplacePixels(pix, x, y, 1, 1); err != nil { ui.SetError(err) } } @@ -788,8 +787,8 @@ func (i *Image) Dispose() { if i.isSubImage() { return } - i.mipmap.MarkDisposed() - i.mipmap = nil + i.image.MarkDisposed() + i.image = nil } // ReplacePixels replaces the pixels of the image with p. @@ -817,7 +816,7 @@ func (i *Image) ReplacePixels(pixels []byte) { // Do not need to copy pixels here. // * In internal/mipmap, pixels are copied when necessary. // * In internal/shareable, pixels are copied to make its paddings. - if err := i.mipmap.ReplacePixels(pixels, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil { + if err := i.image.ReplacePixels(pixels, r.Min.X, r.Min.Y, r.Dx(), r.Dy()); err != nil { ui.SetError(err) } } @@ -842,7 +841,7 @@ func NewImage(width, height int) *Image { panic(fmt.Sprintf("ebiten: height at NewImage must be positive but %d", height)) } i := &Image{ - mipmap: mipmap.New(width, height), + image: ui.NewImage(width, height), bounds: image.Rect(0, 0, width, height), } i.addr = i @@ -873,7 +872,7 @@ func NewImageFromImage(source image.Image) *Image { } i := &Image{ - mipmap: mipmap.New(width, height), + image: ui.NewImage(width, height), bounds: image.Rect(0, 0, width, height), } i.addr = i @@ -884,7 +883,7 @@ func NewImageFromImage(source image.Image) *Image { func newScreenFramebufferImage(width, height int) *Image { i := &Image{ - mipmap: mipmap.NewScreenFramebufferMipmap(width, height), + image: ui.NewScreenFramebufferImage(width, height), bounds: image.Rect(0, 0, width, height), screen: true, } diff --git a/imagedumper_desktop.go b/imagedumper_desktop.go index 45fdc2417..207abc7c6 100644 --- a/imagedumper_desktop.go +++ b/imagedumper_desktop.go @@ -52,7 +52,7 @@ func takeScreenshot(screen *Image) error { } blackbg := !IsScreenTransparent() - if err := screen.mipmap.DumpScreenshot(newname, blackbg); err != nil { + if err := screen.image.DumpScreenshot(newname, blackbg); err != nil { return err } diff --git a/internal/ui/image.go b/internal/ui/image.go new file mode 100644 index 000000000..a203c3b97 --- /dev/null +++ b/internal/ui/image.go @@ -0,0 +1,96 @@ +// Copyright 2022 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 ui + +import ( + "github.com/hajimehoshi/ebiten/v2/internal/affine" + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" + "github.com/hajimehoshi/ebiten/v2/internal/mipmap" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" +) + +type Image struct { + mipmap *mipmap.Mipmap +} + +func NewImage(width, height int) *Image { + return &Image{ + mipmap: mipmap.New(width, height), + } +} + +func NewScreenFramebufferImage(width, height int) *Image { + return &Image{ + mipmap: mipmap.NewScreenFramebufferMipmap(width, height), + } +} + +func (i *Image) MarkDisposed() { + i.mipmap.MarkDisposed() + i.mipmap = nil +} + +func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm affine.ColorM, mode graphicsdriver.CompositeMode, filter graphicsdriver.Filter, address graphicsdriver.Address, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool) { + var srcMipmaps [graphics.ShaderImageNum]*mipmap.Mipmap + for i, src := range srcs { + if src == nil { + continue + } + srcMipmaps[i] = src.mipmap + } + + var s *mipmap.Shader + if shader != nil { + s = shader.shader + } + + i.mipmap.DrawTriangles(srcMipmaps, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms, evenOdd, canSkipMipmap) +} + +func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error { + return i.mipmap.ReplacePixels(pix, x, y, width, height) +} + +func (i *Image) Pixels(x, y, width, height int) ([]byte, error) { + return i.mipmap.Pixels(x, y, width, height) +} + +func (i *Image) DumpScreenshot(name string, blackbg bool) error { + return i.mipmap.DumpScreenshot(name, blackbg) +} + +func (i *Image) SetIndependent(independent bool) { + i.mipmap.SetIndependent(independent) +} + +func (i *Image) SetVolatile(volatile bool) { + i.mipmap.SetVolatile(volatile) +} + +type Shader struct { + shader *mipmap.Shader +} + +func NewShader(program *shaderir.Program) *Shader { + return &Shader{ + shader: mipmap.NewShader(program), + } +} + +func (s *Shader) MarkDisposed() { + s.shader.MarkDisposed() + s.shader = nil +} diff --git a/shader.go b/shader.go index b1f0c797e..2eef81b5e 100644 --- a/shader.go +++ b/shader.go @@ -23,9 +23,9 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" - "github.com/hajimehoshi/ebiten/v2/internal/mipmap" "github.com/hajimehoshi/ebiten/v2/internal/shader" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" + "github.com/hajimehoshi/ebiten/v2/internal/ui" ) var shaderSuffix string @@ -111,7 +111,7 @@ func imageSrc%[1]dAt(pos vec2) vec4 { // // For the details about the shader, see https://ebiten.org/documents/shader.html. type Shader struct { - shader *mipmap.Shader + shader *ui.Shader uniformNames []string uniformTypes []shaderir.Type } @@ -172,7 +172,7 @@ func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { } return &Shader{ - shader: mipmap.NewShader(s), + shader: ui.NewShader(s), uniformNames: s.UniformNames, uniformTypes: s.Uniforms, }, nil