// Copyright 2023 The Ebitengine Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package text import ( "image" "golang.org/x/image/font" "golang.org/x/image/math/fixed" ) type glyphBoundsCacheValue struct { bounds fixed.Rectangle26_6 advance fixed.Int26_6 ok bool } type glyphAdvanceCacheValue struct { advance fixed.Int26_6 ok bool } type kernCacheKey struct { r0 rune r1 rune } type faceWithCache struct { f font.Face metrics font.Metrics glyphBoundsCache map[rune]glyphBoundsCacheValue glyphAdvanceCache map[rune]glyphAdvanceCacheValue kernCache map[kernCacheKey]fixed.Int26_6 } func (f *faceWithCache) Close() error { if err := f.f.Close(); err != nil { return err } f.glyphBoundsCache = nil f.glyphAdvanceCache = nil f.kernCache = nil return nil } var faceWithCacheCache map[font.Face]*faceWithCache func faceWithCacheFromFace(face font.Face) *faceWithCache { if f, ok := faceWithCacheCache[face]; ok { return f } f := &faceWithCache{ f: face, } if faceWithCacheCache == nil { faceWithCacheCache = map[font.Face]*faceWithCache{} } faceWithCacheCache[face] = f return f } func (f *faceWithCache) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { // TODO: Move glyphImageCache to here. return f.f.Glyph(dot, r) } func (f *faceWithCache) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { if v, ok := f.glyphBoundsCache[r]; ok { return v.bounds, v.advance, v.ok } bounds, advance, ok = f.f.GlyphBounds(r) if f.glyphBoundsCache == nil { f.glyphBoundsCache = map[rune]glyphBoundsCacheValue{} } f.glyphBoundsCache[r] = glyphBoundsCacheValue{ bounds: bounds, advance: advance, ok: ok, } return } func (f *faceWithCache) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { if v, ok := f.glyphAdvanceCache[r]; ok { return v.advance, v.ok } advance, ok = f.f.GlyphAdvance(r) if f.glyphAdvanceCache == nil { f.glyphAdvanceCache = map[rune]glyphAdvanceCacheValue{} } f.glyphAdvanceCache[r] = glyphAdvanceCacheValue{ advance: advance, ok: ok, } return } func (f *faceWithCache) Kern(r0, r1 rune) fixed.Int26_6 { key := kernCacheKey{r0: r0, r1: r1} if v, ok := f.kernCache[key]; ok { return v } v := f.f.Kern(r0, r1) if f.kernCache == nil { f.kernCache = map[kernCacheKey]fixed.Int26_6{} } f.kernCache[key] = v return v } func (f *faceWithCache) Metrics() font.Metrics { if f.metrics != (font.Metrics{}) { return f.metrics } f.metrics = f.f.Metrics() return f.metrics }