mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +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
@ -15,6 +15,5 @@
|
||||
package ebiten
|
||||
|
||||
var (
|
||||
CopyImage = copyImage
|
||||
MipmapLevelForDownscale = mipmapLevelForDownscale
|
||||
CopyImage = copyImage
|
||||
)
|
||||
|
71
image.go
71
image.go
@ -19,6 +19,7 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/buffered"
|
||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
)
|
||||
@ -33,9 +34,7 @@ type Image struct {
|
||||
// See strings.Builder for similar examples.
|
||||
addr *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.
|
||||
mipmap *mipmap
|
||||
buffered *buffered.Image
|
||||
|
||||
bounds image.Rectangle
|
||||
original *Image
|
||||
@ -56,7 +55,7 @@ func (i *Image) Size() (width, height int) {
|
||||
}
|
||||
|
||||
func (i *Image) isDisposed() bool {
|
||||
return i.mipmap.isDisposed()
|
||||
return i.buffered == nil
|
||||
}
|
||||
|
||||
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)")
|
||||
}
|
||||
|
||||
i.mipmap.fill(color.RGBAModel.Convert(clr).(color.RGBA))
|
||||
i.buffered.Fill(color.RGBAModel.Convert(clr).(color.RGBA))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -199,7 +198,8 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
|
||||
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
|
||||
}
|
||||
|
||||
@ -307,7 +307,32 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
||||
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.
|
||||
@ -324,8 +349,8 @@ func (i *Image) SubImage(r image.Rectangle) image.Image {
|
||||
}
|
||||
|
||||
img := &Image{
|
||||
mipmap: i.mipmap,
|
||||
filter: i.filter,
|
||||
buffered: i.buffered,
|
||||
filter: i.filter,
|
||||
}
|
||||
|
||||
// Keep the original image's reference not to dispose that by GC.
|
||||
@ -351,10 +376,6 @@ func (i *Image) Bounds() image.Rectangle {
|
||||
if i.isDisposed() {
|
||||
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
|
||||
}
|
||||
|
||||
@ -380,7 +401,7 @@ func (i *Image) At(x, y int) color.Color {
|
||||
if i.isSubImage() && !image.Pt(x, y).In(i.bounds) {
|
||||
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}
|
||||
}
|
||||
|
||||
@ -404,7 +425,7 @@ func (i *Image) Set(x, y int, clr color.Color) {
|
||||
}
|
||||
|
||||
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.
|
||||
@ -424,7 +445,8 @@ func (i *Image) Dispose() error {
|
||||
if i.isSubImage() {
|
||||
return nil
|
||||
}
|
||||
i.mipmap.dispose()
|
||||
i.buffered.MarkDisposed()
|
||||
i.buffered = 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))
|
||||
}
|
||||
|
||||
i.mipmap.replacePixels(p)
|
||||
i.buffered.ReplacePixels(p)
|
||||
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 {
|
||||
i := &Image{
|
||||
mipmap: newMipmap(width, height, volatile),
|
||||
filter: filter,
|
||||
buffered: buffered.NewImage(width, height, volatile),
|
||||
filter: filter,
|
||||
bounds: image.Rect(0, 0, width, height),
|
||||
}
|
||||
i.addr = i
|
||||
return i
|
||||
@ -529,8 +552,9 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
||||
width, height := size.X, size.Y
|
||||
|
||||
i := &Image{
|
||||
mipmap: newMipmap(width, height, false),
|
||||
filter: filter,
|
||||
buffered: buffered.NewImage(width, height, false),
|
||||
filter: filter,
|
||||
bounds: image.Rect(0, 0, width, height),
|
||||
}
|
||||
i.addr = i
|
||||
|
||||
@ -540,8 +564,9 @@ func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
||||
|
||||
func newScreenFramebufferImage(width, height int) *Image {
|
||||
i := &Image{
|
||||
mipmap: newScreenFramebufferMipmap(width, height),
|
||||
filter: FilterDefault,
|
||||
buffered: buffered.NewScreenFramebufferImage(width, height),
|
||||
filter: FilterDefault,
|
||||
bounds: image.Rect(0, 0, width, height),
|
||||
}
|
||||
i.addr = i
|
||||
return i
|
||||
|
@ -52,7 +52,7 @@ func takeScreenshot(screen *Image) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := screen.mipmap.dump(newname); err != nil {
|
||||
if err := screen.buffered.Dump(newname); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -15,15 +15,16 @@
|
||||
package buffered
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||
"github.com/hajimehoshi/ebiten/internal/shareable"
|
||||
"github.com/hajimehoshi/ebiten/internal/mipmap"
|
||||
)
|
||||
|
||||
type Image struct {
|
||||
img *shareable.Image
|
||||
img *mipmap.Mipmap
|
||||
width int
|
||||
height int
|
||||
|
||||
@ -32,7 +33,7 @@ type Image struct {
|
||||
}
|
||||
|
||||
func BeginFrame() error {
|
||||
if err := shareable.BeginFrame(); err != nil {
|
||||
if err := mipmap.BeginFrame(); err != nil {
|
||||
return err
|
||||
}
|
||||
flushDelayedCommands()
|
||||
@ -40,7 +41,7 @@ func BeginFrame() error {
|
||||
}
|
||||
|
||||
func EndFrame() error {
|
||||
return shareable.EndFrame()
|
||||
return mipmap.EndFrame()
|
||||
}
|
||||
|
||||
func NewImage(width, height int, volatile bool) *Image {
|
||||
@ -48,7 +49,7 @@ func NewImage(width, height int, volatile bool) *Image {
|
||||
delayedCommandsM.Lock()
|
||||
if needsToDelayCommands {
|
||||
delayedCommands = append(delayedCommands, func() {
|
||||
i.img = shareable.NewImage(width, height, volatile)
|
||||
i.img = mipmap.New(width, height, volatile)
|
||||
i.width = width
|
||||
i.height = height
|
||||
})
|
||||
@ -57,7 +58,7 @@ func NewImage(width, height int, volatile bool) *Image {
|
||||
}
|
||||
delayedCommandsM.Unlock()
|
||||
|
||||
i.img = shareable.NewImage(width, height, volatile)
|
||||
i.img = mipmap.New(width, height, volatile)
|
||||
i.width = width
|
||||
i.height = height
|
||||
return i
|
||||
@ -68,7 +69,7 @@ func NewScreenFramebufferImage(width, height int) *Image {
|
||||
delayedCommandsM.Lock()
|
||||
if needsToDelayCommands {
|
||||
delayedCommands = append(delayedCommands, func() {
|
||||
i.img = shareable.NewScreenFramebufferImage(width, height)
|
||||
i.img = mipmap.NewScreenFramebufferMipmap(width, height)
|
||||
i.width = width
|
||||
i.height = height
|
||||
})
|
||||
@ -77,7 +78,7 @@ func NewScreenFramebufferImage(width, height int) *Image {
|
||||
}
|
||||
delayedCommandsM.Unlock()
|
||||
|
||||
i.img = shareable.NewScreenFramebufferImage(width, height)
|
||||
i.img = mipmap.NewScreenFramebufferMipmap(width, height)
|
||||
i.width = width
|
||||
i.height = height
|
||||
return i
|
||||
@ -204,6 +205,35 @@ func (i *Image) ReplacePixels(pix []byte) {
|
||||
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) {
|
||||
if i == src {
|
||||
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
|
||||
// limitations under the License.
|
||||
|
||||
package ebiten
|
||||
package mipmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -21,67 +21,82 @@ import (
|
||||
"math"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/affine"
|
||||
"github.com/hajimehoshi/ebiten/internal/buffered"
|
||||
"github.com/hajimehoshi/ebiten/internal/driver"
|
||||
"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
|
||||
height int
|
||||
volatile bool
|
||||
orig *buffered.Image
|
||||
orig *shareable.Image
|
||||
imgs map[image.Rectangle]levelToImage
|
||||
}
|
||||
|
||||
func newMipmap(width, height int, volatile bool) *mipmap {
|
||||
return &mipmap{
|
||||
func New(width, height int, volatile bool) *Mipmap {
|
||||
return &Mipmap{
|
||||
width: width,
|
||||
height: height,
|
||||
volatile: volatile,
|
||||
orig: buffered.NewImage(width, height, volatile),
|
||||
orig: shareable.NewImage(width, height, volatile),
|
||||
imgs: map[image.Rectangle]levelToImage{},
|
||||
}
|
||||
}
|
||||
|
||||
func newScreenFramebufferMipmap(width, height int) *mipmap {
|
||||
return &mipmap{
|
||||
func NewScreenFramebufferMipmap(width, height int) *Mipmap {
|
||||
return &Mipmap{
|
||||
width: width,
|
||||
height: height,
|
||||
orig: buffered.NewScreenFramebufferImage(width, height),
|
||||
orig: shareable.NewScreenFramebufferImage(width, height),
|
||||
imgs: map[image.Rectangle]levelToImage{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mipmap) dump(name string) error {
|
||||
func (m *Mipmap) Dump(name string) error {
|
||||
return m.orig.Dump(name)
|
||||
}
|
||||
|
||||
func (m *mipmap) fill(clr color.RGBA) {
|
||||
func (m *Mipmap) Fill(clr color.RGBA) {
|
||||
m.orig.Fill(clr)
|
||||
m.disposeMipmaps()
|
||||
}
|
||||
|
||||
func (m *mipmap) replacePixels(pix []byte) {
|
||||
func (m *Mipmap) ReplacePixels(pix []byte) {
|
||||
m.orig.ReplacePixels(pix)
|
||||
m.disposeMipmaps()
|
||||
}
|
||||
|
||||
func (m *mipmap) size() (int, int) {
|
||||
return m.width, m.height
|
||||
}
|
||||
|
||||
func (m *mipmap) at(x, y int) (r, g, b, a byte) {
|
||||
func (m *Mipmap) At(x, y int) (r, g, b, a byte) {
|
||||
return m.orig.At(x, y)
|
||||
}
|
||||
|
||||
func (m *mipmap) set(x, y int, r, g, b, a byte) {
|
||||
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) {
|
||||
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)) {
|
||||
@ -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")
|
||||
}
|
||||
|
||||
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 {
|
||||
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()
|
||||
@ -149,37 +164,12 @@ func (m *mipmap) drawImage(src *mipmap, bounds image.Rectangle, geom *GeoM, colo
|
||||
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) {
|
||||
bx0 := float32(bounds.Min.X)
|
||||
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)
|
||||
func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
|
||||
m.orig.DrawTriangles(src.orig, vertices, indices, colorm, mode, filter, address)
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
var src *buffered.Image
|
||||
var src *shareable.Image
|
||||
var vs []float32
|
||||
var filter driver.Filter
|
||||
switch {
|
||||
@ -237,7 +227,7 @@ func (m *mipmap) level(r image.Rectangle, level int) *buffered.Image {
|
||||
imgs[level] = 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)
|
||||
imgs[level] = s
|
||||
|
||||
@ -264,17 +254,17 @@ func sizeForLevel(origWidth, origHeight int, level int) (width, height int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (m *mipmap) isDisposed() bool {
|
||||
func (m *Mipmap) isDisposed() bool {
|
||||
return m.orig == nil
|
||||
}
|
||||
|
||||
func (m *mipmap) dispose() {
|
||||
func (m *Mipmap) MarkDisposed() {
|
||||
m.disposeMipmaps()
|
||||
m.orig.MarkDisposed()
|
||||
m.orig = nil
|
||||
}
|
||||
|
||||
func (m *mipmap) disposeMipmaps() {
|
||||
func (m *Mipmap) disposeMipmaps() {
|
||||
for _, a := range m.imgs {
|
||||
for _, img := range a {
|
||||
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 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()
|
||||
if math.IsNaN(float64(det)) {
|
||||
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.
|
||||
return mipmapLevelForDownscale(det)
|
||||
return MipmapLevelForDownscale(det)
|
||||
}
|
||||
|
||||
func mipmapLevelForDownscale(det float32) int {
|
||||
func MipmapLevelForDownscale(det float32) int {
|
||||
if math.IsNaN(float64(det)) {
|
||||
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) {
|
||||
a, b, c, d, _, _ := geom.elements()
|
||||
a, b, c, d := geom.A, geom.B, geom.C, geom.D
|
||||
// (0, 1)
|
||||
x0 := 0*a + 1*b
|
||||
y0 := 0*c + 1*d
|
@ -12,13 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ebiten_test
|
||||
package mipmap_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
. "github.com/hajimehoshi/ebiten"
|
||||
. "github.com/hajimehoshi/ebiten/internal/mipmap"
|
||||
)
|
||||
|
||||
func TestMipmapLevelForDownscale(t *testing.T) {
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ebiten
|
||||
package mipmap
|
||||
|
||||
import (
|
||||
"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[:48]
|
||||
|
||||
// For each values, see the comment at shareable.(*Image).DrawTriangles.
|
||||
vs[0] = tx
|
||||
vs[1] = ty
|
||||
vs[2] = u0
|
Loading…
Reference in New Issue
Block a user