freetype/truetype/face.go

238 lines
6.2 KiB
Go
Raw Normal View History

// Copyright 2015 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package truetype
import (
"image"
"github.com/golang/freetype/raster"
"golang.org/x/exp/shiny/font"
"golang.org/x/image/math/fixed"
)
// Options are optional arguments to NewFace.
type Options struct {
// Size is the font size in points, as in "a 10 point font size".
//
// A zero value means to use a 12 point font size.
Size float64
// DPI is the dots-per-inch resolution.
//
// A zero value means to use 72 DPI.
DPI float64
// Hinting is how to quantize the glyph nodes.
//
// A zero value means to use no hinting.
Hinting font.Hinting
}
func (o *Options) size() float64 {
if o.Size > 0 {
return o.Size
}
return 12
}
func (o *Options) dpi() float64 {
if o.DPI > 0 {
return o.DPI
}
return 72
}
func (o *Options) hinting() font.Hinting {
switch o.Hinting {
case font.HintingVertical, font.HintingFull:
// TODO: support vertical hinting.
return font.HintingFull
}
return font.HintingNone
}
// NewFace returns a new font.Face for the given Font.
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)),
}
// Set the rasterizer's bounds to be big enough to handle the largest glyph.
b := f.Bounds(a.scale)
xmin := +int(b.XMin) >> 6
ymin := -int(b.YMax) >> 6
xmax := +int(b.XMax+63) >> 6
ymax := -int(b.YMin-63) >> 6
a.r.SetBounds(xmax-xmin, ymax-ymin)
return a
}
type face struct {
f *Font
hinting font.Hinting
scale fixed.Int26_6
r raster.Rasterizer
glyphBuf GlyphBuf
// TODO: clip rectangle?
}
// Close satisfies the font.Face interface.
func (a *face) Close() error { return nil }
// Kern satisfies the font.Face interface.
func (a *face) Kern(r0, r1 rune) fixed.Int26_6 {
i0 := a.f.Index(r0)
i1 := a.f.Index(r1)
kern := fixed.Int26_6(a.f.Kerning(a.scale, i0, i1))
if a.hinting != font.HintingNone {
kern = (kern + 32) &^ 63
}
return kern
}
// Glyph satisfies the font.Face interface.
func (a *face) Glyph(dot fixed.Point26_6, r rune) (
newDot fixed.Point26_6, dr image.Rectangle, mask image.Image, maskp image.Point, ok bool) {
// Split p.X and p.Y into their integer and fractional parts.
ix, fx := int(dot.X>>6), dot.X&0x3f
iy, fy := int(dot.Y>>6), dot.Y&0x3f
advanceWidth, mask, offset, ok := a.rasterize(a.f.Index(r), fx, fy)
if !ok {
return fixed.Point26_6{}, image.Rectangle{}, nil, image.Point{}, false
}
newDot = fixed.Point26_6{
X: dot.X + advanceWidth,
Y: dot.Y,
}
mb := mask.Bounds()
dr.Min = image.Point{
X: ix + offset.X,
Y: iy + offset.Y,
}
dr.Max = image.Point{
X: dr.Min.X + mb.Dx(),
Y: dr.Min.Y + mb.Dy(),
}
return newDot, dr, mask, image.Point{}, true
}
// rasterize returns the advance width, glyph mask and integer-pixel offset
// to render the given glyph at the given sub-pixel offsets.
// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (
fixed.Int26_6, *image.Alpha, image.Point, bool) {
if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
return 0, nil, image.Point{}, false
}
// Calculate the integer-pixel bounds for the glyph.
xmin := int(fx+fixed.Int26_6(a.glyphBuf.B.XMin)) >> 6
ymin := int(fy-fixed.Int26_6(a.glyphBuf.B.YMax)) >> 6
xmax := int(fx+fixed.Int26_6(a.glyphBuf.B.XMax)+0x3f) >> 6
ymax := int(fy-fixed.Int26_6(a.glyphBuf.B.YMin)+0x3f) >> 6
if xmin > xmax || ymin > ymax {
return 0, nil, image.Point{}, false
}
// A TrueType's glyph's nodes can have negative co-ordinates, but the
// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
// the pixel offsets, based on the font's FUnit metrics, that let a
// negative co-ordinate in TrueType space be non-negative in rasterizer
// space. xmin and ymin are typically <= 0.
fx += fixed.Int26_6(-xmin << 6)
fy += fixed.Int26_6(-ymin << 6)
// Rasterize the glyph's vectors.
a.r.Clear()
e0 := 0
for _, e1 := range a.glyphBuf.End {
a.drawContour(a.glyphBuf.Point[e0:e1], fx, fy)
e0 = e1
}
// TODO: don't allocate a new mask each time.
mask := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
a.r.Rasterize(raster.NewAlphaSrcPainter(mask))
return fixed.Int26_6(a.glyphBuf.AdvanceWidth), mask, image.Point{xmin, ymin}, true
}
// drawContour draws the given closed contour with the given offset.
func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) {
if len(ps) == 0 {
return
}
// The low bit of each point's Flags value is whether the point is on the
// curve. Truetype fonts only have quadratic Bézier curves, not cubics.
// Thus, two consecutive off-curve points imply an on-curve point in the
// middle of those two.
//
// See http://chanae.walon.org/pub/ttf/ttf_glyphs.htm for more details.
// ps[0] is a truetype.Point measured in FUnits and positive Y going
// upwards. start is the same thing measured in fixed point units and
// positive Y going downwards, and offset by (dx, dy).
start := fixed.Point26_6{
X: dx + fixed.Int26_6(ps[0].X),
Y: dy - fixed.Int26_6(ps[0].Y),
}
var others []Point
if ps[0].Flags&0x01 != 0 {
others = ps[1:]
} else {
last := fixed.Point26_6{
X: dx + fixed.Int26_6(ps[len(ps)-1].X),
Y: dy - fixed.Int26_6(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 + fixed.Int26_6(p.X),
Y: dy - fixed.Int26_6(p.Y),
}
on := p.Flags&0x01 != 0
if on {
if on0 {
a.r.Add1(q)
} else {
a.r.Add2(q0, q)
}
} else {
if on0 {
// No-op.
} 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
}
// Close the curve.
if on0 {
a.r.Add1(start)
} else {
a.r.Add2(q0, start)
}
}