mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 02:38:53 +01:00
parent
03a8aaee5c
commit
c0e41de921
@ -15,17 +15,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"log"
|
||||
"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/examples/resources/fonts"
|
||||
"github.com/hajimehoshi/ebiten/v2/text/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
@ -46,13 +44,8 @@ const (
|
||||
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 {
|
||||
segments sfnt.Segments
|
||||
bounds fixed.Rectangle26_6
|
||||
path vector.Path
|
||||
vertices []ebiten.Vertex
|
||||
indices []uint16
|
||||
|
||||
@ -60,83 +53,37 @@ type Game struct {
|
||||
}
|
||||
|
||||
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++
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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.indices = g.indices[:0]
|
||||
|
||||
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.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 {
|
||||
g.vertices[i].DstX += screenWidth/2 - fixed26_6ToFloat32(g.bounds.Max.X+g.bounds.Min.X)/2
|
||||
g.vertices[i].DstY += screenHeight/2 - fixed26_6ToFloat32(g.bounds.Max.Y+g.bounds.Min.Y)/2
|
||||
g.vertices[i].DstX += 50
|
||||
g.vertices[i].DstY += 50
|
||||
g.vertices[i].SrcX = 1
|
||||
g.vertices[i].SrcY = 1
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
var _ Face = (*GoTextFace)(nil)
|
||||
@ -286,14 +287,13 @@ func (g *GoTextFace) advance(text string) float64 {
|
||||
return fixed26_6ToFloat64(output.Advance)
|
||||
}
|
||||
|
||||
// appendGlyphs implements Face.
|
||||
func (g *GoTextFace) appendGlyphs(glyphs []Glyph, text string, indexOffset int, originX, originY float64) []Glyph {
|
||||
_, gs := g.Source.shape(text, g)
|
||||
|
||||
// appendGlyphsForLine implements Face.
|
||||
func (g *GoTextFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
|
||||
origin := fixed.Point26_6{
|
||||
X: float64ToFixed26_6(originX),
|
||||
Y: float64ToFixed26_6(originY),
|
||||
}
|
||||
_, gs := g.Source.shape(line, g)
|
||||
for _, glyph := range gs {
|
||||
img, imgX, imgY := g.glyphImage(glyph, origin)
|
||||
if img != nil {
|
||||
@ -344,6 +344,22 @@ func (g *GoTextFace) glyphImage(glyph glyph, origin fixed.Point26_6) (*ebiten.Im
|
||||
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.
|
||||
func (g *GoTextFace) direction() Direction {
|
||||
return g.Direction
|
||||
|
@ -21,9 +21,10 @@ import (
|
||||
|
||||
"github.com/go-text/typesetting/opentype/api"
|
||||
"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/vector"
|
||||
)
|
||||
|
||||
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)
|
||||
biasY := fixed26_6ToFloat32(-glyphBounds.Min.Y + subpixelOffset.Y)
|
||||
|
||||
rast := vector.NewRasterizer(w, h)
|
||||
rast := gvector.NewRasterizer(w, h)
|
||||
rast.DrawOp = draw.Src
|
||||
for _, seg := range segs {
|
||||
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{})
|
||||
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"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
// Align is the alignment that determines how to put a text.
|
||||
@ -125,13 +126,30 @@ func AppendGlyphs(glyphs []Glyph, text string, face Face, options *LayoutOptions
|
||||
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 assumes the text is rendered with the position (x, y).
|
||||
// (x, y) might affect the subpixel rendering results.
|
||||
func appendGlyphs(glyphs []Glyph, text string, face Face, x, y float64, options *LayoutOptions) []Glyph {
|
||||
forEachLine(text, face, options, func(line string, indexOffset int, originX, originY float64) {
|
||||
glyphs = face.appendGlyphsForLine(glyphs, line, indexOffset, originX+x, originY+y)
|
||||
})
|
||||
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 glyphs
|
||||
return
|
||||
}
|
||||
|
||||
if options == nil {
|
||||
@ -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 {
|
||||
break
|
||||
@ -253,8 +271,6 @@ func appendGlyphs(glyphs []Glyph, text string, face Face, x, y float64, options
|
||||
originX -= options.LineSpacingInPixels
|
||||
}
|
||||
}
|
||||
|
||||
return glyphs
|
||||
}
|
||||
|
||||
type horizontalAlign int
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"golang.org/x/image/math/fixed"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
var _ Face = (*StdFace)(nil)
|
||||
@ -84,8 +85,8 @@ func (s *StdFace) advance(text string) float64 {
|
||||
return fixed26_6ToFloat64(font.MeasureString(s.f, text))
|
||||
}
|
||||
|
||||
// appendGlyphs implements Face.
|
||||
func (s *StdFace) appendGlyphs(glyphs []Glyph, text string, indexOffset int, originX, originY float64) []Glyph {
|
||||
// appendGlyphsForLine implements Face.
|
||||
func (s *StdFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
|
||||
s.copyCheck()
|
||||
|
||||
origin := fixed.Point26_6{
|
||||
@ -94,7 +95,7 @@ func (s *StdFace) appendGlyphs(glyphs []Glyph, text string, indexOffset int, ori
|
||||
}
|
||||
prevR := rune(-1)
|
||||
|
||||
for i, r := range text {
|
||||
for i, r := range line {
|
||||
if prevR >= 0 {
|
||||
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 {
|
||||
// Adjust the position to the integers.
|
||||
// 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{
|
||||
StartIndexInBytes: indexOffset + i,
|
||||
EndIndexInBytes: indexOffset + i + size,
|
||||
@ -173,6 +174,10 @@ func (s *StdFace) direction() Direction {
|
||||
return DirectionLeftToRight
|
||||
}
|
||||
|
||||
// appendVectorPathForLine implements Face.
|
||||
func (s *StdFace) appendVectorPathForLine(path *vector.Path, text string, originX, originY float64) {
|
||||
}
|
||||
|
||||
// Metrics implelements Face.
|
||||
func (s *StdFace) private() {
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"golang.org/x/image/math/fixed"
|
||||
|
||||
"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.
|
||||
@ -38,7 +39,8 @@ type Face interface {
|
||||
|
||||
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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user