updated ebiten version from 2.7.9 to 2.9.9
This commit is contained in:
+204
@@ -0,0 +1,204 @@
|
||||
// Copyright 2025 The Ebitengine 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 vector
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math"
|
||||
"slices"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
type atlasRegion struct {
|
||||
pathIndex int
|
||||
imageIndex int
|
||||
imageBounds image.Rectangle
|
||||
}
|
||||
|
||||
type atlas struct {
|
||||
pathRenderingBounds []image.Rectangle
|
||||
atlasRegions []atlasRegion
|
||||
pathIndexToAtlasRegionIndex map[int]int
|
||||
atlasSizes []image.Point
|
||||
atlasImages []*ebiten.Image
|
||||
}
|
||||
|
||||
func roundUpAtlasSize(size int) int {
|
||||
if size < 16 {
|
||||
return 16
|
||||
}
|
||||
return int(math.Ceil(math.Pow(1.5, math.Ceil(math.Log(float64(size))/math.Log(1.5)))))
|
||||
}
|
||||
|
||||
func roundUp16(x int) int {
|
||||
return (x + 15) &^ 15
|
||||
}
|
||||
|
||||
func (a *atlas) setPaths(dstBounds image.Rectangle, paths []*Path, antialias bool) {
|
||||
// Reset the members.
|
||||
a.pathRenderingBounds = slices.Delete(a.pathRenderingBounds, 0, len(a.pathRenderingBounds))
|
||||
a.atlasRegions = slices.Delete(a.atlasRegions, 0, len(a.atlasRegions))
|
||||
clear(a.pathIndexToAtlasRegionIndex)
|
||||
a.atlasSizes = slices.Delete(a.atlasSizes, 0, len(a.atlasSizes))
|
||||
|
||||
if len(paths) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
a.pathRenderingBounds = slices.Grow(a.pathRenderingBounds, len(paths))[:len(paths)]
|
||||
for i, p := range paths {
|
||||
b := p.Bounds().Intersect(dstBounds)
|
||||
// Round up the size to 16px in order to encourage reusing sub image cache.
|
||||
a.pathRenderingBounds[i] = image.Rectangle{
|
||||
Min: b.Min,
|
||||
Max: b.Min.Add(image.Pt(roundUp16(b.Dx()), roundUp16(b.Dy()))),
|
||||
}
|
||||
a.atlasRegions = append(a.atlasRegions, atlasRegion{
|
||||
pathIndex: i,
|
||||
})
|
||||
}
|
||||
|
||||
slices.SortFunc(a.atlasRegions, func(ra, rb atlasRegion) int {
|
||||
ba := a.pathRenderingBounds[ra.pathIndex]
|
||||
bb := a.pathRenderingBounds[rb.pathIndex]
|
||||
if ba.Dy() != bb.Dy() {
|
||||
return bb.Dy() - ba.Dy()
|
||||
}
|
||||
if ba.Dx() != bb.Dx() {
|
||||
return ba.Dx() - bb.Dx()
|
||||
}
|
||||
return ra.pathIndex - rb.pathIndex
|
||||
})
|
||||
|
||||
if a.pathIndexToAtlasRegionIndex == nil {
|
||||
a.pathIndexToAtlasRegionIndex = make(map[int]int, len(a.atlasRegions))
|
||||
}
|
||||
for i, r := range a.atlasRegions {
|
||||
a.pathIndexToAtlasRegionIndex[r.pathIndex] = i
|
||||
}
|
||||
|
||||
w, h := dstBounds.Dx(), dstBounds.Dy()
|
||||
// For antialiasing, doubled regions in the X direction are used.
|
||||
if antialias {
|
||||
w *= 2
|
||||
}
|
||||
// Use 2^n - 1, as a region in internal/atlas has 1px padding.
|
||||
maxImageSize := max(4093, w, h)
|
||||
|
||||
// Pack the regions into an atlas with a very simple algorithm:
|
||||
// Order the regions by height and then place them in a row.
|
||||
var atlasImageCount int
|
||||
{
|
||||
a.atlasSizes = append(a.atlasSizes, image.Point{})
|
||||
|
||||
var atlasImageIndex int
|
||||
var currentRowHeight int
|
||||
var currentPosition image.Point
|
||||
for i := range a.atlasRegions {
|
||||
pb := a.pathRenderingBounds[a.atlasRegions[i].pathIndex]
|
||||
// TODO: What if s already exceeds maxImageSize (#3357)?
|
||||
s := pb.Size()
|
||||
// An additional image for antialiasing must be on the same atlas,
|
||||
// so extend the width and use it as a sub image.
|
||||
if antialias {
|
||||
s.X *= 2
|
||||
}
|
||||
if i == 0 {
|
||||
currentRowHeight = s.Y
|
||||
} else if currentPosition.X+s.X > maxImageSize {
|
||||
// Try the next row.
|
||||
currentPosition.X = 0
|
||||
currentPosition.Y += currentRowHeight
|
||||
if currentPosition.Y+s.Y > maxImageSize {
|
||||
atlasImageIndex++
|
||||
a.atlasSizes = append(a.atlasSizes, image.Point{})
|
||||
currentPosition.Y = 0
|
||||
currentRowHeight = s.Y
|
||||
} else {
|
||||
currentRowHeight = max(currentRowHeight, s.Y)
|
||||
}
|
||||
}
|
||||
a.atlasRegions[i].imageIndex = atlasImageIndex
|
||||
a.atlasRegions[i].imageBounds = image.Rectangle{
|
||||
Min: currentPosition,
|
||||
Max: currentPosition.Add(s),
|
||||
}
|
||||
a.atlasSizes[atlasImageIndex] = image.Point{
|
||||
X: max(a.atlasSizes[atlasImageIndex].X, a.atlasRegions[i].imageBounds.Max.X),
|
||||
Y: max(a.atlasSizes[atlasImageIndex].Y, a.atlasRegions[i].imageBounds.Max.Y),
|
||||
}
|
||||
currentPosition.X += s.X
|
||||
}
|
||||
atlasImageCount = atlasImageIndex + 1
|
||||
}
|
||||
|
||||
a.atlasImages = slices.Grow(a.atlasImages, atlasImageCount)[:atlasImageCount]
|
||||
for i := range a.atlasImages {
|
||||
s := a.atlasSizes[i]
|
||||
var origWidth, origHeight int
|
||||
if a.atlasImages[i] != nil {
|
||||
origWidth = a.atlasImages[i].Bounds().Dx()
|
||||
origHeight = a.atlasImages[i].Bounds().Dy()
|
||||
if origWidth < s.X || origHeight < s.Y {
|
||||
a.atlasImages[i].Deallocate()
|
||||
a.atlasImages[i] = nil
|
||||
}
|
||||
}
|
||||
if a.atlasImages[i] != nil {
|
||||
a.atlasImages[i].Clear()
|
||||
} else {
|
||||
// Extend the bounds a little bit by roundUpAtlasSize to avoid creating an image too often.
|
||||
w := min(maxImageSize, max(roundUpAtlasSize(s.X), origWidth))
|
||||
h := min(maxImageSize, max(roundUpAtlasSize(s.Y), origHeight))
|
||||
a.atlasImages[i] = ebiten.NewImage(w, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *atlas) stencilBufferImageAt(i int, antialias bool, antialiasIndex int) *ebiten.Image {
|
||||
idx, ok := a.pathIndexToAtlasRegionIndex[i]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
ar := a.atlasRegions[idx]
|
||||
if ar.imageBounds.Empty() {
|
||||
return nil
|
||||
}
|
||||
|
||||
atlas := a.atlasImages[ar.imageIndex]
|
||||
b := ar.imageBounds
|
||||
if antialias {
|
||||
switch antialiasIndex {
|
||||
case 0:
|
||||
b = image.Rectangle{
|
||||
Min: b.Min,
|
||||
Max: image.Pt(b.Min.X+b.Dx()/2, b.Max.Y),
|
||||
}
|
||||
case 1:
|
||||
b = image.Rectangle{
|
||||
Min: image.Pt(b.Min.X+b.Dx()/2, b.Min.Y),
|
||||
Max: b.Max,
|
||||
}
|
||||
default:
|
||||
panic("not reached")
|
||||
}
|
||||
}
|
||||
return atlas.SubImage(b).(*ebiten.Image)
|
||||
}
|
||||
|
||||
func (a *atlas) pathRenderingPositionAt(i int) image.Point {
|
||||
return a.pathRenderingBounds[i].Min
|
||||
}
|
||||
Reference in New Issue
Block a user