mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-27 19:22:49 +01:00
mipmap: Create mipmap package and bufferd.Image uses it
Mipmap calculation must be executed after the main loop starts because the graphics driver's HasHighPrecisionFloat is needed. Then, operations on mipmap images must be called from images in buffered package. Updates #1044
This commit is contained in:
parent
7f4a82ddf0
commit
c99fd1a742
@ -16,5 +16,4 @@ package ebiten
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
CopyImage = copyImage
|
CopyImage = copyImage
|
||||||
MipmapLevelForDownscale = mipmapLevelForDownscale
|
|
||||||
)
|
)
|
||||||
|
63
image.go
63
image.go
@ -19,6 +19,7 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/internal/buffered"
|
||||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||||
)
|
)
|
||||||
@ -33,9 +34,7 @@ type Image struct {
|
|||||||
// See strings.Builder for similar examples.
|
// See strings.Builder for similar examples.
|
||||||
addr *Image
|
addr *Image
|
||||||
|
|
||||||
// mipmap is a set of shareable.Image sorted by the order of mipmap level.
|
buffered *buffered.Image
|
||||||
// The level 0 image is a regular image and higher-level images are used for mipmap.
|
|
||||||
mipmap *mipmap
|
|
||||||
|
|
||||||
bounds image.Rectangle
|
bounds image.Rectangle
|
||||||
original *Image
|
original *Image
|
||||||
@ -56,7 +55,7 @@ func (i *Image) Size() (width, height int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) isDisposed() bool {
|
func (i *Image) isDisposed() bool {
|
||||||
return i.mipmap.isDisposed()
|
return i.buffered == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) isSubImage() bool {
|
func (i *Image) isSubImage() bool {
|
||||||
@ -90,7 +89,7 @@ func (i *Image) Fill(clr color.Color) error {
|
|||||||
panic("ebiten: render to a subimage is not implemented (Fill)")
|
panic("ebiten: render to a subimage is not implemented (Fill)")
|
||||||
}
|
}
|
||||||
|
|
||||||
i.mipmap.fill(color.RGBAModel.Convert(clr).(color.RGBA))
|
i.buffered.Fill(color.RGBAModel.Convert(clr).(color.RGBA))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +198,8 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
|
|||||||
filter = driver.Filter(img.filter)
|
filter = driver.Filter(img.filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
i.mipmap.drawImage(img.mipmap, img.Bounds(), geom, options.ColorM.impl, mode, filter)
|
a, b, c, d, tx, ty := geom.elements()
|
||||||
|
i.buffered.DrawImage(img.buffered, img.Bounds(), a, b, c, d, tx, ty, options.ColorM.impl, mode, filter)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +307,32 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
|||||||
filter = driver.Filter(img.filter)
|
filter = driver.Filter(img.filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
i.mipmap.drawTriangles(img.mipmap, img.Bounds(), vertices, indices, options.ColorM.impl, mode, filter, driver.Address(options.Address))
|
b := img.Bounds()
|
||||||
|
bx0 := float32(b.Min.X)
|
||||||
|
by0 := float32(b.Min.Y)
|
||||||
|
bx1 := float32(b.Max.X)
|
||||||
|
by1 := float32(b.Max.Y)
|
||||||
|
|
||||||
|
// TODO: Should we use mipmap.verticesBackend?
|
||||||
|
vs := make([]float32, len(vertices)*graphics.VertexFloatNum)
|
||||||
|
for i, v := range vertices {
|
||||||
|
vs[i*graphics.VertexFloatNum] = v.DstX
|
||||||
|
vs[i*graphics.VertexFloatNum+1] = v.DstY
|
||||||
|
vs[i*graphics.VertexFloatNum+2] = v.SrcX
|
||||||
|
vs[i*graphics.VertexFloatNum+3] = v.SrcY
|
||||||
|
vs[i*graphics.VertexFloatNum+4] = bx0
|
||||||
|
vs[i*graphics.VertexFloatNum+5] = by0
|
||||||
|
vs[i*graphics.VertexFloatNum+6] = bx1
|
||||||
|
vs[i*graphics.VertexFloatNum+7] = by1
|
||||||
|
vs[i*graphics.VertexFloatNum+8] = v.ColorR
|
||||||
|
vs[i*graphics.VertexFloatNum+9] = v.ColorG
|
||||||
|
vs[i*graphics.VertexFloatNum+10] = v.ColorB
|
||||||
|
vs[i*graphics.VertexFloatNum+11] = v.ColorA
|
||||||
|
}
|
||||||
|
is := make([]uint16, len(indices))
|
||||||
|
copy(is, indices)
|
||||||
|
|
||||||
|
i.buffered.DrawTriangles(img.buffered, vs, is, options.ColorM.impl, mode, filter, driver.Address(options.Address))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubImage returns an image representing the portion of the image p visible through r. The returned value shares pixels with the original image.
|
// SubImage returns an image representing the portion of the image p visible through r. The returned value shares pixels with the original image.
|
||||||
@ -324,7 +349,7 @@ func (i *Image) SubImage(r image.Rectangle) image.Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
img := &Image{
|
img := &Image{
|
||||||
mipmap: i.mipmap,
|
buffered: i.buffered,
|
||||||
filter: i.filter,
|
filter: i.filter,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,10 +376,6 @@ func (i *Image) Bounds() image.Rectangle {
|
|||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
panic("ebiten: the image is already disposed")
|
panic("ebiten: the image is already disposed")
|
||||||
}
|
}
|
||||||
if !i.isSubImage() {
|
|
||||||
w, h := i.mipmap.size()
|
|
||||||
return image.Rect(0, 0, w, h)
|
|
||||||
}
|
|
||||||
return i.bounds
|
return i.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,7 +401,7 @@ func (i *Image) At(x, y int) color.Color {
|
|||||||
if i.isSubImage() && !image.Pt(x, y).In(i.bounds) {
|
if i.isSubImage() && !image.Pt(x, y).In(i.bounds) {
|
||||||
return color.RGBA{}
|
return color.RGBA{}
|
||||||
}
|
}
|
||||||
r, g, b, a := i.mipmap.at(x, y)
|
r, g, b, a := i.buffered.At(x, y)
|
||||||
return color.RGBA{r, g, b, a}
|
return color.RGBA{r, g, b, a}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,7 +425,7 @@ func (i *Image) Set(x, y int, clr color.Color) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r, g, b, a := clr.RGBA()
|
r, g, b, a := clr.RGBA()
|
||||||
i.mipmap.set(x, y, byte(r>>8), byte(g>>8), byte(b>>8), byte(a>>8))
|
i.buffered.Set(x, y, byte(r>>8), byte(g>>8), byte(b>>8), byte(a>>8))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispose disposes the image data. After disposing, most of image functions do nothing and returns meaningless values.
|
// Dispose disposes the image data. After disposing, most of image functions do nothing and returns meaningless values.
|
||||||
@ -424,7 +445,8 @@ func (i *Image) Dispose() error {
|
|||||||
if i.isSubImage() {
|
if i.isSubImage() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
i.mipmap.dispose()
|
i.buffered.MarkDisposed()
|
||||||
|
i.buffered = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,7 +476,7 @@ func (i *Image) ReplacePixels(p []byte) error {
|
|||||||
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l))
|
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l))
|
||||||
}
|
}
|
||||||
|
|
||||||
i.mipmap.replacePixels(p)
|
i.buffered.ReplacePixels(p)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,8 +530,9 @@ func NewImage(width, height int, filter Filter) (*Image, error) {
|
|||||||
|
|
||||||
func newImage(width, height int, filter Filter, volatile bool) *Image {
|
func newImage(width, height int, filter Filter, volatile bool) *Image {
|
||||||
i := &Image{
|
i := &Image{
|
||||||
mipmap: newMipmap(width, height, volatile),
|
buffered: buffered.NewImage(width, height, volatile),
|
||||||
filter: filter,
|
filter: filter,
|
||||||
|
bounds: image.Rect(0, 0, width, height),
|
||||||
}
|
}
|
||||||
i.addr = i
|
i.addr = i
|
||||||
return i
|
return i
|
||||||
@ -529,8 +552,9 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
|||||||
width, height := size.X, size.Y
|
width, height := size.X, size.Y
|
||||||
|
|
||||||
i := &Image{
|
i := &Image{
|
||||||
mipmap: newMipmap(width, height, false),
|
buffered: buffered.NewImage(width, height, false),
|
||||||
filter: filter,
|
filter: filter,
|
||||||
|
bounds: image.Rect(0, 0, width, height),
|
||||||
}
|
}
|
||||||
i.addr = i
|
i.addr = i
|
||||||
|
|
||||||
@ -540,8 +564,9 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
|||||||
|
|
||||||
func newScreenFramebufferImage(width, height int) *Image {
|
func newScreenFramebufferImage(width, height int) *Image {
|
||||||
i := &Image{
|
i := &Image{
|
||||||
mipmap: newScreenFramebufferMipmap(width, height),
|
buffered: buffered.NewScreenFramebufferImage(width, height),
|
||||||
filter: FilterDefault,
|
filter: FilterDefault,
|
||||||
|
bounds: image.Rect(0, 0, width, height),
|
||||||
}
|
}
|
||||||
i.addr = i
|
i.addr = i
|
||||||
return i
|
return i
|
||||||
|
@ -52,7 +52,7 @@ func takeScreenshot(screen *Image) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := screen.mipmap.dump(newname); err != nil {
|
if err := screen.buffered.Dump(newname); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,15 +15,16 @@
|
|||||||
package buffered
|
package buffered
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||||
"github.com/hajimehoshi/ebiten/internal/shareable"
|
"github.com/hajimehoshi/ebiten/internal/mipmap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Image struct {
|
type Image struct {
|
||||||
img *shareable.Image
|
img *mipmap.Mipmap
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ type Image struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BeginFrame() error {
|
func BeginFrame() error {
|
||||||
if err := shareable.BeginFrame(); err != nil {
|
if err := mipmap.BeginFrame(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
flushDelayedCommands()
|
flushDelayedCommands()
|
||||||
@ -40,7 +41,7 @@ func BeginFrame() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func EndFrame() error {
|
func EndFrame() error {
|
||||||
return shareable.EndFrame()
|
return mipmap.EndFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImage(width, height int, volatile bool) *Image {
|
func NewImage(width, height int, volatile bool) *Image {
|
||||||
@ -48,7 +49,7 @@ func NewImage(width, height int, volatile bool) *Image {
|
|||||||
delayedCommandsM.Lock()
|
delayedCommandsM.Lock()
|
||||||
if needsToDelayCommands {
|
if needsToDelayCommands {
|
||||||
delayedCommands = append(delayedCommands, func() {
|
delayedCommands = append(delayedCommands, func() {
|
||||||
i.img = shareable.NewImage(width, height, volatile)
|
i.img = mipmap.New(width, height, volatile)
|
||||||
i.width = width
|
i.width = width
|
||||||
i.height = height
|
i.height = height
|
||||||
})
|
})
|
||||||
@ -57,7 +58,7 @@ func NewImage(width, height int, volatile bool) *Image {
|
|||||||
}
|
}
|
||||||
delayedCommandsM.Unlock()
|
delayedCommandsM.Unlock()
|
||||||
|
|
||||||
i.img = shareable.NewImage(width, height, volatile)
|
i.img = mipmap.New(width, height, volatile)
|
||||||
i.width = width
|
i.width = width
|
||||||
i.height = height
|
i.height = height
|
||||||
return i
|
return i
|
||||||
@ -68,7 +69,7 @@ func NewScreenFramebufferImage(width, height int) *Image {
|
|||||||
delayedCommandsM.Lock()
|
delayedCommandsM.Lock()
|
||||||
if needsToDelayCommands {
|
if needsToDelayCommands {
|
||||||
delayedCommands = append(delayedCommands, func() {
|
delayedCommands = append(delayedCommands, func() {
|
||||||
i.img = shareable.NewScreenFramebufferImage(width, height)
|
i.img = mipmap.NewScreenFramebufferMipmap(width, height)
|
||||||
i.width = width
|
i.width = width
|
||||||
i.height = height
|
i.height = height
|
||||||
})
|
})
|
||||||
@ -77,7 +78,7 @@ func NewScreenFramebufferImage(width, height int) *Image {
|
|||||||
}
|
}
|
||||||
delayedCommandsM.Unlock()
|
delayedCommandsM.Unlock()
|
||||||
|
|
||||||
i.img = shareable.NewScreenFramebufferImage(width, height)
|
i.img = mipmap.NewScreenFramebufferMipmap(width, height)
|
||||||
i.width = width
|
i.width = width
|
||||||
i.height = height
|
i.height = height
|
||||||
return i
|
return i
|
||||||
@ -204,6 +205,35 @@ func (i *Image) ReplacePixels(pix []byte) {
|
|||||||
i.img.ReplacePixels(pix)
|
i.img.ReplacePixels(pix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
delayedCommandsM.Lock()
|
||||||
|
if needsToDelayCommands {
|
||||||
|
delayedCommands = append(delayedCommands, func() {
|
||||||
|
i.img.DrawImage(src.img, bounds, g, colorm, mode, filter)
|
||||||
|
})
|
||||||
|
delayedCommandsM.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delayedCommandsM.Unlock()
|
||||||
|
|
||||||
|
src.resolvePendingPixels(true)
|
||||||
|
i.resolvePendingPixels(false)
|
||||||
|
i.img.DrawImage(src.img, bounds, g, colorm, mode, filter)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
|
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
|
||||||
if i == src {
|
if i == src {
|
||||||
panic("buffered: Image.DrawTriangles: src must be different from the receiver")
|
panic("buffered: Image.DrawTriangles: src must be different from the receiver")
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package ebiten
|
package mipmap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -21,67 +21,82 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||||
"github.com/hajimehoshi/ebiten/internal/buffered"
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||||
|
"github.com/hajimehoshi/ebiten/internal/shareable"
|
||||||
)
|
)
|
||||||
|
|
||||||
type levelToImage map[int]*buffered.Image
|
func BeginFrame() error {
|
||||||
|
return shareable.BeginFrame()
|
||||||
|
}
|
||||||
|
|
||||||
type mipmap struct {
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
type levelToImage map[int]*shareable.Image
|
||||||
|
|
||||||
|
// 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 {
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
volatile bool
|
volatile bool
|
||||||
orig *buffered.Image
|
orig *shareable.Image
|
||||||
imgs map[image.Rectangle]levelToImage
|
imgs map[image.Rectangle]levelToImage
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMipmap(width, height int, volatile bool) *mipmap {
|
func New(width, height int, volatile bool) *Mipmap {
|
||||||
return &mipmap{
|
return &Mipmap{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
volatile: volatile,
|
volatile: volatile,
|
||||||
orig: buffered.NewImage(width, height, volatile),
|
orig: shareable.NewImage(width, height, volatile),
|
||||||
imgs: map[image.Rectangle]levelToImage{},
|
imgs: map[image.Rectangle]levelToImage{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newScreenFramebufferMipmap(width, height int) *mipmap {
|
func NewScreenFramebufferMipmap(width, height int) *Mipmap {
|
||||||
return &mipmap{
|
return &Mipmap{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
orig: buffered.NewScreenFramebufferImage(width, height),
|
orig: shareable.NewScreenFramebufferImage(width, height),
|
||||||
imgs: map[image.Rectangle]levelToImage{},
|
imgs: map[image.Rectangle]levelToImage{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) dump(name string) error {
|
func (m *Mipmap) Dump(name string) error {
|
||||||
return m.orig.Dump(name)
|
return m.orig.Dump(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) fill(clr color.RGBA) {
|
func (m *Mipmap) Fill(clr color.RGBA) {
|
||||||
m.orig.Fill(clr)
|
m.orig.Fill(clr)
|
||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) replacePixels(pix []byte) {
|
func (m *Mipmap) ReplacePixels(pix []byte) {
|
||||||
m.orig.ReplacePixels(pix)
|
m.orig.ReplacePixels(pix)
|
||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) size() (int, int) {
|
func (m *Mipmap) At(x, y int) (r, g, b, a byte) {
|
||||||
return m.width, m.height
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mipmap) at(x, y int) (r, g, b, a byte) {
|
|
||||||
return m.orig.At(x, y)
|
return m.orig.At(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) set(x, y int, r, g, b, a byte) {
|
func (m *Mipmap) DrawImage(src *Mipmap, bounds image.Rectangle, geom *GeoM, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter) {
|
||||||
m.orig.Set(x, y, r, g, b, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
if det := geom.det(); det == 0 {
|
||||||
return
|
return
|
||||||
} else if math.IsNaN(float64(det)) {
|
} else if math.IsNaN(float64(det)) {
|
||||||
@ -130,7 +145,7 @@ func (m *mipmap) drawImage(src *mipmap, bounds image.Rectangle, geom *GeoM, colo
|
|||||||
panic("ebiten: Mipmap must not be used when the filter is FilterScreen")
|
panic("ebiten: Mipmap must not be used when the filter is FilterScreen")
|
||||||
}
|
}
|
||||||
|
|
||||||
a, b, c, d, tx, ty := geom.elements()
|
a, b, c, d, tx, ty := geom.A, geom.B, geom.C, geom.D, geom.Tx, geom.Ty
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
vs := quadVertices(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, a, b, c, d, tx, ty, cr, cg, cb, ca, screen)
|
vs := quadVertices(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, a, b, c, d, tx, ty, cr, cg, cb, ca, screen)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
@ -149,37 +164,12 @@ func (m *mipmap) drawImage(src *mipmap, bounds image.Rectangle, geom *GeoM, colo
|
|||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) drawTriangles(src *mipmap, bounds image.Rectangle, vertices []Vertex, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
|
func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
|
||||||
bx0 := float32(bounds.Min.X)
|
m.orig.DrawTriangles(src.orig, vertices, indices, colorm, mode, filter, address)
|
||||||
by0 := float32(bounds.Min.Y)
|
|
||||||
bx1 := float32(bounds.Max.X)
|
|
||||||
by1 := float32(bounds.Max.Y)
|
|
||||||
|
|
||||||
// TODO: Needs boundary check optimization?
|
|
||||||
// See https://go101.org/article/bounds-check-elimination.html
|
|
||||||
|
|
||||||
vs := vertexSlice(len(vertices), false)
|
|
||||||
for i, v := range vertices {
|
|
||||||
vs[i*graphics.VertexFloatNum] = v.DstX
|
|
||||||
vs[i*graphics.VertexFloatNum+1] = v.DstY
|
|
||||||
vs[i*graphics.VertexFloatNum+2] = v.SrcX
|
|
||||||
vs[i*graphics.VertexFloatNum+3] = v.SrcY
|
|
||||||
vs[i*graphics.VertexFloatNum+4] = bx0
|
|
||||||
vs[i*graphics.VertexFloatNum+5] = by0
|
|
||||||
vs[i*graphics.VertexFloatNum+6] = bx1
|
|
||||||
vs[i*graphics.VertexFloatNum+7] = by1
|
|
||||||
vs[i*graphics.VertexFloatNum+8] = v.ColorR
|
|
||||||
vs[i*graphics.VertexFloatNum+9] = v.ColorG
|
|
||||||
vs[i*graphics.VertexFloatNum+10] = v.ColorB
|
|
||||||
vs[i*graphics.VertexFloatNum+11] = v.ColorA
|
|
||||||
}
|
|
||||||
is := make([]uint16, len(indices))
|
|
||||||
copy(is, indices)
|
|
||||||
m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, address)
|
|
||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) level(r image.Rectangle, level int) *buffered.Image {
|
func (m *Mipmap) level(r image.Rectangle, level int) *shareable.Image {
|
||||||
if level == 0 {
|
if level == 0 {
|
||||||
panic("ebiten: level must be non-zero at level")
|
panic("ebiten: level must be non-zero at level")
|
||||||
}
|
}
|
||||||
@ -197,7 +187,7 @@ func (m *mipmap) level(r image.Rectangle, level int) *buffered.Image {
|
|||||||
return img
|
return img
|
||||||
}
|
}
|
||||||
|
|
||||||
var src *buffered.Image
|
var src *shareable.Image
|
||||||
var vs []float32
|
var vs []float32
|
||||||
var filter driver.Filter
|
var filter driver.Filter
|
||||||
switch {
|
switch {
|
||||||
@ -237,7 +227,7 @@ func (m *mipmap) level(r image.Rectangle, level int) *buffered.Image {
|
|||||||
imgs[level] = nil
|
imgs[level] = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s := buffered.NewImage(w2, h2, m.volatile)
|
s := shareable.NewImage(w2, h2, m.volatile)
|
||||||
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressClampToZero)
|
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressClampToZero)
|
||||||
imgs[level] = s
|
imgs[level] = s
|
||||||
|
|
||||||
@ -264,17 +254,17 @@ func sizeForLevel(origWidth, origHeight int, level int) (width, height int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) isDisposed() bool {
|
func (m *Mipmap) isDisposed() bool {
|
||||||
return m.orig == nil
|
return m.orig == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) dispose() {
|
func (m *Mipmap) MarkDisposed() {
|
||||||
m.disposeMipmaps()
|
m.disposeMipmaps()
|
||||||
m.orig.MarkDisposed()
|
m.orig.MarkDisposed()
|
||||||
m.orig = nil
|
m.orig = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mipmap) disposeMipmaps() {
|
func (m *Mipmap) disposeMipmaps() {
|
||||||
for _, a := range m.imgs {
|
for _, a := range m.imgs {
|
||||||
for _, img := range a {
|
for _, img := range a {
|
||||||
img.MarkDisposed()
|
img.MarkDisposed()
|
||||||
@ -288,7 +278,7 @@ func (m *mipmap) disposeMipmaps() {
|
|||||||
// mipmapLevel returns an appropriate mipmap level for the given determinant of a geometry matrix.
|
// mipmapLevel returns an appropriate mipmap level for the given determinant of a geometry matrix.
|
||||||
//
|
//
|
||||||
// mipmapLevel panics if det is NaN or 0.
|
// mipmapLevel panics if det is NaN or 0.
|
||||||
func (m *mipmap) mipmapLevel(geom *GeoM, width, height int, filter driver.Filter) int {
|
func (m *Mipmap) mipmapLevel(geom *GeoM, width, height int, filter driver.Filter) int {
|
||||||
det := geom.det()
|
det := geom.det()
|
||||||
if math.IsNaN(float64(det)) {
|
if math.IsNaN(float64(det)) {
|
||||||
panic("ebiten: det must be finite at mipmapLevel")
|
panic("ebiten: det must be finite at mipmapLevel")
|
||||||
@ -338,10 +328,10 @@ func (m *mipmap) mipmapLevel(geom *GeoM, width, height int, filter driver.Filter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is a separate function for testing.
|
// This is a separate function for testing.
|
||||||
return mipmapLevelForDownscale(det)
|
return MipmapLevelForDownscale(det)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mipmapLevelForDownscale(det float32) int {
|
func MipmapLevelForDownscale(det float32) int {
|
||||||
if math.IsNaN(float64(det)) {
|
if math.IsNaN(float64(det)) {
|
||||||
panic("ebiten: det must be finite at mipmapLevelForDownscale")
|
panic("ebiten: det must be finite at mipmapLevelForDownscale")
|
||||||
}
|
}
|
||||||
@ -401,7 +391,7 @@ func minf32(a, b, c, d float32) float32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func geomScaleSize(geom *GeoM) (sx, sy float32) {
|
func geomScaleSize(geom *GeoM) (sx, sy float32) {
|
||||||
a, b, c, d, _, _ := geom.elements()
|
a, b, c, d := geom.A, geom.B, geom.C, geom.D
|
||||||
// (0, 1)
|
// (0, 1)
|
||||||
x0 := 0*a + 1*b
|
x0 := 0*a + 1*b
|
||||||
y0 := 0*c + 1*d
|
y0 := 0*c + 1*d
|
@ -12,13 +12,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package ebiten_test
|
package mipmap_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/hajimehoshi/ebiten"
|
. "github.com/hajimehoshi/ebiten/internal/mipmap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMipmapLevelForDownscale(t *testing.T) {
|
func TestMipmapLevelForDownscale(t *testing.T) {
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package ebiten
|
package mipmap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
@ -70,7 +70,6 @@ func quadVertices(sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb
|
|||||||
vs := vertexSlice(4, last)
|
vs := vertexSlice(4, last)
|
||||||
_ = vs[:48]
|
_ = vs[:48]
|
||||||
|
|
||||||
// For each values, see the comment at shareable.(*Image).DrawTriangles.
|
|
||||||
vs[0] = tx
|
vs[0] = tx
|
||||||
vs[1] = ty
|
vs[1] = ty
|
||||||
vs[2] = u0
|
vs[2] = u0
|
Loading…
Reference in New Issue
Block a user