mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 13:56:32 +08:00
1 parent
2c06e5ff69
commit
478c18cb1f
3 files changed
+43
-7
No files matched your search
+11
-2
@@ -17,6 +17,7 @@ package ofdgo
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
@@ -47,11 +48,18 @@ func parseSFNTFont(data []byte) (*sfntFont, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseSFNTTables(table)
|
||||
}
|
||||
|
||||
// parseSFNTTables 校验字体表并解析度量和字符映射,借用表数据且不修改其内容
|
||||
// 入参: table 字体表
|
||||
// 返回: *sfntFont 字体, error 格式错误
|
||||
func parseSFNTTables(table map[string][]byte) (*sfntFont, error) {
|
||||
head, maxp, hhea := table["head"], table["maxp"], table["hhea"]
|
||||
if len(head) != 54 || len(maxp) < 6 || len(hhea) < 36 {
|
||||
return nil, fmt.Errorf("missing or truncated font metrics")
|
||||
}
|
||||
f := &sfntFont{Tables: table, IsTrueType: table["glyf"] != nil, IsCFF: table["CFF "] != nil}
|
||||
f := &sfntFont{Tables: maps.Clone(table), IsTrueType: table["glyf"] != nil, IsCFF: table["CFF "] != nil}
|
||||
f.units, f.count = binary.BigEndian.Uint16(head[18:]), binary.BigEndian.Uint16(maxp[4:])
|
||||
f.horizontal = binary.BigEndian.Uint16(hhea[34:])
|
||||
if f.units < 16 || f.units > 16384 || f.count == 0 || f.IsTrueType == f.IsCFF || !validSFNTMetrics(table["hmtx"], f.count, f.horizontal) {
|
||||
@@ -72,10 +80,11 @@ func parseSFNTFont(data []byte) (*sfntFont, error) {
|
||||
return nil, fmt.Errorf("invalid glyph location table")
|
||||
}
|
||||
}
|
||||
f.cmaps, err = readSFNTCmaps(table["cmap"])
|
||||
cmaps, err := readSFNTCmaps(table["cmap"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.cmaps = cmaps
|
||||
for _, cmap := range f.cmaps {
|
||||
for _, glyph := range cmap.mapping {
|
||||
if glyph >= f.count {
|
||||
|
||||
@@ -103,12 +103,24 @@ func (r *Renderer) cachedTextGlyphPath(face *canvas.FontFace, glyph textGlyph) (
|
||||
}
|
||||
outlines := r.canvasState().fontOutlines[face.Font]
|
||||
if outlines == nil {
|
||||
metrics, err := parseSFNTFont(fontSFNTData(face.Font.Tables))
|
||||
if err != nil {
|
||||
r.renderError = err
|
||||
return &canvas.Path{}, 0
|
||||
cache := r.canvasFontOutlines()
|
||||
outlines, _ = cache.get(face.Font)
|
||||
if outlines == nil {
|
||||
metrics, err := parseSFNTTables(face.Font.Tables)
|
||||
if err != nil {
|
||||
r.renderError = err
|
||||
return &canvas.Path{}, 0
|
||||
}
|
||||
outlines = &sfntOutliner{font: metrics}
|
||||
cost := uint64(512)
|
||||
for _, data := range metrics.Tables {
|
||||
cost += uint64(len(data)) * 8
|
||||
}
|
||||
for _, cmap := range metrics.cmaps {
|
||||
cost += uint64(len(cmap.mapping)) * 48
|
||||
}
|
||||
cache.put(face.Font, outlines, int(min(cost, uint64(cache.limit)+1)))
|
||||
}
|
||||
outlines = &sfntOutliner{font: metrics}
|
||||
r.canvasState().fontOutlines[face.Font] = outlines
|
||||
}
|
||||
path, width, err := textGlyphPath(face, glyph, outlines)
|
||||
|
||||
@@ -36,6 +36,9 @@ type canvasPathCacheKey struct{}
|
||||
// canvasFillCacheKey 区分按填充规则规范化的共享路径缓存
|
||||
type canvasFillCacheKey struct{}
|
||||
|
||||
// canvasOutlineCacheKey 区分子渲染器共享的字体轮廓解析缓存
|
||||
type canvasOutlineCacheKey struct{}
|
||||
|
||||
// canvasFillKey 保存完整几何摘要及填充规则
|
||||
type canvasFillKey struct {
|
||||
digest [32]byte
|
||||
@@ -114,6 +117,18 @@ func (r *Renderer) canvasFontFamilies() *renderCache[canvasFontKey, *canvas.Font
|
||||
return cache
|
||||
}
|
||||
|
||||
// canvasFontOutlines 获取子渲染器共享的字体轮廓解析缓存,限制字体及解析数据保留量
|
||||
// 返回: *renderCache[*canvas.Font, *sfntOutliner] 字体轮廓缓存
|
||||
func (r *Renderer) canvasFontOutlines() *renderCache[*canvas.Font, *sfntOutliner] {
|
||||
key := canvasOutlineCacheKey{}
|
||||
if state, ok := r.sharedBackendStates[key]; ok {
|
||||
return state.(*renderCache[*canvas.Font, *sfntOutliner])
|
||||
}
|
||||
cache := &renderCache[*canvas.Font, *sfntOutliner]{limit: 32 << 20}
|
||||
r.sharedBackendStates[key] = cache
|
||||
return cache
|
||||
}
|
||||
|
||||
// canvasState 获取当前渲染器的Canvas私有缓存,按需创建且不向其他后端暴露
|
||||
// 返回: *canvasBackendState Canvas缓存
|
||||
func (r *Renderer) canvasState() *canvasBackendState {
|
||||
|
||||
Reference in new issue
Block a user