diff --git a/draw2dbase/text.go b/draw2dbase/text.go index 4283bad..1665363 100644 --- a/draw2dbase/text.go +++ b/draw2dbase/text.go @@ -2,67 +2,85 @@ package draw2dbase import "github.com/llgcode/draw2d" -var glyphCache map[string]map[rune]*Glyph +var ( + DefaultGlyphCache = &defaultGlyphCache{make(map[string]map[rune]*Glyph)} +) -func init() { - glyphCache = make(map[string]map[rune]*Glyph) +// Types implementing this interface can be passed to gc.SetGlyphCache to change the +// way glyphs are being stored and retrieved. +type GlyphCache interface { + // Fetch fetches a glyph from the cache, storing with Render first if it doesn't already exist + Fetch(gc draw2d.GraphicContext, fontName string, chr rune) *Glyph + + // Render renders a glyph then returns it + Render(gc draw2d.GraphicContext, fontName string, chr rune) *Glyph } -// FetchGlyph fetches a glyph from the cache, calling renderGlyph first if it doesn't already exist -func FetchGlyph(gc draw2d.GraphicContext, fontName string, chr rune) *Glyph { - if glyphCache[fontName] == nil { - glyphCache[fontName] = make(map[rune]*Glyph, 60) - } - if glyphCache[fontName][chr] == nil { - glyphCache[fontName][chr] = renderGlyph(gc, fontName, chr) - } - return glyphCache[fontName][chr].Copy() +// NewGlyphCache creates and returns a new GlyphCache +func NewGlyphCache() GlyphCache { + return &defaultGlyphCache{make(map[string]map[rune]*Glyph)} } -// renderGlyph renders a glyph then caches and returns it -func renderGlyph(gc draw2d.GraphicContext, fontName string, chr rune) *Glyph { +type defaultGlyphCache struct { + glyphMap map[string]map[rune]*Glyph +} + +// Fetch fetches a glyph from the cache, storing with Render first if it doesn't already exist +func (cache *defaultGlyphCache) Fetch(gc draw2d.GraphicContext, fontName string, chr rune) *Glyph { + if cache.glyphMap[fontName] == nil { + cache.glyphMap[fontName] = make(map[rune]*Glyph, 60) + } + if cache.glyphMap[fontName][chr] == nil { + cache.glyphMap[fontName][chr] = cache.Render(gc, fontName, chr) + } + return cache.glyphMap[fontName][chr].Copy() +} + +// Render renders a glyph then returns it +func (cache *defaultGlyphCache) Render(gc draw2d.GraphicContext, fontName string, chr rune) *Glyph { gc.Save() defer gc.Restore() gc.BeginPath() width := gc.CreateStringPath(string(chr), 0, 0) path := gc.GetPath() return &Glyph{ - path: &path, + Path: &path, Width: width, } } // Glyph represents a rune which has been converted to a Path and width type Glyph struct { - // path represents a glyph, it is always at (0, 0) - path *draw2d.Path + // Path represents a glyph, it is always at (0, 0) + Path *draw2d.Path // Width of the glyph Width float64 } +// Copy copys the Glyph, and returns the copy func (g *Glyph) Copy() *Glyph { return &Glyph{ - path: g.path.Copy(), + Path: g.Path.Copy(), Width: g.Width, } } -// Fill copies a glyph from the cache, and fills it +// Fill fills a Glyph in the specified coordinates func (g *Glyph) Fill(gc draw2d.GraphicContext, x, y float64) float64 { gc.Save() gc.BeginPath() gc.Translate(x, y) - gc.Fill(g.path) + gc.Fill(g.Path) gc.Restore() return g.Width } -// Stroke fetches a glyph from the cache, and strokes it +// Stroke strokes a Glyph in the specified coordinates func (g *Glyph) Stroke(gc draw2d.GraphicContext, x, y float64) float64 { gc.Save() gc.BeginPath() gc.Translate(x, y) - gc.Stroke(g.path) + gc.Stroke(g.Path) gc.Restore() return g.Width } diff --git a/draw2dgl/gc.go b/draw2dgl/gc.go index d512ccc..ae8b3d1 100644 --- a/draw2dgl/gc.go +++ b/draw2dgl/gc.go @@ -126,7 +126,8 @@ type GraphicContext struct { fillRasterizer *raster.Rasterizer strokeRasterizer *raster.Rasterizer glyphBuf *truetype.GlyphBuf - DPI int + glyphCache draw2dbase.GlyphCache + DPI int } // NewGraphicContext creates a new Graphic context from an image. @@ -137,6 +138,7 @@ func NewGraphicContext(width, height int) *GraphicContext { raster.NewRasterizer(width, height), raster.NewRasterizer(width, height), &truetype.GlyphBuf{}, + draw2dbase.DefaultGlyphCache, 92, } return gc @@ -217,7 +219,7 @@ func (gc *GraphicContext) FillStringAt(text string, x, y float64) (width float64 if hasPrev { x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index)) } - glyph := draw2dbase.FetchGlyph(gc, fontName, r) + glyph := gc.glyphCache.Fetch(gc, fontName, r) x += glyph.Fill(gc, x, y) prev, hasPrev = index, true } @@ -283,7 +285,7 @@ func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (width float if hasPrev { x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index)) } - glyph := draw2dbase.FetchGlyph(gc, fontName, r) + glyph := gc.glyphCache.Fetch(gc, fontName, r) x += glyph.Stroke(gc, x, y) prev, hasPrev = index, true } @@ -312,6 +314,16 @@ func (gc *GraphicContext) SetFontSize(fontSize float64) { gc.recalc() } +// Changes the glyph cache backend used by the GraphicContext. +// To restore the default glyph cache, call this function passing nil as argument. +func (gc *GraphicContext) SetGlyphCache(cache draw2dbase.GlyphCache) { + if cache == nil { + gc.glyphCache = draw2dbase.DefaultGlyphCache + } else { + gc.glyphCache = cache + } +} + func (gc *GraphicContext) GetDPI() int { return gc.DPI } diff --git a/draw2dimg/ftgc.go b/draw2dimg/ftgc.go index abaf7e5..33c7a57 100644 --- a/draw2dimg/ftgc.go +++ b/draw2dimg/ftgc.go @@ -36,6 +36,7 @@ type GraphicContext struct { fillRasterizer *raster.Rasterizer strokeRasterizer *raster.Rasterizer glyphBuf *truetype.GlyphBuf + glyphCache draw2dbase.GlyphCache DPI int } @@ -75,6 +76,7 @@ func NewGraphicContextWithPainter(img draw.Image, painter Painter) *GraphicConte raster.NewRasterizer(width, height), raster.NewRasterizer(width, height), &truetype.GlyphBuf{}, + draw2dbase.DefaultGlyphCache, dpi, } return gc @@ -136,7 +138,7 @@ func (gc *GraphicContext) FillStringAt(text string, x, y float64) (width float64 if hasPrev { x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index)) } - glyph := draw2dbase.FetchGlyph(gc, fontName, r) + glyph := gc.glyphCache.Fetch(gc, fontName, r) x += glyph.Fill(gc, x, y) prev, hasPrev = index, true } @@ -163,7 +165,7 @@ func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (width float if hasPrev { x += fUnitsToFloat64(f.Kern(fixed.Int26_6(gc.Current.Scale), prev, index)) } - glyph := draw2dbase.FetchGlyph(gc, fontName, r) + glyph := gc.glyphCache.Fetch(gc, fontName, r) x += glyph.Stroke(gc, x, y) prev, hasPrev = index, true } @@ -291,6 +293,16 @@ func (gc *GraphicContext) SetFontSize(fontSize float64) { gc.recalc() } +// Changes the glyph cache backend used by the GraphicContext. +// To restore the default glyph cache, call this function passing nil as argument. +func (gc *GraphicContext) SetGlyphCache(cache draw2dbase.GlyphCache) { + if cache == nil { + gc.glyphCache = draw2dbase.DefaultGlyphCache + } else { + gc.glyphCache = cache + } +} + func (gc *GraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) { gc.painter.SetColor(color) rasterizer.Rasterize(gc.painter)