mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-13 22:47:26 +01:00
parent
03a8aaee5c
commit
c0e41de921
@ -15,17 +15,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"golang.org/x/image/font"
|
|
||||||
"golang.org/x/image/font/sfnt"
|
|
||||||
"golang.org/x/image/math/fixed"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/text/v2"
|
||||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,13 +44,8 @@ const (
|
|||||||
screenHeight = 480
|
screenHeight = 480
|
||||||
)
|
)
|
||||||
|
|
||||||
func fixed26_6ToFloat32(x fixed.Int26_6) float32 {
|
|
||||||
return float32(x>>6) + float32(x&((1<<6)-1))/float32(1<<6)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
segments sfnt.Segments
|
path vector.Path
|
||||||
bounds fixed.Rectangle26_6
|
|
||||||
vertices []ebiten.Vertex
|
vertices []ebiten.Vertex
|
||||||
indices []uint16
|
indices []uint16
|
||||||
|
|
||||||
@ -60,83 +53,37 @@ type Game struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Update() error {
|
func (g *Game) Update() error {
|
||||||
|
if g.tick == 0 {
|
||||||
|
s, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
op := &text.LayoutOptions{}
|
||||||
|
op.LineSpacingInPixels = 100
|
||||||
|
text.AppendVectorPath(&g.path, "あいうえお\nかきくけこ", &text.GoTextFace{
|
||||||
|
Source: s,
|
||||||
|
Size: 100,
|
||||||
|
}, op)
|
||||||
|
}
|
||||||
|
|
||||||
g.tick++
|
g.tick++
|
||||||
|
|
||||||
if g.segments == nil {
|
|
||||||
ppem := fixed.I(300)
|
|
||||||
|
|
||||||
f, err := sfnt.Parse(fonts.MPlus1pRegular_ttf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var b sfnt.Buffer
|
|
||||||
idx, err := f.GlyphIndex(&b, 'あ')
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
segments, err := f.LoadGlyph(&b, idx, ppem, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
g.segments = segments
|
|
||||||
|
|
||||||
bounds, err := f.Bounds(&b, ppem, font.HintingNone)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
g.bounds = bounds
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Draw(screen *ebiten.Image) {
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
var path vector.Path
|
|
||||||
for _, seg := range g.segments {
|
|
||||||
switch seg.Op {
|
|
||||||
case sfnt.SegmentOpMoveTo:
|
|
||||||
path.MoveTo(
|
|
||||||
fixed26_6ToFloat32(seg.Args[0].X),
|
|
||||||
fixed26_6ToFloat32(seg.Args[0].Y),
|
|
||||||
)
|
|
||||||
case sfnt.SegmentOpLineTo:
|
|
||||||
path.LineTo(
|
|
||||||
fixed26_6ToFloat32(seg.Args[0].X),
|
|
||||||
fixed26_6ToFloat32(seg.Args[0].Y),
|
|
||||||
)
|
|
||||||
case sfnt.SegmentOpQuadTo:
|
|
||||||
path.QuadTo(
|
|
||||||
fixed26_6ToFloat32(seg.Args[0].X),
|
|
||||||
fixed26_6ToFloat32(seg.Args[0].Y),
|
|
||||||
fixed26_6ToFloat32(seg.Args[1].X),
|
|
||||||
fixed26_6ToFloat32(seg.Args[1].Y),
|
|
||||||
)
|
|
||||||
case sfnt.SegmentOpCubeTo:
|
|
||||||
path.CubicTo(
|
|
||||||
fixed26_6ToFloat32(seg.Args[0].X),
|
|
||||||
fixed26_6ToFloat32(seg.Args[0].Y),
|
|
||||||
fixed26_6ToFloat32(seg.Args[1].X),
|
|
||||||
fixed26_6ToFloat32(seg.Args[1].Y),
|
|
||||||
fixed26_6ToFloat32(seg.Args[2].X),
|
|
||||||
fixed26_6ToFloat32(seg.Args[2].Y),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
path.Close()
|
|
||||||
|
|
||||||
g.vertices = g.vertices[:0]
|
g.vertices = g.vertices[:0]
|
||||||
g.indices = g.indices[:0]
|
g.indices = g.indices[:0]
|
||||||
|
|
||||||
op := &vector.StrokeOptions{}
|
op := &vector.StrokeOptions{}
|
||||||
op.Width = 7*(float32(math.Sin(float64(g.tick)*2*math.Pi/180))+1) + 1
|
op.Width = 2*(float32(math.Sin(float64(g.tick)*2*math.Pi/180))+1) + 1
|
||||||
op.LineJoin = vector.LineJoinRound
|
op.LineJoin = vector.LineJoinRound
|
||||||
op.LineCap = vector.LineCapRound
|
op.LineCap = vector.LineCapRound
|
||||||
g.vertices, g.indices = path.AppendVerticesAndIndicesForStroke(g.vertices, g.indices, op)
|
g.vertices, g.indices = g.path.AppendVerticesAndIndicesForStroke(g.vertices, g.indices, op)
|
||||||
|
|
||||||
for i := range g.vertices {
|
for i := range g.vertices {
|
||||||
g.vertices[i].DstX += screenWidth/2 - fixed26_6ToFloat32(g.bounds.Max.X+g.bounds.Min.X)/2
|
g.vertices[i].DstX += 50
|
||||||
g.vertices[i].DstY += screenHeight/2 - fixed26_6ToFloat32(g.bounds.Max.Y+g.bounds.Min.Y)/2
|
g.vertices[i].DstY += 50
|
||||||
g.vertices[i].SrcX = 1
|
g.vertices[i].SrcX = 1
|
||||||
g.vertices[i].SrcY = 1
|
g.vertices[i].SrcY = 1
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Face = (*GoTextFace)(nil)
|
var _ Face = (*GoTextFace)(nil)
|
||||||
@ -286,14 +287,13 @@ func (g *GoTextFace) advance(text string) float64 {
|
|||||||
return fixed26_6ToFloat64(output.Advance)
|
return fixed26_6ToFloat64(output.Advance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendGlyphs implements Face.
|
// appendGlyphsForLine implements Face.
|
||||||
func (g *GoTextFace) appendGlyphs(glyphs []Glyph, text string, indexOffset int, originX, originY float64) []Glyph {
|
func (g *GoTextFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
|
||||||
_, gs := g.Source.shape(text, g)
|
|
||||||
|
|
||||||
origin := fixed.Point26_6{
|
origin := fixed.Point26_6{
|
||||||
X: float64ToFixed26_6(originX),
|
X: float64ToFixed26_6(originX),
|
||||||
Y: float64ToFixed26_6(originY),
|
Y: float64ToFixed26_6(originY),
|
||||||
}
|
}
|
||||||
|
_, gs := g.Source.shape(line, g)
|
||||||
for _, glyph := range gs {
|
for _, glyph := range gs {
|
||||||
img, imgX, imgY := g.glyphImage(glyph, origin)
|
img, imgX, imgY := g.glyphImage(glyph, origin)
|
||||||
if img != nil {
|
if img != nil {
|
||||||
@ -344,6 +344,22 @@ func (g *GoTextFace) glyphImage(glyph glyph, origin fixed.Point26_6) (*ebiten.Im
|
|||||||
return img, imgX, imgY
|
return img, imgX, imgY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendVectorPathForLine implements Face.
|
||||||
|
func (g *GoTextFace) appendVectorPathForLine(path *vector.Path, line string, originX, originY float64) {
|
||||||
|
origin := fixed.Point26_6{
|
||||||
|
X: float64ToFixed26_6(originX),
|
||||||
|
Y: float64ToFixed26_6(originY),
|
||||||
|
}
|
||||||
|
_, gs := g.Source.shape(line, g)
|
||||||
|
for _, glyph := range gs {
|
||||||
|
appendVectorPathFromSegments(path, glyph.scaledSegments, fixed26_6ToFloat32(origin.X), fixed26_6ToFloat32(origin.Y))
|
||||||
|
origin = origin.Add(fixed.Point26_6{
|
||||||
|
X: glyph.shapingGlyph.XAdvance,
|
||||||
|
Y: -glyph.shapingGlyph.YAdvance,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// direction implements Face.
|
// direction implements Face.
|
||||||
func (g *GoTextFace) direction() Direction {
|
func (g *GoTextFace) direction() Direction {
|
||||||
return g.Direction
|
return g.Direction
|
||||||
|
@ -21,9 +21,10 @@ import (
|
|||||||
|
|
||||||
"github.com/go-text/typesetting/opentype/api"
|
"github.com/go-text/typesetting/opentype/api"
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
"golang.org/x/image/vector"
|
gvector "golang.org/x/image/vector"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
)
|
)
|
||||||
|
|
||||||
func segmentsToBounds(segs []api.Segment) fixed.Rectangle26_6 {
|
func segmentsToBounds(segs []api.Segment) fixed.Rectangle26_6 {
|
||||||
@ -94,7 +95,7 @@ func segmentsToImage(segs []api.Segment, subpixelOffset fixed.Point26_6, glyphBo
|
|||||||
biasX := fixed26_6ToFloat32(-glyphBounds.Min.X + subpixelOffset.X)
|
biasX := fixed26_6ToFloat32(-glyphBounds.Min.X + subpixelOffset.X)
|
||||||
biasY := fixed26_6ToFloat32(-glyphBounds.Min.Y + subpixelOffset.Y)
|
biasY := fixed26_6ToFloat32(-glyphBounds.Min.Y + subpixelOffset.Y)
|
||||||
|
|
||||||
rast := vector.NewRasterizer(w, h)
|
rast := gvector.NewRasterizer(w, h)
|
||||||
rast.DrawOp = draw.Src
|
rast.DrawOp = draw.Src
|
||||||
for _, seg := range segs {
|
for _, seg := range segs {
|
||||||
switch seg.Op {
|
switch seg.Op {
|
||||||
@ -120,3 +121,26 @@ func segmentsToImage(segs []api.Segment, subpixelOffset fixed.Point26_6, glyphBo
|
|||||||
rast.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
|
rast.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
|
||||||
return ebiten.NewImageFromImage(dst)
|
return ebiten.NewImageFromImage(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendVectorPathFromSegments(path *vector.Path, segs []api.Segment, x, y float32) {
|
||||||
|
for _, seg := range segs {
|
||||||
|
switch seg.Op {
|
||||||
|
case api.SegmentOpMoveTo:
|
||||||
|
path.MoveTo(seg.Args[0].X+x, seg.Args[0].Y+y)
|
||||||
|
case api.SegmentOpLineTo:
|
||||||
|
path.LineTo(seg.Args[0].X+x, seg.Args[0].Y+y)
|
||||||
|
case api.SegmentOpQuadTo:
|
||||||
|
path.QuadTo(
|
||||||
|
seg.Args[0].X+x, seg.Args[0].Y+y,
|
||||||
|
seg.Args[1].X+x, seg.Args[1].Y+y,
|
||||||
|
)
|
||||||
|
case api.SegmentOpCubeTo:
|
||||||
|
path.CubicTo(
|
||||||
|
seg.Args[0].X+x, seg.Args[0].Y+y,
|
||||||
|
seg.Args[1].X+x, seg.Args[1].Y+y,
|
||||||
|
seg.Args[2].X+x, seg.Args[2].Y+y,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path.Close()
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Align is the alignment that determines how to put a text.
|
// Align is the alignment that determines how to put a text.
|
||||||
@ -125,15 +126,32 @@ func AppendGlyphs(glyphs []Glyph, text string, face Face, options *LayoutOptions
|
|||||||
return appendGlyphs(glyphs, text, face, 0, 0, options)
|
return appendGlyphs(glyphs, text, face, 0, 0, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AppndVectorPath appends a vector path for glyphs to the given path.
|
||||||
|
//
|
||||||
|
// AppendVectorPath works only when face is *GoTextFace so far. For other types, AppendVectorPath does nothing.
|
||||||
|
func AppendVectorPath(path *vector.Path, text string, face Face, options *LayoutOptions) {
|
||||||
|
forEachLine(text, face, options, func(line string, indexOffset int, originX, originY float64) {
|
||||||
|
face.appendVectorPathForLine(path, line, originX, originY)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// appendGlyphs appends glyphs to the given slice and returns a slice.
|
// appendGlyphs appends glyphs to the given slice and returns a slice.
|
||||||
//
|
//
|
||||||
// appendGlyphs assumes the text is rendered with the position (x, y).
|
// appendGlyphs assumes the text is rendered with the position (x, y).
|
||||||
// (x, y) might affect the subpixel rendering results.
|
// (x, y) might affect the subpixel rendering results.
|
||||||
func appendGlyphs(glyphs []Glyph, text string, face Face, x, y float64, options *LayoutOptions) []Glyph {
|
func appendGlyphs(glyphs []Glyph, text string, face Face, x, y float64, options *LayoutOptions) []Glyph {
|
||||||
if text == "" {
|
forEachLine(text, face, options, func(line string, indexOffset int, originX, originY float64) {
|
||||||
|
glyphs = face.appendGlyphsForLine(glyphs, line, indexOffset, originX+x, originY+y)
|
||||||
|
})
|
||||||
return glyphs
|
return glyphs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// forEachLine interates lines.
|
||||||
|
func forEachLine(text string, face Face, options *LayoutOptions, f func(text string, indexOffset int, originX, originY float64)) {
|
||||||
|
if text == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if options == nil {
|
if options == nil {
|
||||||
options = &LayoutOptions{}
|
options = &LayoutOptions{}
|
||||||
}
|
}
|
||||||
@ -232,7 +250,7 @@ func appendGlyphs(glyphs []Glyph, text string, face Face, x, y float64, options
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glyphs = face.appendGlyphs(glyphs, line, indexOffset, originX+offsetX+x, originY+offsetY+y)
|
f(line, indexOffset, originX+offsetX, originY+offsetY)
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
break
|
break
|
||||||
@ -253,8 +271,6 @@ func appendGlyphs(glyphs []Glyph, text string, face Face, x, y float64, options
|
|||||||
originX -= options.LineSpacingInPixels
|
originX -= options.LineSpacingInPixels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return glyphs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type horizontalAlign int
|
type horizontalAlign int
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Face = (*StdFace)(nil)
|
var _ Face = (*StdFace)(nil)
|
||||||
@ -84,8 +85,8 @@ func (s *StdFace) advance(text string) float64 {
|
|||||||
return fixed26_6ToFloat64(font.MeasureString(s.f, text))
|
return fixed26_6ToFloat64(font.MeasureString(s.f, text))
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendGlyphs implements Face.
|
// appendGlyphsForLine implements Face.
|
||||||
func (s *StdFace) appendGlyphs(glyphs []Glyph, text string, indexOffset int, originX, originY float64) []Glyph {
|
func (s *StdFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
|
||||||
s.copyCheck()
|
s.copyCheck()
|
||||||
|
|
||||||
origin := fixed.Point26_6{
|
origin := fixed.Point26_6{
|
||||||
@ -94,7 +95,7 @@ func (s *StdFace) appendGlyphs(glyphs []Glyph, text string, indexOffset int, ori
|
|||||||
}
|
}
|
||||||
prevR := rune(-1)
|
prevR := rune(-1)
|
||||||
|
|
||||||
for i, r := range text {
|
for i, r := range line {
|
||||||
if prevR >= 0 {
|
if prevR >= 0 {
|
||||||
origin.X += s.f.Kern(prevR, r)
|
origin.X += s.f.Kern(prevR, r)
|
||||||
}
|
}
|
||||||
@ -102,7 +103,7 @@ func (s *StdFace) appendGlyphs(glyphs []Glyph, text string, indexOffset int, ori
|
|||||||
if img != nil {
|
if img != nil {
|
||||||
// Adjust the position to the integers.
|
// Adjust the position to the integers.
|
||||||
// The current glyph images assume that they are rendered on integer positions so far.
|
// The current glyph images assume that they are rendered on integer positions so far.
|
||||||
_, size := utf8.DecodeRuneInString(text[i:])
|
_, size := utf8.DecodeRuneInString(line[i:])
|
||||||
glyphs = append(glyphs, Glyph{
|
glyphs = append(glyphs, Glyph{
|
||||||
StartIndexInBytes: indexOffset + i,
|
StartIndexInBytes: indexOffset + i,
|
||||||
EndIndexInBytes: indexOffset + i + size,
|
EndIndexInBytes: indexOffset + i + size,
|
||||||
@ -173,6 +174,10 @@ func (s *StdFace) direction() Direction {
|
|||||||
return DirectionLeftToRight
|
return DirectionLeftToRight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// appendVectorPathForLine implements Face.
|
||||||
|
func (s *StdFace) appendVectorPathForLine(path *vector.Path, text string, originX, originY float64) {
|
||||||
|
}
|
||||||
|
|
||||||
// Metrics implelements Face.
|
// Metrics implelements Face.
|
||||||
func (s *StdFace) private() {
|
func (s *StdFace) private() {
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Face is an interface representing a font face. The implementations are only GoTextFace and StdFace.
|
// Face is an interface representing a font face. The implementations are only GoTextFace and StdFace.
|
||||||
@ -38,7 +39,8 @@ type Face interface {
|
|||||||
|
|
||||||
advance(text string) float64
|
advance(text string) float64
|
||||||
|
|
||||||
appendGlyphs(glyphs []Glyph, text string, indexOffset int, originX, originY float64) []Glyph
|
appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph
|
||||||
|
appendVectorPathForLine(path *vector.Path, text string, originX, originY float64)
|
||||||
|
|
||||||
direction() Direction
|
direction() Direction
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user