mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
parent
bf7acd54bb
commit
26744b46ff
@ -51,6 +51,9 @@ func runImageImportCheck(pass *analysis.Pass) (any, error) {
|
|||||||
if strings.HasSuffix(pkgPath, "_test") {
|
if strings.HasSuffix(pkgPath, "_test") {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
if pkgPath == "github.com/hajimehoshi/ebiten/v2/text/v2" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Remove this exception after v3 is released (#2336).
|
// TODO: Remove this exception after v3 is released (#2336).
|
||||||
if pkgPath == "github.com/hajimehoshi/ebiten/v2/ebitenutil" {
|
if pkgPath == "github.com/hajimehoshi/ebiten/v2/ebitenutil" {
|
||||||
|
@ -328,6 +328,7 @@ func (g *GoTextFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffse
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GoTextFace) glyphImage(glyph glyph, origin fixed.Point26_6) (*ebiten.Image, int, int) {
|
func (g *GoTextFace) glyphImage(glyph glyph, origin fixed.Point26_6) (*ebiten.Image, int, int) {
|
||||||
|
if glyph.bitmap != nil {
|
||||||
if g.direction().isHorizontal() {
|
if g.direction().isHorizontal() {
|
||||||
origin.X = adjustGranularity(origin.X, g)
|
origin.X = adjustGranularity(origin.X, g)
|
||||||
origin.Y &^= ((1 << 6) - 1)
|
origin.Y &^= ((1 << 6) - 1)
|
||||||
@ -335,12 +336,20 @@ func (g *GoTextFace) glyphImage(glyph glyph, origin fixed.Point26_6) (*ebiten.Im
|
|||||||
origin.X &^= ((1 << 6) - 1)
|
origin.X &^= ((1 << 6) - 1)
|
||||||
origin.Y = adjustGranularity(origin.Y, g)
|
origin.Y = adjustGranularity(origin.Y, g)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
origin.X &^= ((1 << 6) - 1)
|
||||||
|
origin.Y &^= ((1 << 6) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
b := glyph.bounds
|
b := glyph.bounds
|
||||||
subpixelOffset := fixed.Point26_6{
|
|
||||||
|
var subpixelOffset fixed.Point26_6
|
||||||
|
if glyph.bitmap != nil {
|
||||||
|
subpixelOffset = fixed.Point26_6{
|
||||||
X: (origin.X + b.Min.X) & ((1 << 6) - 1),
|
X: (origin.X + b.Min.X) & ((1 << 6) - 1),
|
||||||
Y: (origin.Y + b.Min.Y) & ((1 << 6) - 1),
|
Y: (origin.Y + b.Min.Y) & ((1 << 6) - 1),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
key := goTextGlyphImageCacheKey{
|
key := goTextGlyphImageCacheKey{
|
||||||
gid: glyph.shapingGlyph.GlyphID,
|
gid: glyph.shapingGlyph.GlyphID,
|
||||||
xoffset: subpixelOffset.X,
|
xoffset: subpixelOffset.X,
|
||||||
@ -348,6 +357,9 @@ func (g *GoTextFace) glyphImage(glyph glyph, origin fixed.Point26_6) (*ebiten.Im
|
|||||||
variations: g.ensureVariationsString(),
|
variations: g.ensureVariationsString(),
|
||||||
}
|
}
|
||||||
img := g.Source.getOrCreateGlyphImage(g, key, func() *ebiten.Image {
|
img := g.Source.getOrCreateGlyphImage(g, key, func() *ebiten.Image {
|
||||||
|
if glyph.bitmap != nil {
|
||||||
|
return ebiten.NewImageFromImage(glyph.bitmap)
|
||||||
|
}
|
||||||
return segmentsToImage(glyph.scaledSegments, subpixelOffset, b)
|
return segmentsToImage(glyph.scaledSegments, subpixelOffset, b)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ package text
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png" // As typesettings/font already imports image/png, it is fine to ignore side effects (#2336).
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -26,6 +29,7 @@ import (
|
|||||||
"github.com/go-text/typesetting/opentype/loader"
|
"github.com/go-text/typesetting/opentype/loader"
|
||||||
"github.com/go-text/typesetting/shaping"
|
"github.com/go-text/typesetting/shaping"
|
||||||
"golang.org/x/image/math/fixed"
|
"golang.org/x/image/math/fixed"
|
||||||
|
"golang.org/x/image/tiff"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
)
|
)
|
||||||
@ -46,6 +50,7 @@ type glyph struct {
|
|||||||
endIndex int
|
endIndex int
|
||||||
scaledSegments []api.Segment
|
scaledSegments []api.Segment
|
||||||
bounds fixed.Rectangle26_6
|
bounds fixed.Rectangle26_6
|
||||||
|
bitmap image.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
type goTextOutputCacheValue struct {
|
type goTextOutputCacheValue struct {
|
||||||
@ -73,6 +78,9 @@ type GoTextFaceSource struct {
|
|||||||
|
|
||||||
shaper shaping.HarfbuzzShaper
|
shaper shaping.HarfbuzzShaper
|
||||||
|
|
||||||
|
bitmapSizesResult []api.BitmapSize
|
||||||
|
bitmapSizesOnce sync.Once
|
||||||
|
|
||||||
m sync.Mutex
|
m sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,6 +188,17 @@ func (g *GoTextFaceSource) shape(text string, face *GoTextFace) ([]shaping.Outpu
|
|||||||
|
|
||||||
f := face.Source.f
|
f := face.Source.f
|
||||||
f.SetVariations(face.variations)
|
f.SetVariations(face.variations)
|
||||||
|
f.XPpem = 0
|
||||||
|
f.YPpem = 0
|
||||||
|
var useBitmap bool
|
||||||
|
for _, bs := range g.bitmapSizes() {
|
||||||
|
if float64(bs.YPpem) == face.Size {
|
||||||
|
f.XPpem = bs.XPpem
|
||||||
|
f.YPpem = bs.YPpem
|
||||||
|
useBitmap = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
runes := []rune(text)
|
runes := []rune(text)
|
||||||
input := shaping.Input{
|
input := shaping.Input{
|
||||||
@ -219,22 +238,63 @@ func (g *GoTextFaceSource) shape(text string, face *GoTextFace) ([]shaping.Outpu
|
|||||||
indices = append(indices, len(text))
|
indices = append(indices, len(text))
|
||||||
|
|
||||||
for _, gl := range out.Glyphs {
|
for _, gl := range out.Glyphs {
|
||||||
gl := gl
|
shapingGlyph := gl
|
||||||
var segs []api.Segment
|
var segs []api.Segment
|
||||||
switch data := g.f.GlyphData(gl.GlyphID).(type) {
|
var bitmap image.Image
|
||||||
|
switch data := g.f.GlyphData(shapingGlyph.GlyphID).(type) {
|
||||||
case api.GlyphOutline:
|
case api.GlyphOutline:
|
||||||
if out.Direction.IsSideways() {
|
if out.Direction.IsSideways() {
|
||||||
data.Sideways(fixed26_6ToFloat32(-gl.YOffset) / fixed26_6ToFloat32(out.Size) * float32(f.Upem()))
|
data.Sideways(fixed26_6ToFloat32(-shapingGlyph.YOffset) / fixed26_6ToFloat32(out.Size) * float32(f.Upem()))
|
||||||
}
|
}
|
||||||
segs = data.Segments
|
segs = data.Segments
|
||||||
case api.GlyphSVG:
|
case api.GlyphSVG:
|
||||||
segs = data.Outline.Segments
|
segs = data.Outline.Segments
|
||||||
case api.GlyphBitmap:
|
case api.GlyphBitmap:
|
||||||
|
if useBitmap {
|
||||||
|
switch data.Format {
|
||||||
|
case api.BlackAndWhite:
|
||||||
|
img := image.NewAlpha(image.Rect(0, 0, data.Width, data.Height))
|
||||||
|
for j := 0; j < data.Height; j++ {
|
||||||
|
for i := 0; i < data.Width; i++ {
|
||||||
|
idx := j*data.Width + i
|
||||||
|
if data.Data[idx/8]&(1<<(7-idx%8)) != 0 {
|
||||||
|
img.Pix[j*img.Stride+i] = 0xff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitmap = img
|
||||||
|
case api.PNG:
|
||||||
|
img, err := png.Decode(bytes.NewReader(data.Data))
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bitmap = img
|
||||||
|
case api.JPG:
|
||||||
|
img, err := jpeg.Decode(bytes.NewReader(data.Data))
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bitmap = img
|
||||||
|
case api.TIFF:
|
||||||
|
img, err := tiff.Decode(bytes.NewReader(data.Data))
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bitmap = img
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use outline segments in any cases for vector rendering.
|
||||||
if data.Outline != nil {
|
if data.Outline != nil {
|
||||||
segs = data.Outline.Segments
|
segs = data.Outline.Segments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gl := glyph{
|
||||||
|
startIndex: indices[shapingGlyph.ClusterIndex],
|
||||||
|
endIndex: indices[shapingGlyph.ClusterIndex+shapingGlyph.RuneCount],
|
||||||
|
}
|
||||||
|
|
||||||
scaledSegs := make([]api.Segment, len(segs))
|
scaledSegs := make([]api.Segment, len(segs))
|
||||||
scale := float32(g.scale(fixed26_6ToFloat64(out.Size)))
|
scale := float32(g.scale(fixed26_6ToFloat64(out.Size)))
|
||||||
for i, seg := range segs {
|
for i, seg := range segs {
|
||||||
@ -245,13 +305,15 @@ func (g *GoTextFaceSource) shape(text string, face *GoTextFace) ([]shaping.Outpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gs = append(gs, glyph{
|
gl.shapingGlyph = &shapingGlyph
|
||||||
shapingGlyph: &gl,
|
gl.scaledSegments = scaledSegs
|
||||||
startIndex: indices[gl.ClusterIndex],
|
gl.bounds = segmentsToBounds(scaledSegs)
|
||||||
endIndex: indices[gl.ClusterIndex+gl.RuneCount],
|
|
||||||
scaledSegments: scaledSegs,
|
if bitmap != nil {
|
||||||
bounds: segmentsToBounds(scaledSegs),
|
gl.bitmap = bitmap
|
||||||
})
|
}
|
||||||
|
|
||||||
|
gs = append(gs, gl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,6 +354,13 @@ func (g *GoTextFaceSource) getOrCreateGlyphImage(goTextFace *GoTextFace, key goT
|
|||||||
return g.glyphImageCache[goTextFace.Size].getOrCreate(goTextFace, key, create)
|
return g.glyphImageCache[goTextFace.Size].getOrCreate(goTextFace, key, create)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GoTextFaceSource) bitmapSizes() []api.BitmapSize {
|
||||||
|
g.bitmapSizesOnce.Do(func() {
|
||||||
|
g.bitmapSizesResult = g.f.BitmapSizes()
|
||||||
|
})
|
||||||
|
return g.bitmapSizesResult
|
||||||
|
}
|
||||||
|
|
||||||
type singleFontmap struct {
|
type singleFontmap struct {
|
||||||
face font.Face
|
face font.Face
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user