github.com/jmigpin/editor@v1.6.0/util/fontutil/facecache.go (about) 1 package fontutil 2 3 import ( 4 "image" 5 6 "golang.org/x/image/font" 7 "golang.org/x/image/math/fixed" 8 ) 9 10 type FaceCache struct { 11 font.Face 12 gc map[rune]*GlyphCache 13 gac map[rune]*GlyphAdvanceCache 14 gbc map[rune]*GlyphBoundsCache 15 kc map[string]fixed.Int26_6 // kern cache 16 } 17 18 func NewFaceCache(face font.Face) *FaceCache { 19 fc := &FaceCache{Face: face} 20 fc.gc = make(map[rune]*GlyphCache) 21 fc.gac = make(map[rune]*GlyphAdvanceCache) 22 fc.gbc = make(map[rune]*GlyphBoundsCache) 23 fc.kc = make(map[string]fixed.Int26_6) 24 return fc 25 } 26 func (fc *FaceCache) Glyph(dot fixed.Point26_6, ru rune) ( 27 dr image.Rectangle, 28 mask image.Image, 29 maskp image.Point, 30 advance fixed.Int26_6, 31 ok bool, 32 ) { 33 gc, ok := fc.gc[ru] 34 if !ok { 35 gc = NewGlyphCache(fc.Face, ru) 36 fc.gc[ru] = gc 37 } 38 p := image.Point{dot.X.Floor(), dot.Y.Floor()} 39 dr2 := gc.dr.Add(p) 40 return dr2, gc.mask, gc.maskp, gc.advance, gc.ok 41 } 42 func (fc *FaceCache) GlyphAdvance(ru rune) (advance fixed.Int26_6, ok bool) { 43 gac, ok := fc.gac[ru] 44 if !ok { 45 gac = NewGlyphAdvanceCache(fc.Face, ru) 46 fc.gac[ru] = gac 47 } 48 return gac.advance, gac.ok 49 } 50 func (fc *FaceCache) GlyphBounds(ru rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { 51 gbc, ok := fc.gbc[ru] 52 if !ok { 53 gbc = NewGlyphBoundsCache(fc.Face, ru) 54 fc.gbc[ru] = gbc 55 } 56 return gbc.bounds, gbc.advance, gbc.ok 57 } 58 func (fc *FaceCache) Kern(r0, r1 rune) fixed.Int26_6 { 59 i := kernIndex(r0, r1) 60 k, ok := fc.kc[i] 61 if !ok { 62 k = NewKernCache(fc.Face, r0, r1) 63 fc.kc[i] = k 64 } 65 return k 66 } 67 68 //---------- 69 70 type GlyphCache struct { 71 dr image.Rectangle 72 mask image.Image 73 maskp image.Point 74 advance fixed.Int26_6 75 ok bool 76 } 77 78 func NewGlyphCache(face font.Face, ru rune) *GlyphCache { 79 var zeroDot fixed.Point26_6 // always use zero 80 dr, mask, maskp, adv, ok := face.Glyph(zeroDot, ru) 81 82 // avoid the truetype package cache (it's not giving the same mask everytime, probably needs cache parameter) 83 if ok { 84 mask = copyMask(mask) 85 } 86 87 return &GlyphCache{dr, mask, maskp, adv, ok} 88 } 89 90 //---------- 91 92 type GlyphAdvanceCache struct { 93 advance fixed.Int26_6 94 ok bool 95 } 96 97 func NewGlyphAdvanceCache(face font.Face, ru rune) *GlyphAdvanceCache { 98 adv, ok := face.GlyphAdvance(ru) // only one can run at a time 99 return &GlyphAdvanceCache{adv, ok} 100 } 101 102 //---------- 103 104 type GlyphBoundsCache struct { 105 bounds fixed.Rectangle26_6 106 advance fixed.Int26_6 107 ok bool 108 } 109 110 func NewGlyphBoundsCache(face font.Face, ru rune) *GlyphBoundsCache { 111 bounds, adv, ok := face.GlyphBounds(ru) // only one can run at a time 112 return &GlyphBoundsCache{bounds, adv, ok} 113 } 114 115 //---------- 116 117 func kernIndex(r0, r1 rune) string { 118 return string([]rune{r0, ',', r1}) 119 } 120 121 func NewKernCache(face font.Face, r0, r1 rune) fixed.Int26_6 { 122 return face.Kern(r0, r1) // only one can run at a time 123 } 124 125 //---------- 126 127 func copyMask(mask image.Image) image.Image { 128 alpha := *(mask.(*image.Alpha)) // copy structure 129 pix := make([]uint8, len(alpha.Pix)) 130 copy(pix, alpha.Pix) 131 alpha.Pix = pix 132 return &alpha 133 } 134 135 //---------- 136 137 //func copyMask2(mask image.Image) (image.Image, []byte) { 138 // alpha := *(mask.(*image.Alpha)) // copy structure 139 // pix := make([]uint8, len(alpha.Pix)) 140 // copy(pix, alpha.Pix) 141 // alpha.Pix = pix 142 // h := bytesHash(pix) 143 // return &alpha, h 144 //} 145 146 //func bytesHash(b []byte) []byte { 147 // h := sha1.New() 148 // h.Write(b) 149 // return h.Sum(nil) 150 //}