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.
This commit is contained in:
Hajime Hoshi 2022-03-20 01:24:47 +09:00
parent 3e44a20b22
commit e78f34aa26
5 changed files with 123 additions and 28 deletions

View File

@ -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. // 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, // An image on an atlas is surrounded by a transparent edge,
// and the shader program unexpectedly picks the pixel on the edges. // and the shader program unexpectedly picks the pixel on the edges.
c.offscreen.mipmap.SetIndependent(true) c.offscreen.image.SetIndependent(true)
} }
return ow, oh 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 { 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. // 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 // Draw should not update the game state and then the screen should not be updated without Update, but

View File

@ -22,7 +22,6 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/affine" "github.com/hajimehoshi/ebiten/v2/internal/affine"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/mipmap"
"github.com/hajimehoshi/ebiten/v2/internal/ui" "github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
@ -38,7 +37,7 @@ type Image struct {
// See strings.Builder for similar examples. // See strings.Builder for similar examples.
addr *Image addr *Image
mipmap *mipmap.Mipmap image *ui.Image
bounds image.Rectangle bounds image.Rectangle
original *Image original *Image
@ -58,7 +57,7 @@ func (i *Image) Size() (width, height int) {
} }
func (i *Image) isDisposed() bool { func (i *Image) isDisposed() bool {
return i.mipmap == nil return i.image == nil
} }
func (i *Image) isSubImage() bool { 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) vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1)
is := graphics.QuadIndices() 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. // 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)) is := make([]uint16, len(indices))
copy(is, 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. // DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
@ -469,7 +468,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
is := make([]uint16, len(indices)) is := make([]uint16, len(indices))
copy(is, indices) copy(is, indices)
var imgs [graphics.ShaderImageNum]*mipmap.Mipmap var imgs [graphics.ShaderImageNum]*ui.Image
var imgw, imgh int var imgw, imgh int
for i, img := range options.Images { for i, img := range options.Images {
if img == nil { 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") 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 var sx, sy float32
@ -519,7 +518,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
us := shader.convertUniforms(options.Uniforms) 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. // 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) mode := graphicsdriver.CompositeMode(options.CompositeMode)
var imgs [graphics.ShaderImageNum]*mipmap.Mipmap var imgs [graphics.ShaderImageNum]*ui.Image
for i, img := range options.Images { for i, img := range options.Images {
if img == nil { if img == nil {
continue 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 { if w, h := img.Size(); width != w || height != h {
panic("ebiten: all the source images must be the same size with the rectangle") 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 var sx, sy float32
@ -631,7 +630,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
} }
us := shader.convertUniforms(options.Uniforms) 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. // 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{ img := &Image{
mipmap: i.mipmap, image: i.image,
bounds: r, bounds: r,
original: orig, 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()) { if !image.Pt(x, y).In(i.Bounds()) {
return 0, 0, 0, 0 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 err != nil {
if panicOnErrorAtImageAt { if panicOnErrorAtImageAt {
panic(err) panic(err)
@ -765,7 +764,7 @@ func (i *Image) Set(x, y int, clr color.Color) {
r, g, b, a := clr.RGBA() r, g, b, a := clr.RGBA()
pix := []byte{byte(r >> 8), byte(g >> 8), byte(b >> 8), byte(a >> 8)} 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) ui.SetError(err)
} }
} }
@ -788,8 +787,8 @@ func (i *Image) Dispose() {
if i.isSubImage() { if i.isSubImage() {
return return
} }
i.mipmap.MarkDisposed() i.image.MarkDisposed()
i.mipmap = nil i.image = nil
} }
// ReplacePixels replaces the pixels of the image with p. // 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. // Do not need to copy pixels here.
// * In internal/mipmap, pixels are copied when necessary. // * In internal/mipmap, pixels are copied when necessary.
// * In internal/shareable, pixels are copied to make its paddings. // * 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) 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)) panic(fmt.Sprintf("ebiten: height at NewImage must be positive but %d", height))
} }
i := &Image{ i := &Image{
mipmap: mipmap.New(width, height), image: ui.NewImage(width, height),
bounds: image.Rect(0, 0, width, height), bounds: image.Rect(0, 0, width, height),
} }
i.addr = i i.addr = i
@ -873,7 +872,7 @@ func NewImageFromImage(source image.Image) *Image {
} }
i := &Image{ i := &Image{
mipmap: mipmap.New(width, height), image: ui.NewImage(width, height),
bounds: image.Rect(0, 0, width, height), bounds: image.Rect(0, 0, width, height),
} }
i.addr = i i.addr = i
@ -884,7 +883,7 @@ func NewImageFromImage(source image.Image) *Image {
func newScreenFramebufferImage(width, height int) *Image { func newScreenFramebufferImage(width, height int) *Image {
i := &Image{ i := &Image{
mipmap: mipmap.NewScreenFramebufferMipmap(width, height), image: ui.NewScreenFramebufferImage(width, height),
bounds: image.Rect(0, 0, width, height), bounds: image.Rect(0, 0, width, height),
screen: true, screen: true,
} }

View File

@ -52,7 +52,7 @@ func takeScreenshot(screen *Image) error {
} }
blackbg := !IsScreenTransparent() blackbg := !IsScreenTransparent()
if err := screen.mipmap.DumpScreenshot(newname, blackbg); err != nil { if err := screen.image.DumpScreenshot(newname, blackbg); err != nil {
return err return err
} }

96
internal/ui/image.go Normal file
View File

@ -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
}

View File

@ -23,9 +23,9 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "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/shader"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
var shaderSuffix string 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. // For the details about the shader, see https://ebiten.org/documents/shader.html.
type Shader struct { type Shader struct {
shader *mipmap.Shader shader *ui.Shader
uniformNames []string uniformNames []string
uniformTypes []shaderir.Type uniformTypes []shaderir.Type
} }
@ -172,7 +172,7 @@ func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
} }
return &Shader{ return &Shader{
shader: mipmap.NewShader(s), shader: ui.NewShader(s),
uniformNames: s.UniformNames, uniformNames: s.UniformNames,
uniformTypes: s.Uniforms, uniformTypes: s.Uniforms,
}, nil }, nil