package truetype
import (
"image"
"math"
"github.com/golang/freetype/raster"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
func powerOf2(i int ) bool {
return i != 0 && (i &(i -1 )) == 0
}
type Options struct {
Size float64
DPI float64
Hinting font .Hinting
GlyphCacheEntries int
SubPixelsX int
SubPixelsY int
}
func (o *Options ) size () float64 {
if o != nil && o .Size > 0 {
return o .Size
}
return 12
}
func (o *Options ) dpi () float64 {
if o != nil && o .DPI > 0 {
return o .DPI
}
return 72
}
func (o *Options ) hinting () font .Hinting {
if o != nil {
switch o .Hinting {
case font .HintingVertical , font .HintingFull :
return font .HintingFull
}
}
return font .HintingNone
}
func (o *Options ) glyphCacheEntries () int {
if o != nil && powerOf2 (o .GlyphCacheEntries ) {
return o .GlyphCacheEntries
}
return 512
}
func (o *Options ) subPixelsX () (value uint32 , halfQuantum , mask fixed .Int26_6 ) {
if o != nil {
switch o .SubPixelsX {
case 1 , 2 , 4 , 8 , 16 , 32 , 64 :
return subPixels (o .SubPixelsX )
}
}
return subPixels (4 )
}
func (o *Options ) subPixelsY () (value uint32 , halfQuantum , mask fixed .Int26_6 ) {
if o != nil {
switch o .SubPixelsX {
case 1 , 2 , 4 , 8 , 16 , 32 , 64 :
return subPixels (o .SubPixelsX )
}
}
return subPixels (1 )
}
func subPixels(q int ) (value uint32 , bias , mask fixed .Int26_6 ) {
return uint32 (q ), 32 / fixed .Int26_6 (q ), -64 / fixed .Int26_6 (q )
}
type glyphCacheEntry struct {
key glyphCacheKey
val glyphCacheVal
}
type glyphCacheKey struct {
index Index
fx, fy uint8
}
type glyphCacheVal struct {
advanceWidth fixed .Int26_6
offset image .Point
gw int
gh int
}
type indexCacheEntry struct {
rune rune
index Index
}
func NewFace (f *Font , opts *Options ) font .Face {
a := &face {
f : f ,
hinting : opts .hinting (),
scale : fixed .Int26_6 (0.5 + (opts .size () * opts .dpi () * 64 / 72 )),
glyphCache : make ([]glyphCacheEntry , opts .glyphCacheEntries ()),
}
a .subPixelX , a .subPixelBiasX , a .subPixelMaskX = opts .subPixelsX ()
a .subPixelY , a .subPixelBiasY , a .subPixelMaskY = opts .subPixelsY ()
for i := range a .glyphCache {
a .glyphCache [i ].key .fy = 0xff
}
for i := range a .indexCache {
a .indexCache [i ].rune = -1
}
b := f .Bounds (a .scale )
xmin := +int (b .Min .X ) >> 6
ymin := -int (b .Max .Y ) >> 6
xmax := +int (b .Max .X +63 ) >> 6
ymax := -int (b .Min .Y -63 ) >> 6
a .maxw = xmax - xmin
a .maxh = ymax - ymin
a .masks = image .NewAlpha (image .Rect (0 , 0 , a .maxw , a .maxh *len (a .glyphCache )))
a .r .SetBounds (a .maxw , a .maxh )
a .p = facePainter {a }
return a
}
type face struct {
f *Font
hinting font .Hinting
scale fixed .Int26_6
subPixelX uint32
subPixelBiasX fixed .Int26_6
subPixelMaskX fixed .Int26_6
subPixelY uint32
subPixelBiasY fixed .Int26_6
subPixelMaskY fixed .Int26_6
masks *image .Alpha
glyphCache []glyphCacheEntry
r raster .Rasterizer
p raster .Painter
paintOffset int
maxw int
maxh int
glyphBuf GlyphBuf
indexCache [indexCacheLen ]indexCacheEntry
}
const indexCacheLen = 256
func (a *face ) index (r rune ) Index {
const mask = indexCacheLen - 1
c := &a .indexCache [r &mask ]
if c .rune == r {
return c .index
}
i := a .f .Index (r )
c .rune = r
c .index = i
return i
}
func (a *face ) Close () error { return nil }
func (a *face ) Metrics () font .Metrics {
scale := float64 (a .scale )
fupe := float64 (a .f .FUnitsPerEm ())
return font .Metrics {
Height : a .scale ,
Ascent : fixed .Int26_6 (math .Ceil (scale * float64 (+a .f .ascent ) / fupe )),
Descent : fixed .Int26_6 (math .Ceil (scale * float64 (-a .f .descent ) / fupe )),
}
}
func (a *face ) Kern (r0 , r1 rune ) fixed .Int26_6 {
i0 := a .index (r0 )
i1 := a .index (r1 )
kern := a .f .Kern (a .scale , i0 , i1 )
if a .hinting != font .HintingNone {
kern = (kern + 32 ) &^ 63
}
return kern
}
func (a *face ) Glyph (dot fixed .Point26_6 , r rune ) (
dr image .Rectangle , mask image .Image , maskp image .Point , advance fixed .Int26_6 , ok bool ) {
dotX := (dot .X + a .subPixelBiasX ) & a .subPixelMaskX
dotY := (dot .Y + a .subPixelBiasY ) & a .subPixelMaskY
ix , fx := int (dotX >>6 ), dotX &0x3f
iy , fy := int (dotY >>6 ), dotY &0x3f
index := a .index (r )
cIndex := uint32 (index )
cIndex = cIndex *a .subPixelX - uint32 (fx /a .subPixelMaskX )
cIndex = cIndex *a .subPixelY - uint32 (fy /a .subPixelMaskY )
cIndex &= uint32 (len (a .glyphCache ) - 1 )
a .paintOffset = a .maxh * int (cIndex )
k := glyphCacheKey {
index : index ,
fx : uint8 (fx ),
fy : uint8 (fy ),
}
var v glyphCacheVal
if a .glyphCache [cIndex ].key != k {
var ok bool
v , ok = a .rasterize (index , fx , fy )
if !ok {
return image .Rectangle {}, nil , image .Point {}, 0 , false
}
a .glyphCache [cIndex ] = glyphCacheEntry {k , v }
} else {
v = a .glyphCache [cIndex ].val
}
dr .Min = image .Point {
X : ix + v .offset .X ,
Y : iy + v .offset .Y ,
}
dr .Max = image .Point {
X : dr .Min .X + v .gw ,
Y : dr .Min .Y + v .gh ,
}
return dr , a .masks , image .Point {Y : a .paintOffset }, v .advanceWidth , true
}
func (a *face ) GlyphBounds (r rune ) (bounds fixed .Rectangle26_6 , advance fixed .Int26_6 , ok bool ) {
if err := a .glyphBuf .Load (a .f , a .scale , a .index (r ), a .hinting ); err != nil {
return fixed .Rectangle26_6 {}, 0 , false
}
xmin := +a .glyphBuf .Bounds .Min .X
ymin := -a .glyphBuf .Bounds .Max .Y
xmax := +a .glyphBuf .Bounds .Max .X
ymax := -a .glyphBuf .Bounds .Min .Y
if xmin > xmax || ymin > ymax {
return fixed .Rectangle26_6 {}, 0 , false
}
return fixed .Rectangle26_6 {
Min : fixed .Point26_6 {
X : xmin ,
Y : ymin ,
},
Max : fixed .Point26_6 {
X : xmax ,
Y : ymax ,
},
}, a .glyphBuf .AdvanceWidth , true
}
func (a *face ) GlyphAdvance (r rune ) (advance fixed .Int26_6 , ok bool ) {
if err := a .glyphBuf .Load (a .f , a .scale , a .index (r ), a .hinting ); err != nil {
return 0 , false
}
return a .glyphBuf .AdvanceWidth , true
}
func (a *face ) rasterize (index Index , fx , fy fixed .Int26_6 ) (v glyphCacheVal , ok bool ) {
if err := a .glyphBuf .Load (a .f , a .scale , index , a .hinting ); err != nil {
return glyphCacheVal {}, false
}
xmin := int (fx +a .glyphBuf .Bounds .Min .X ) >> 6
ymin := int (fy -a .glyphBuf .Bounds .Max .Y ) >> 6
xmax := int (fx +a .glyphBuf .Bounds .Max .X +0x3f ) >> 6
ymax := int (fy -a .glyphBuf .Bounds .Min .Y +0x3f ) >> 6
if xmin > xmax || ymin > ymax {
return glyphCacheVal {}, false
}
fx -= fixed .Int26_6 (xmin << 6 )
fy -= fixed .Int26_6 (ymin << 6 )
a .r .Clear ()
pixOffset := a .paintOffset * a .maxw
clear (a .masks .Pix [pixOffset : pixOffset +a .maxw *a .maxh ])
e0 := 0
for _ , e1 := range a .glyphBuf .Ends {
a .drawContour (a .glyphBuf .Points [e0 :e1 ], fx , fy )
e0 = e1
}
a .r .Rasterize (a .p )
return glyphCacheVal {
a .glyphBuf .AdvanceWidth ,
image .Point {xmin , ymin },
xmax - xmin ,
ymax - ymin ,
}, true
}
func clear(pix []byte ) {
for i := range pix {
pix [i ] = 0
}
}
func (a *face ) drawContour (ps []Point , dx , dy fixed .Int26_6 ) {
if len (ps ) == 0 {
return
}
start := fixed .Point26_6 {
X : dx + ps [0 ].X ,
Y : dy - ps [0 ].Y ,
}
var others []Point
if ps [0 ].Flags &0x01 != 0 {
others = ps [1 :]
} else {
last := fixed .Point26_6 {
X : dx + ps [len (ps )-1 ].X ,
Y : dy - ps [len (ps )-1 ].Y ,
}
if ps [len (ps )-1 ].Flags &0x01 != 0 {
start = last
others = ps [:len (ps )-1 ]
} else {
start = fixed .Point26_6 {
X : (start .X + last .X ) / 2 ,
Y : (start .Y + last .Y ) / 2 ,
}
others = ps
}
}
a .r .Start (start )
q0 , on0 := start , true
for _ , p := range others {
q := fixed .Point26_6 {
X : dx + p .X ,
Y : dy - p .Y ,
}
on := p .Flags &0x01 != 0
if on {
if on0 {
a .r .Add1 (q )
} else {
a .r .Add2 (q0 , q )
}
} else {
if on0 {
} else {
mid := fixed .Point26_6 {
X : (q0 .X + q .X ) / 2 ,
Y : (q0 .Y + q .Y ) / 2 ,
}
a .r .Add2 (q0 , mid )
}
}
q0 , on0 = q , on
}
if on0 {
a .r .Add1 (start )
} else {
a .r .Add2 (q0 , start )
}
}
type facePainter struct {
a *face
}
func (p facePainter ) Paint (ss []raster .Span , done bool ) {
m := p .a .masks
b := m .Bounds ()
b .Min .Y = p .a .paintOffset
b .Max .Y = p .a .paintOffset + p .a .maxh
for _ , s := range ss {
s .Y += p .a .paintOffset
if s .Y < b .Min .Y {
continue
}
if s .Y >= b .Max .Y {
return
}
if s .X0 < b .Min .X {
s .X0 = b .Min .X
}
if s .X1 > b .Max .X {
s .X1 = b .Max .X
}
if s .X0 >= s .X1 {
continue
}
base := (s .Y -m .Rect .Min .Y )*m .Stride - m .Rect .Min .X
p := m .Pix [base +s .X0 : base +s .X1 ]
color := uint8 (s .Alpha >> 8 )
for i := range p {
p [i ] = color
}
}
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .