updated ebiten version from 2.7.9 to 2.9.9
This commit is contained in:
+358
-59
@@ -18,6 +18,8 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
"sync"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
@@ -27,6 +29,18 @@ var (
|
||||
whiteSubImage = whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
|
||||
)
|
||||
|
||||
var (
|
||||
theCachedVerticesForUtil []ebiten.Vertex
|
||||
theCachedIndicesForUtil []uint32
|
||||
theCacheForUtilM sync.Mutex
|
||||
)
|
||||
|
||||
func useCachedVerticesAndIndicesForUtil(fn func([]ebiten.Vertex, []uint32) (vs []ebiten.Vertex, is []uint32)) {
|
||||
theCacheForUtilM.Lock()
|
||||
defer theCacheForUtilM.Unlock()
|
||||
theCachedVerticesForUtil, theCachedIndicesForUtil = fn(theCachedVerticesForUtil[:0], theCachedIndicesForUtil[:0])
|
||||
}
|
||||
|
||||
func init() {
|
||||
b := whiteImage.Bounds()
|
||||
pix := make([]byte, 4*b.Dx()*b.Dy())
|
||||
@@ -37,88 +51,373 @@ func init() {
|
||||
whiteImage.WritePixels(pix)
|
||||
}
|
||||
|
||||
func drawVerticesForUtil(dst *ebiten.Image, vs []ebiten.Vertex, is []uint16, clr color.Color, antialias bool) {
|
||||
r, g, b, a := clr.RGBA()
|
||||
for i := range vs {
|
||||
vs[i].SrcX = 1
|
||||
vs[i].SrcY = 1
|
||||
vs[i].ColorR = float32(r) / 0xffff
|
||||
vs[i].ColorG = float32(g) / 0xffff
|
||||
vs[i].ColorB = float32(b) / 0xffff
|
||||
vs[i].ColorA = float32(a) / 0xffff
|
||||
// StrokeLine strokes a line (x0, y0)-(x1, y1) with the specified width and color.
|
||||
func StrokeLine(dst *ebiten.Image, x0, y0, x1, y1 float32, strokeWidth float32, clr color.Color, antialias bool) {
|
||||
if antialias {
|
||||
var path Path
|
||||
path.MoveTo(x0, y0)
|
||||
path.LineTo(x1, y1)
|
||||
strokeOp := &StrokeOptions{}
|
||||
strokeOp.Width = strokeWidth
|
||||
drawOp := &DrawPathOptions{}
|
||||
drawOp.AntiAlias = true
|
||||
drawOp.ColorScale.ScaleWithColor(clr)
|
||||
StrokePath(dst, &path, strokeOp, drawOp)
|
||||
return
|
||||
}
|
||||
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.ColorScaleMode = ebiten.ColorScaleModePremultipliedAlpha
|
||||
op.AntiAlias = antialias
|
||||
dst.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
// Use a regular DrawImage for batching.
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(math.Hypot(float64(x1-x0), float64(y1-y0)), float64(strokeWidth))
|
||||
op.GeoM.Translate(0, -float64(strokeWidth)/2)
|
||||
op.GeoM.Rotate(math.Atan2(float64(y1-y0), float64(x1-x0)))
|
||||
op.GeoM.Translate(float64(x0), float64(y0))
|
||||
op.ColorScale.ScaleWithColor(clr)
|
||||
dst.DrawImage(whiteSubImage, op)
|
||||
}
|
||||
|
||||
// StrokeLine strokes a line (x0, y0)-(x1, y1) with the specified width and color.
|
||||
//
|
||||
// clr has be to be a solid (non-transparent) color.
|
||||
func StrokeLine(dst *ebiten.Image, x0, y0, x1, y1 float32, strokeWidth float32, clr color.Color, antialias bool) {
|
||||
var path Path
|
||||
path.MoveTo(x0, y0)
|
||||
path.LineTo(x1, y1)
|
||||
strokeOp := &StrokeOptions{}
|
||||
strokeOp.Width = strokeWidth
|
||||
vs, is := path.AppendVerticesAndIndicesForStroke(nil, nil, strokeOp)
|
||||
// FillRect fills a rectangle with the specified width and color.
|
||||
func FillRect(dst *ebiten.Image, x, y, width, height float32, clr color.Color, antialias bool) {
|
||||
if antialias {
|
||||
var path Path
|
||||
path.MoveTo(x, y)
|
||||
path.LineTo(x, y+height)
|
||||
path.LineTo(x+width, y+height)
|
||||
path.LineTo(x+width, y)
|
||||
drawOp := &DrawPathOptions{}
|
||||
drawOp.AntiAlias = true
|
||||
drawOp.ColorScale.ScaleWithColor(clr)
|
||||
FillPath(dst, &path, nil, drawOp)
|
||||
return
|
||||
}
|
||||
|
||||
drawVerticesForUtil(dst, vs, is, clr, antialias)
|
||||
// Use a regular DrawImage for batching.
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(float64(width), float64(height))
|
||||
op.GeoM.Translate(float64(x), float64(y))
|
||||
op.ColorScale.ScaleWithColor(clr)
|
||||
dst.DrawImage(whiteSubImage, op)
|
||||
}
|
||||
|
||||
// DrawFilledRect fills a rectangle with the specified width and color.
|
||||
//
|
||||
// Deprecated: as of v2.9. Use [FillRect] instead.
|
||||
func DrawFilledRect(dst *ebiten.Image, x, y, width, height float32, clr color.Color, antialias bool) {
|
||||
var path Path
|
||||
path.MoveTo(x, y)
|
||||
path.LineTo(x, y+height)
|
||||
path.LineTo(x+width, y+height)
|
||||
path.LineTo(x+width, y)
|
||||
vs, is := path.AppendVerticesAndIndicesForFilling(nil, nil)
|
||||
|
||||
drawVerticesForUtil(dst, vs, is, clr, antialias)
|
||||
FillRect(dst, x, y, width, height, clr, antialias)
|
||||
}
|
||||
|
||||
// StrokeRect strokes a rectangle with the specified width and color.
|
||||
//
|
||||
// clr has be to be a solid (non-transparent) color.
|
||||
func StrokeRect(dst *ebiten.Image, x, y, width, height float32, strokeWidth float32, clr color.Color, antialias bool) {
|
||||
var path Path
|
||||
path.MoveTo(x, y)
|
||||
path.LineTo(x, y+height)
|
||||
path.LineTo(x+width, y+height)
|
||||
path.LineTo(x+width, y)
|
||||
path.Close()
|
||||
if antialias {
|
||||
var path Path
|
||||
path.MoveTo(x, y)
|
||||
path.LineTo(x, y+height)
|
||||
path.LineTo(x+width, y+height)
|
||||
path.LineTo(x+width, y)
|
||||
path.Close()
|
||||
strokeOp := &StrokeOptions{}
|
||||
strokeOp.Width = strokeWidth
|
||||
strokeOp.MiterLimit = 10
|
||||
drawOp := &DrawPathOptions{}
|
||||
drawOp.AntiAlias = true
|
||||
drawOp.ColorScale.ScaleWithColor(clr)
|
||||
StrokePath(dst, &path, strokeOp, drawOp)
|
||||
return
|
||||
}
|
||||
|
||||
strokeOp := &StrokeOptions{}
|
||||
strokeOp.Width = strokeWidth
|
||||
strokeOp.MiterLimit = 10
|
||||
vs, is := path.AppendVerticesAndIndicesForStroke(nil, nil, strokeOp)
|
||||
if strokeWidth <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
drawVerticesForUtil(dst, vs, is, clr, antialias)
|
||||
if strokeWidth >= width || strokeWidth >= height {
|
||||
FillRect(dst, x-strokeWidth/2, y-strokeWidth/2, width+strokeWidth, height+strokeWidth, clr, false)
|
||||
return
|
||||
}
|
||||
|
||||
// Use a regular DrawImage for batching.
|
||||
{
|
||||
// Render the top side.
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(float64(width+strokeWidth), float64(strokeWidth))
|
||||
op.GeoM.Translate(float64(x-strokeWidth/2), float64(y-strokeWidth/2))
|
||||
op.ColorScale.ScaleWithColor(clr)
|
||||
dst.DrawImage(whiteSubImage, op)
|
||||
}
|
||||
{
|
||||
// Render the left side.
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(float64(strokeWidth), float64(height-strokeWidth))
|
||||
op.GeoM.Translate(float64(x-strokeWidth/2), float64(y+strokeWidth/2))
|
||||
op.ColorScale.ScaleWithColor(clr)
|
||||
dst.DrawImage(whiteSubImage, op)
|
||||
}
|
||||
{
|
||||
// Render the right side.
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(float64(strokeWidth), float64(height-strokeWidth))
|
||||
op.GeoM.Translate(float64(x+width-strokeWidth/2), float64(y+strokeWidth/2))
|
||||
op.ColorScale.ScaleWithColor(clr)
|
||||
dst.DrawImage(whiteSubImage, op)
|
||||
}
|
||||
{
|
||||
// Render the bottom side.
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Scale(float64(width+strokeWidth), float64(strokeWidth))
|
||||
op.GeoM.Translate(float64(x-strokeWidth/2), float64(y+height-strokeWidth/2))
|
||||
op.ColorScale.ScaleWithColor(clr)
|
||||
dst.DrawImage(whiteSubImage, op)
|
||||
}
|
||||
}
|
||||
|
||||
// FillCircle fills a circle with the specified center position (cx, cy), the radius (r), width and color.
|
||||
func FillCircle(dst *ebiten.Image, cx, cy, r float32, clr color.Color, antialias bool) {
|
||||
if antialias {
|
||||
var path Path
|
||||
path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise)
|
||||
drawOp := &DrawPathOptions{}
|
||||
drawOp.AntiAlias = true
|
||||
drawOp.ColorScale.ScaleWithColor(clr)
|
||||
FillPath(dst, &path, nil, drawOp)
|
||||
return
|
||||
}
|
||||
|
||||
// Use a regular DrawTriangles32 for batching.
|
||||
cr, cg, cb, ca := clr.RGBA()
|
||||
crf := float32(cr) / 0xffff
|
||||
cgf := float32(cg) / 0xffff
|
||||
cbf := float32(cb) / 0xffff
|
||||
caf := float32(ca) / 0xffff
|
||||
useCachedVerticesAndIndicesForUtil(func(vs []ebiten.Vertex, is []uint32) ([]ebiten.Vertex, []uint32) {
|
||||
count := int(math.Ceil(math.Pi * float64(r)))
|
||||
for i := range count {
|
||||
angle := float64(i) * (2 * math.Pi / float64(count))
|
||||
sin, cos := math.Sincos(angle)
|
||||
x := cx + r*float32(cos)
|
||||
y := cy + r*float32(sin)
|
||||
vs = append(vs, ebiten.Vertex{
|
||||
DstX: x,
|
||||
DstY: y,
|
||||
SrcX: 1,
|
||||
SrcY: 1,
|
||||
ColorR: crf,
|
||||
ColorG: cgf,
|
||||
ColorB: cbf,
|
||||
ColorA: caf,
|
||||
})
|
||||
if i > 1 {
|
||||
idx := uint32(len(vs))
|
||||
is = append(is, 0, idx-1, idx-2)
|
||||
}
|
||||
}
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.ColorScaleMode = ebiten.ColorScaleModePremultipliedAlpha
|
||||
dst.DrawTriangles32(vs, is, whiteSubImage, op)
|
||||
return vs, is
|
||||
})
|
||||
}
|
||||
|
||||
// DrawFilledCircle fills a circle with the specified center position (cx, cy), the radius (r), width and color.
|
||||
//
|
||||
// Deprecated: as of v2.9. Use [FillCircle] instead.
|
||||
func DrawFilledCircle(dst *ebiten.Image, cx, cy, r float32, clr color.Color, antialias bool) {
|
||||
var path Path
|
||||
path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise)
|
||||
vs, is := path.AppendVerticesAndIndicesForFilling(nil, nil)
|
||||
|
||||
drawVerticesForUtil(dst, vs, is, clr, antialias)
|
||||
FillCircle(dst, cx, cy, r, clr, antialias)
|
||||
}
|
||||
|
||||
// StrokeCircle strokes a circle with the specified center position (cx, cy), the radius (r), width and color.
|
||||
//
|
||||
// clr has be to be a solid (non-transparent) color.
|
||||
func StrokeCircle(dst *ebiten.Image, cx, cy, r float32, strokeWidth float32, clr color.Color, antialias bool) {
|
||||
var path Path
|
||||
path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise)
|
||||
path.Close()
|
||||
if antialias {
|
||||
var path Path
|
||||
path.Arc(cx, cy, r, 0, 2*math.Pi, Clockwise)
|
||||
path.Close()
|
||||
strokeOp := &StrokeOptions{}
|
||||
strokeOp.Width = strokeWidth
|
||||
strokeOp.LineJoin = LineJoinRound
|
||||
drawOp := &DrawPathOptions{}
|
||||
drawOp.AntiAlias = true
|
||||
drawOp.ColorScale.ScaleWithColor(clr)
|
||||
StrokePath(dst, &path, strokeOp, drawOp)
|
||||
return
|
||||
}
|
||||
|
||||
strokeOp := &StrokeOptions{}
|
||||
strokeOp.Width = strokeWidth
|
||||
vs, is := path.AppendVerticesAndIndicesForStroke(nil, nil, strokeOp)
|
||||
if strokeWidth <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
drawVerticesForUtil(dst, vs, is, clr, antialias)
|
||||
if strokeWidth >= r {
|
||||
FillCircle(dst, cx, cy, r+strokeWidth/2, clr, false)
|
||||
return
|
||||
}
|
||||
|
||||
// Use a regular DrawTriangles32 for batching.
|
||||
cr, cg, cb, ca := clr.RGBA()
|
||||
crf := float32(cr) / 0xffff
|
||||
cgf := float32(cg) / 0xffff
|
||||
cbf := float32(cb) / 0xffff
|
||||
caf := float32(ca) / 0xffff
|
||||
useCachedVerticesAndIndicesForUtil(func(vs []ebiten.Vertex, is []uint32) ([]ebiten.Vertex, []uint32) {
|
||||
count := int(math.Ceil(math.Pi * float64(r+strokeWidth/2)))
|
||||
for i := range count {
|
||||
angle := float64(i) * (2 * math.Pi / float64(count))
|
||||
sin, cos := math.Sincos(angle)
|
||||
x0 := cx + (r+strokeWidth/2)*float32(cos)
|
||||
y0 := cy + (r+strokeWidth/2)*float32(sin)
|
||||
vs = append(vs, ebiten.Vertex{
|
||||
DstX: x0,
|
||||
DstY: y0,
|
||||
SrcX: 1,
|
||||
SrcY: 1,
|
||||
ColorR: crf,
|
||||
ColorG: cgf,
|
||||
ColorB: cbf,
|
||||
ColorA: caf,
|
||||
})
|
||||
x1 := cx + (r-strokeWidth/2)*float32(cos)
|
||||
y1 := cy + (r-strokeWidth/2)*float32(sin)
|
||||
vs = append(vs, ebiten.Vertex{
|
||||
DstX: x1,
|
||||
DstY: y1,
|
||||
SrcX: 1,
|
||||
SrcY: 1,
|
||||
ColorR: crf,
|
||||
ColorG: cgf,
|
||||
ColorB: cbf,
|
||||
ColorA: caf,
|
||||
})
|
||||
idx := uint32(2 * i)
|
||||
total := uint32(2 * count)
|
||||
is = append(is, idx, idx+1, (idx+2)%total, idx+1, (idx+2)%total, (idx+3)%total)
|
||||
}
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.ColorScaleMode = ebiten.ColorScaleModePremultipliedAlpha
|
||||
dst.DrawTriangles32(vs, is, whiteSubImage, op)
|
||||
return vs, is
|
||||
})
|
||||
}
|
||||
|
||||
// FillRule is the rule whether an overlapped region is rendered or not.
|
||||
type FillRule int
|
||||
|
||||
const (
|
||||
// FillRuleNonZero means that triangles are rendered based on the non-zero rule.
|
||||
// If and only if the number of overlaps is not 0, the region is rendered.
|
||||
FillRuleNonZero FillRule = iota
|
||||
|
||||
// FillRuleEvenOdd means that triangles are rendered based on the even-odd rule.
|
||||
// If and only if the number of overlaps is odd, the region is rendered.
|
||||
FillRuleEvenOdd
|
||||
)
|
||||
|
||||
var (
|
||||
theCallbackTokens = map[*ebiten.Image]int64{}
|
||||
theFillPathsStates = map[*ebiten.Image]*fillPathsState{}
|
||||
theFillPathsStatesPool = sync.Pool{
|
||||
New: func() any {
|
||||
return &fillPathsState{}
|
||||
},
|
||||
}
|
||||
theFillPathM sync.Mutex
|
||||
)
|
||||
|
||||
// FillOptions is options to fill a path.
|
||||
type FillOptions struct {
|
||||
// FillRule is the rule whether an overlapped region is rendered or not.
|
||||
// The default (zero) value is FillRuleNonZero.
|
||||
FillRule FillRule
|
||||
}
|
||||
|
||||
// DrawPathOptions is options to draw a path.
|
||||
type DrawPathOptions struct {
|
||||
// AntiAlias is whether the path is drawn with anti-aliasing.
|
||||
// The default (zero) value is false.
|
||||
AntiAlias bool
|
||||
|
||||
// ColorScale is the color scale to apply to the path.
|
||||
// The default (zero) value is identity, which is (1, 1, 1, 1) (white).
|
||||
ColorScale ebiten.ColorScale
|
||||
|
||||
// Blend is the blend mode to apply to the path.
|
||||
// The default (zero) value is ebiten.BlendSourceOver.
|
||||
Blend ebiten.Blend
|
||||
}
|
||||
|
||||
// FillPath fills the specified path with the specified options.
|
||||
func FillPath(dst *ebiten.Image, path *Path, fillOptions *FillOptions, drawPathOptions *DrawPathOptions) {
|
||||
if drawPathOptions == nil {
|
||||
drawPathOptions = &DrawPathOptions{}
|
||||
}
|
||||
if fillOptions == nil {
|
||||
fillOptions = &FillOptions{}
|
||||
}
|
||||
|
||||
bounds := dst.Bounds()
|
||||
|
||||
// Get the original image if dst is a sub-image to integrate the callbacks.
|
||||
dst = originalImage(dst)
|
||||
|
||||
theFillPathM.Lock()
|
||||
defer theFillPathM.Unlock()
|
||||
|
||||
// Remove the previous registered callbacks.
|
||||
if token, ok := theCallbackTokens[dst]; ok {
|
||||
removeUsageCallback(dst, token)
|
||||
}
|
||||
delete(theCallbackTokens, dst)
|
||||
|
||||
if _, ok := theFillPathsStates[dst]; !ok {
|
||||
theFillPathsStates[dst] = theFillPathsStatesPool.Get().(*fillPathsState)
|
||||
}
|
||||
s := theFillPathsStates[dst]
|
||||
if s.antialias != drawPathOptions.AntiAlias || s.blend != drawPathOptions.Blend || s.fillRule != fillOptions.FillRule {
|
||||
s.fillPaths(dst)
|
||||
s.reset()
|
||||
}
|
||||
s.antialias = drawPathOptions.AntiAlias
|
||||
s.blend = drawPathOptions.Blend
|
||||
s.fillRule = fillOptions.FillRule
|
||||
s.addPath(path, bounds, drawPathOptions.ColorScale)
|
||||
|
||||
// Use an independent callback function to avoid unexpected captures.
|
||||
theCallbackTokens[dst] = addUsageCallback(dst, fillPathCallback)
|
||||
}
|
||||
|
||||
func fillPathCallback(dst *ebiten.Image) {
|
||||
if originalImage(dst) != dst {
|
||||
panic("vector: dst must be the original image")
|
||||
}
|
||||
|
||||
theFillPathM.Lock()
|
||||
defer theFillPathM.Unlock()
|
||||
|
||||
// Remove the callback not to call this twice.
|
||||
if token, ok := theCallbackTokens[dst]; ok {
|
||||
removeUsageCallback(dst, token)
|
||||
}
|
||||
delete(theCallbackTokens, dst)
|
||||
|
||||
s, ok := theFillPathsStates[dst]
|
||||
if !ok {
|
||||
panic("vector: fillPathsState must exist here")
|
||||
}
|
||||
s.fillPaths(dst)
|
||||
s.reset()
|
||||
delete(theFillPathsStates, dst)
|
||||
theFillPathsStatesPool.Put(s)
|
||||
}
|
||||
|
||||
// StrokePath strokes the specified path with the specified options.
|
||||
func StrokePath(dst *ebiten.Image, path *Path, strokeOptions *StrokeOptions, drawPathOptions *DrawPathOptions) {
|
||||
var stroke Path
|
||||
op := &AddStrokeOptions{}
|
||||
op.StrokeOptions = *strokeOptions
|
||||
stroke.AddStroke(path, op)
|
||||
FillPath(dst, &stroke, nil, drawPathOptions)
|
||||
}
|
||||
|
||||
//go:linkname originalImage github.com/hajimehoshi/ebiten/v2.originalImage
|
||||
func originalImage(img *ebiten.Image) *ebiten.Image
|
||||
|
||||
//go:linkname addUsageCallback github.com/hajimehoshi/ebiten/v2.addUsageCallback
|
||||
func addUsageCallback(img *ebiten.Image, fn func(img *ebiten.Image)) int64
|
||||
|
||||
//go:linkname removeUsageCallback github.com/hajimehoshi/ebiten/v2.removeUsageCallback
|
||||
func removeUsageCallback(img *ebiten.Image, token int64)
|
||||
|
||||
Reference in New Issue
Block a user