mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 16:44:23 +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 (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -47,11 +48,18 @@ func parseSFNTFont(data []byte) (*sfntFont, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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"]
|
head, maxp, hhea := table["head"], table["maxp"], table["hhea"]
|
||||||
if len(head) != 54 || len(maxp) < 6 || len(hhea) < 36 {
|
if len(head) != 54 || len(maxp) < 6 || len(hhea) < 36 {
|
||||||
return nil, fmt.Errorf("missing or truncated font metrics")
|
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.units, f.count = binary.BigEndian.Uint16(head[18:]), binary.BigEndian.Uint16(maxp[4:])
|
||||||
f.horizontal = binary.BigEndian.Uint16(hhea[34:])
|
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) {
|
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")
|
return nil, fmt.Errorf("invalid glyph location table")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.cmaps, err = readSFNTCmaps(table["cmap"])
|
cmaps, err := readSFNTCmaps(table["cmap"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
f.cmaps = cmaps
|
||||||
for _, cmap := range f.cmaps {
|
for _, cmap := range f.cmaps {
|
||||||
for _, glyph := range cmap.mapping {
|
for _, glyph := range cmap.mapping {
|
||||||
if glyph >= f.count {
|
if glyph >= f.count {
|
||||||
|
|||||||
@@ -103,12 +103,24 @@ func (r *Renderer) cachedTextGlyphPath(face *canvas.FontFace, glyph textGlyph) (
|
|||||||
}
|
}
|
||||||
outlines := r.canvasState().fontOutlines[face.Font]
|
outlines := r.canvasState().fontOutlines[face.Font]
|
||||||
if outlines == nil {
|
if outlines == nil {
|
||||||
metrics, err := parseSFNTFont(fontSFNTData(face.Font.Tables))
|
cache := r.canvasFontOutlines()
|
||||||
if err != nil {
|
outlines, _ = cache.get(face.Font)
|
||||||
r.renderError = err
|
if outlines == nil {
|
||||||
return &canvas.Path{}, 0
|
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
|
r.canvasState().fontOutlines[face.Font] = outlines
|
||||||
}
|
}
|
||||||
path, width, err := textGlyphPath(face, glyph, outlines)
|
path, width, err := textGlyphPath(face, glyph, outlines)
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ type canvasPathCacheKey struct{}
|
|||||||
// canvasFillCacheKey 区分按填充规则规范化的共享路径缓存
|
// canvasFillCacheKey 区分按填充规则规范化的共享路径缓存
|
||||||
type canvasFillCacheKey struct{}
|
type canvasFillCacheKey struct{}
|
||||||
|
|
||||||
|
// canvasOutlineCacheKey 区分子渲染器共享的字体轮廓解析缓存
|
||||||
|
type canvasOutlineCacheKey struct{}
|
||||||
|
|
||||||
// canvasFillKey 保存完整几何摘要及填充规则
|
// canvasFillKey 保存完整几何摘要及填充规则
|
||||||
type canvasFillKey struct {
|
type canvasFillKey struct {
|
||||||
digest [32]byte
|
digest [32]byte
|
||||||
@@ -114,6 +117,18 @@ func (r *Renderer) canvasFontFamilies() *renderCache[canvasFontKey, *canvas.Font
|
|||||||
return cache
|
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私有缓存,按需创建且不向其他后端暴露
|
// canvasState 获取当前渲染器的Canvas私有缓存,按需创建且不向其他后端暴露
|
||||||
// 返回: *canvasBackendState Canvas缓存
|
// 返回: *canvasBackendState Canvas缓存
|
||||||
func (r *Renderer) canvasState() *canvasBackendState {
|
func (r *Renderer) canvasState() *canvasBackendState {
|
||||||
|
|||||||
Reference in new issue
Block a user