Add response time badge and chart

This commit is contained in:
TwinProduction
2021-08-21 17:38:23 -04:00
committed by Chris
parent bab69478dd
commit 470e3a3ebc
140 changed files with 26995 additions and 106 deletions
controller
vendor
github.com
golang.org
modules.txt

20
vendor/github.com/golang/freetype/AUTHORS generated vendored Normal file

@ -0,0 +1,20 @@
# This is the official list of Freetype-Go authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
#
# Freetype-Go is derived from Freetype, which is written in C. The latter
# is copyright 1996-2010 David Turner, Robert Wilhelm, and Werner Lemberg.
# Names should be added to this file as
# Name or Organization <email address>
# The email address is not required for organizations.
# Please keep the list sorted.
Google Inc.
Jeff R. Allen <jra@nella.org>
Maksim Kochkin <maxxarts@gmail.com>
Michael Fogleman <fogleman@gmail.com>
Rémy Oudompheng <oudomphe@phare.normalesup.org>
Roger Peppe <rogpeppe@gmail.com>
Steven Edwards <steven@stephenwithav.com>

38
vendor/github.com/golang/freetype/CONTRIBUTORS generated vendored Normal file

@ -0,0 +1,38 @@
# This is the official list of people who can contribute
# (and typically have contributed) code to the Freetype-Go repository.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# The submission process automatically checks to make sure
# that people submitting code are listed in this file (by email address).
#
# Names should be added to this file only after verifying that
# the individual or the individual's organization has agreed to
# the appropriate Contributor License Agreement, found here:
#
# http://code.google.com/legal/individual-cla-v1.0.html
# http://code.google.com/legal/corporate-cla-v1.0.html
#
# The agreement for individuals can be filled out on the web.
#
# When adding J Random Contributor's name to this file,
# either J's name or J's organization's name should be
# added to the AUTHORS file, depending on whether the
# individual or corporate CLA was used.
# Names should be added to this file like so:
# Name <email address>
# Please keep the list sorted.
Andrew Gerrand <adg@golang.org>
Jeff R. Allen <jra@nella.org> <jeff.allen@gmail.com>
Maksim Kochkin <maxxarts@gmail.com>
Michael Fogleman <fogleman@gmail.com>
Nigel Tao <nigeltao@golang.org>
Rémy Oudompheng <oudomphe@phare.normalesup.org> <remyoudompheng@gmail.com>
Rob Pike <r@golang.org>
Roger Peppe <rogpeppe@gmail.com>
Russ Cox <rsc@golang.org>
Steven Edwards <steven@stephenwithav.com>

12
vendor/github.com/golang/freetype/LICENSE generated vendored Normal file

@ -0,0 +1,12 @@
Use of the Freetype-Go software is subject to your choice of exactly one of
the following two licenses:
* The FreeType License, which is similar to the original BSD license with
an advertising clause, or
* The GNU General Public License (GPL), version 2 or later.
The text of these licenses are available in the licenses/ftl.txt and the
licenses/gpl.txt files respectively. They are also available at
http://freetype.sourceforge.net/license.html
The Luxi fonts in the testdata directory are licensed separately. See the
testdata/COPYING file for details.

245
vendor/github.com/golang/freetype/raster/geom.go generated vendored Normal file

@ -0,0 +1,245 @@
// Copyright 2010 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 raster
import (
"fmt"
"math"
"golang.org/x/image/math/fixed"
)
// maxAbs returns the maximum of abs(a) and abs(b).
func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
if a < 0 {
a = -a
}
if b < 0 {
b = -b
}
if a < b {
return b
}
return a
}
// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
func pNeg(p fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{-p.X, -p.Y}
}
// pDot returns the dot product p·q.
func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 {
px, py := int64(p.X), int64(p.Y)
qx, qy := int64(q.X), int64(q.Y)
return fixed.Int52_12(px*qx + py*qy)
}
// pLen returns the length of the vector p.
func pLen(p fixed.Point26_6) fixed.Int26_6 {
// TODO(nigeltao): use fixed point math.
x := float64(p.X)
y := float64(p.Y)
return fixed.Int26_6(math.Sqrt(x*x + y*y))
}
// pNorm returns the vector p normalized to the given length, or zero if p is
// degenerate.
func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
d := pLen(p)
if d == 0 {
return fixed.Point26_6{}
}
s, t := int64(length), int64(d)
x := int64(p.X) * s / t
y := int64(p.Y) * s / t
return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
}
// pRot45CW returns the vector p rotated clockwise by 45 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
func pRot45CW(p fixed.Point26_6) fixed.Point26_6 {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (+px - py) * 181 / 256
qy := (+px + py) * 181 / 256
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
// pRot90CW returns the vector p rotated clockwise by 90 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{-p.Y, p.X}
}
// pRot135CW returns the vector p rotated clockwise by 135 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
func pRot135CW(p fixed.Point26_6) fixed.Point26_6 {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (-px - py) * 181 / 256
qy := (+px - py) * 181 / 256
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (+px + py) * 181 / 256
qy := (-px + py) * 181 / 256
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{p.Y, -p.X}
}
// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
//
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (-px + py) * 181 / 256
qy := (-px - py) * 181 / 256
return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
}
// An Adder accumulates points on a curve.
type Adder interface {
// Start starts a new curve at the given point.
Start(a fixed.Point26_6)
// Add1 adds a linear segment to the current curve.
Add1(b fixed.Point26_6)
// Add2 adds a quadratic segment to the current curve.
Add2(b, c fixed.Point26_6)
// Add3 adds a cubic segment to the current curve.
Add3(b, c, d fixed.Point26_6)
}
// A Path is a sequence of curves, and a curve is a start point followed by a
// sequence of linear, quadratic or cubic segments.
type Path []fixed.Int26_6
// String returns a human-readable representation of a Path.
func (p Path) String() string {
s := ""
for i := 0; i < len(p); {
if i != 0 {
s += " "
}
switch p[i] {
case 0:
s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
i += 4
case 1:
s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
i += 4
case 2:
s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5]))
i += 6
case 3:
s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7]))
i += 8
default:
panic("freetype/raster: bad path")
}
}
return s
}
// Clear cancels any previous calls to p.Start or p.AddXxx.
func (p *Path) Clear() {
*p = (*p)[:0]
}
// Start starts a new curve at the given point.
func (p *Path) Start(a fixed.Point26_6) {
*p = append(*p, 0, a.X, a.Y, 0)
}
// Add1 adds a linear segment to the current curve.
func (p *Path) Add1(b fixed.Point26_6) {
*p = append(*p, 1, b.X, b.Y, 1)
}
// Add2 adds a quadratic segment to the current curve.
func (p *Path) Add2(b, c fixed.Point26_6) {
*p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
}
// Add3 adds a cubic segment to the current curve.
func (p *Path) Add3(b, c, d fixed.Point26_6) {
*p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
}
// AddPath adds the Path q to p.
func (p *Path) AddPath(q Path) {
*p = append(*p, q...)
}
// AddStroke adds a stroked Path.
func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
Stroke(p, q, width, cr, jr)
}
// firstPoint returns the first point in a non-empty Path.
func (p Path) firstPoint() fixed.Point26_6 {
return fixed.Point26_6{p[1], p[2]}
}
// lastPoint returns the last point in a non-empty Path.
func (p Path) lastPoint() fixed.Point26_6 {
return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
}
// addPathReversed adds q reversed to p.
// For example, if q consists of a linear segment from A to B followed by a
// quadratic segment from B to C to D, then the values of q looks like:
// index: 01234567890123
// value: 0AA01BB12CCDD2
// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
func addPathReversed(p Adder, q Path) {
if len(q) == 0 {
return
}
i := len(q) - 1
for {
switch q[i] {
case 0:
return
case 1:
i -= 4
p.Add1(
fixed.Point26_6{q[i-2], q[i-1]},
)
case 2:
i -= 6
p.Add2(
fixed.Point26_6{q[i+2], q[i+3]},
fixed.Point26_6{q[i-2], q[i-1]},
)
case 3:
i -= 8
p.Add3(
fixed.Point26_6{q[i+4], q[i+5]},
fixed.Point26_6{q[i+2], q[i+3]},
fixed.Point26_6{q[i-2], q[i-1]},
)
default:
panic("freetype/raster: bad path")
}
}
}

287
vendor/github.com/golang/freetype/raster/paint.go generated vendored Normal file

@ -0,0 +1,287 @@
// Copyright 2010 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 raster
import (
"image"
"image/color"
"image/draw"
"math"
)
// A Span is a horizontal segment of pixels with constant alpha. X0 is an
// inclusive bound and X1 is exclusive, the same as for slices. A fully opaque
// Span has Alpha == 0xffff.
type Span struct {
Y, X0, X1 int
Alpha uint32
}
// A Painter knows how to paint a batch of Spans. Rasterization may involve
// Painting multiple batches, and done will be true for the final batch. The
// Spans' Y values are monotonically increasing during a rasterization. Paint
// may use all of ss as scratch space during the call.
type Painter interface {
Paint(ss []Span, done bool)
}
// The PainterFunc type adapts an ordinary function to the Painter interface.
type PainterFunc func(ss []Span, done bool)
// Paint just delegates the call to f.
func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
// An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using
// the Over Porter-Duff composition operator.
type AlphaOverPainter struct {
Image *image.Alpha
}
// Paint satisfies the Painter interface.
func (r AlphaOverPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
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-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
p := r.Image.Pix[base+s.X0 : base+s.X1]
a := int(s.Alpha >> 8)
for i, c := range p {
v := int(c)
p[i] = uint8((v*255 + (255-v)*a) / 255)
}
}
}
// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
return AlphaOverPainter{m}
}
// An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using
// the Src Porter-Duff composition operator.
type AlphaSrcPainter struct {
Image *image.Alpha
}
// Paint satisfies the Painter interface.
func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
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-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
p := r.Image.Pix[base+s.X0 : base+s.X1]
color := uint8(s.Alpha >> 8)
for i := range p {
p[i] = color
}
}
}
// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
return AlphaSrcPainter{m}
}
// An RGBAPainter is a Painter that paints Spans onto a *image.RGBA.
type RGBAPainter struct {
// Image is the image to compose onto.
Image *image.RGBA
// Op is the Porter-Duff composition operator.
Op draw.Op
// cr, cg, cb and ca are the 16-bit color to paint the spans.
cr, cg, cb, ca uint32
}
// Paint satisfies the Painter interface.
func (r *RGBAPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
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
}
// This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go.
ma := s.Alpha
const m = 1<<16 - 1
i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
i1 := i0 + (s.X1-s.X0)*4
if r.Op == draw.Over {
for i := i0; i < i1; i += 4 {
dr := uint32(r.Image.Pix[i+0])
dg := uint32(r.Image.Pix[i+1])
db := uint32(r.Image.Pix[i+2])
da := uint32(r.Image.Pix[i+3])
a := (m - (r.ca * ma / m)) * 0x101
r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
}
} else {
for i := i0; i < i1; i += 4 {
r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
}
}
}
}
// SetColor sets the color to paint the spans.
func (r *RGBAPainter) SetColor(c color.Color) {
r.cr, r.cg, r.cb, r.ca = c.RGBA()
}
// NewRGBAPainter creates a new RGBAPainter for the given image.
func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
return &RGBAPainter{Image: m}
}
// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
// be either fully opaque or fully transparent.
type MonochromePainter struct {
Painter Painter
y, x0, x1 int
}
// Paint delegates to the wrapped Painter after quantizing each Span's alpha
// value and merging adjacent fully opaque Spans.
func (m *MonochromePainter) Paint(ss []Span, done bool) {
// We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
j := 0
for _, s := range ss {
if s.Alpha >= 0x8000 {
if m.y == s.Y && m.x1 == s.X0 {
m.x1 = s.X1
} else {
ss[j] = Span{m.y, m.x0, m.x1, 1<<16 - 1}
j++
m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
}
}
}
if done {
// Flush the accumulated Span.
finalSpan := Span{m.y, m.x0, m.x1, 1<<16 - 1}
if j < len(ss) {
ss[j] = finalSpan
j++
m.Painter.Paint(ss[:j], true)
} else if j == len(ss) {
m.Painter.Paint(ss, false)
if cap(ss) > 0 {
ss = ss[:1]
} else {
ss = make([]Span, 1)
}
ss[0] = finalSpan
m.Painter.Paint(ss, true)
} else {
panic("unreachable")
}
// Reset the accumulator, so that this Painter can be re-used.
m.y, m.x0, m.x1 = 0, 0, 0
} else {
m.Painter.Paint(ss[:j], false)
}
}
// NewMonochromePainter creates a new MonochromePainter that wraps the given
// Painter.
func NewMonochromePainter(p Painter) *MonochromePainter {
return &MonochromePainter{Painter: p}
}
// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
// on each Span's alpha value.
type GammaCorrectionPainter struct {
// Painter is the wrapped Painter.
Painter Painter
// a is the precomputed alpha values for linear interpolation, with fully
// opaque == 0xffff.
a [256]uint16
// gammaIsOne is whether gamma correction is a no-op.
gammaIsOne bool
}
// Paint delegates to the wrapped Painter after performing gamma-correction on
// each Span.
func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
if !g.gammaIsOne {
const n = 0x101
for i, s := range ss {
if s.Alpha == 0 || s.Alpha == 0xffff {
continue
}
p, q := s.Alpha/n, s.Alpha%n
// The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q
ss[i].Alpha = (a + n/2) / n
}
}
g.Painter.Paint(ss, done)
}
// SetGamma sets the gamma value.
func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
g.gammaIsOne = gamma == 1
if g.gammaIsOne {
return
}
for i := 0; i < 256; i++ {
a := float64(i) / 0xff
a = math.Pow(a, gamma)
g.a[i] = uint16(0xffff * a)
}
}
// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
// the given Painter.
func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
g := &GammaCorrectionPainter{Painter: p}
g.SetGamma(gamma)
return g
}

601
vendor/github.com/golang/freetype/raster/raster.go generated vendored Normal file

@ -0,0 +1,601 @@
// Copyright 2010 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 raster provides an anti-aliasing 2-D rasterizer.
//
// It is part of the larger Freetype suite of font-related packages, but the
// raster package is not specific to font rasterization, and can be used
// standalone without any other Freetype package.
//
// Rasterization is done by the same area/coverage accumulation algorithm as
// the Freetype "smooth" module, and the Anti-Grain Geometry library. A
// description of the area/coverage algorithm is at
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
package raster // import "github.com/golang/freetype/raster"
import (
"strconv"
"golang.org/x/image/math/fixed"
)
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
// area/coverage for the pixel at (xi, yi).
type cell struct {
xi int
area, cover int
next int
}
type Rasterizer struct {
// If false, the default behavior is to use the even-odd winding fill
// rule during Rasterize.
UseNonZeroWinding bool
// An offset (in pixels) to the painted spans.
Dx, Dy int
// The width of the Rasterizer. The height is implicit in len(cellIndex).
width int
// splitScaleN is the scaling factor used to determine how many times
// to decompose a quadratic or cubic segment into a linear approximation.
splitScale2, splitScale3 int
// The current pen position.
a fixed.Point26_6
// The current cell and its area/coverage being accumulated.
xi, yi int
area, cover int
// Saved cells.
cell []cell
// Linked list of cells, one per row.
cellIndex []int
// Buffers.
cellBuf [256]cell
cellIndexBuf [64]int
spanBuf [64]Span
}
// findCell returns the index in r.cell for the cell corresponding to
// (r.xi, r.yi). The cell is created if necessary.
func (r *Rasterizer) findCell() int {
if r.yi < 0 || r.yi >= len(r.cellIndex) {
return -1
}
xi := r.xi
if xi < 0 {
xi = -1
} else if xi > r.width {
xi = r.width
}
i, prev := r.cellIndex[r.yi], -1
for i != -1 && r.cell[i].xi <= xi {
if r.cell[i].xi == xi {
return i
}
i, prev = r.cell[i].next, i
}
c := len(r.cell)
if c == cap(r.cell) {
buf := make([]cell, c, 4*c)
copy(buf, r.cell)
r.cell = buf[0 : c+1]
} else {
r.cell = r.cell[0 : c+1]
}
r.cell[c] = cell{xi, 0, 0, i}
if prev == -1 {
r.cellIndex[r.yi] = c
} else {
r.cell[prev].next = c
}
return c
}
// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
func (r *Rasterizer) saveCell() {
if r.area != 0 || r.cover != 0 {
i := r.findCell()
if i != -1 {
r.cell[i].area += r.area
r.cell[i].cover += r.cover
}
r.area = 0
r.cover = 0
}
}
// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
func (r *Rasterizer) setCell(xi, yi int) {
if r.xi != xi || r.yi != yi {
r.saveCell()
r.xi, r.yi = xi, yi
}
}
// scan accumulates area/coverage for the yi'th scanline, going from
// x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates)
// and from y0f to y1f fractional vertical units within that scanline.
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
// Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
x0i := int(x0) / 64
x0f := x0 - fixed.Int26_6(64*x0i)
x1i := int(x1) / 64
x1f := x1 - fixed.Int26_6(64*x1i)
// A perfectly horizontal scan.
if y0f == y1f {
r.setCell(x1i, yi)
return
}
dx, dy := x1-x0, y1f-y0f
// A single cell scan.
if x0i == x1i {
r.area += int((x0f + x1f) * dy)
r.cover += int(dy)
return
}
// There are at least two cells. Apart from the first and last cells,
// all intermediate cells go through the full width of the cell,
// or 64 units in 26.6 fixed point format.
var (
p, q, edge0, edge1 fixed.Int26_6
xiDelta int
)
if dx > 0 {
p, q = (64-x0f)*dy, dx
edge0, edge1, xiDelta = 0, 64, 1
} else {
p, q = x0f*dy, -dx
edge0, edge1, xiDelta = 64, 0, -1
}
yDelta, yRem := p/q, p%q
if yRem < 0 {
yDelta -= 1
yRem += q
}
// Do the first cell.
xi, y := x0i, y0f
r.area += int((x0f + edge1) * yDelta)
r.cover += int(yDelta)
xi, y = xi+xiDelta, y+yDelta
r.setCell(xi, yi)
if xi != x1i {
// Do all the intermediate cells.
p = 64 * (y1f - y + yDelta)
fullDelta, fullRem := p/q, p%q
if fullRem < 0 {
fullDelta -= 1
fullRem += q
}
yRem -= q
for xi != x1i {
yDelta = fullDelta
yRem += fullRem
if yRem >= 0 {
yDelta += 1
yRem -= q
}
r.area += int(64 * yDelta)
r.cover += int(yDelta)
xi, y = xi+xiDelta, y+yDelta
r.setCell(xi, yi)
}
}
// Do the last cell.
yDelta = y1f - y
r.area += int((edge0 + x1f) * yDelta)
r.cover += int(yDelta)
}
// Start starts a new curve at the given point.
func (r *Rasterizer) Start(a fixed.Point26_6) {
r.setCell(int(a.X/64), int(a.Y/64))
r.a = a
}
// Add1 adds a linear segment to the current curve.
func (r *Rasterizer) Add1(b fixed.Point26_6) {
x0, y0 := r.a.X, r.a.Y
x1, y1 := b.X, b.Y
dx, dy := x1-x0, y1-y0
// Break the 26.6 fixed point Y co-ordinates into integral and fractional
// parts.
y0i := int(y0) / 64
y0f := y0 - fixed.Int26_6(64*y0i)
y1i := int(y1) / 64
y1f := y1 - fixed.Int26_6(64*y1i)
if y0i == y1i {
// There is only one scanline.
r.scan(y0i, x0, y0f, x1, y1f)
} else if dx == 0 {
// This is a vertical line segment. We avoid calling r.scan and instead
// manipulate r.area and r.cover directly.
var (
edge0, edge1 fixed.Int26_6
yiDelta int
)
if dy > 0 {
edge0, edge1, yiDelta = 0, 64, 1
} else {
edge0, edge1, yiDelta = 64, 0, -1
}
x0i, yi := int(x0)/64, y0i
x0fTimes2 := (int(x0) - (64 * x0i)) * 2
// Do the first pixel.
dcover := int(edge1 - y0f)
darea := int(x0fTimes2 * dcover)
r.area += darea
r.cover += dcover
yi += yiDelta
r.setCell(x0i, yi)
// Do all the intermediate pixels.
dcover = int(edge1 - edge0)
darea = int(x0fTimes2 * dcover)
for yi != y1i {
r.area += darea
r.cover += dcover
yi += yiDelta
r.setCell(x0i, yi)
}
// Do the last pixel.
dcover = int(y1f - edge0)
darea = int(x0fTimes2 * dcover)
r.area += darea
r.cover += dcover
} else {
// There are at least two scanlines. Apart from the first and last
// scanlines, all intermediate scanlines go through the full height of
// the row, or 64 units in 26.6 fixed point format.
var (
p, q, edge0, edge1 fixed.Int26_6
yiDelta int
)
if dy > 0 {
p, q = (64-y0f)*dx, dy
edge0, edge1, yiDelta = 0, 64, 1
} else {
p, q = y0f*dx, -dy
edge0, edge1, yiDelta = 64, 0, -1
}
xDelta, xRem := p/q, p%q
if xRem < 0 {
xDelta -= 1
xRem += q
}
// Do the first scanline.
x, yi := x0, y0i
r.scan(yi, x, y0f, x+xDelta, edge1)
x, yi = x+xDelta, yi+yiDelta
r.setCell(int(x)/64, yi)
if yi != y1i {
// Do all the intermediate scanlines.
p = 64 * dx
fullDelta, fullRem := p/q, p%q
if fullRem < 0 {
fullDelta -= 1
fullRem += q
}
xRem -= q
for yi != y1i {
xDelta = fullDelta
xRem += fullRem
if xRem >= 0 {
xDelta += 1
xRem -= q
}
r.scan(yi, x, edge0, x+xDelta, edge1)
x, yi = x+xDelta, yi+yiDelta
r.setCell(int(x)/64, yi)
}
}
// Do the last scanline.
r.scan(yi, x, edge0, x1, y1f)
}
// The next lineTo starts from b.
r.a = b
}
// Add2 adds a quadratic segment to the current curve.
func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
// Calculate nSplit (the number of recursive decompositions) based on how
// 'curvy' it is. Specifically, how much the middle point b deviates from
// (a+c)/2.
dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2)
nsplit := 0
for dev > 0 {
dev /= 4
nsplit++
}
// dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit
// is 16.
const maxNsplit = 16
if nsplit > maxNsplit {
panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
}
// Recursively decompose the curve nSplit levels deep.
var (
pStack [2*maxNsplit + 3]fixed.Point26_6
sStack [maxNsplit + 1]int
i int
)
sStack[0] = nsplit
pStack[0] = c
pStack[1] = b
pStack[2] = r.a
for i >= 0 {
s := sStack[i]
p := pStack[2*i:]
if s > 0 {
// Split the quadratic curve p[:3] into an equivalent set of two
// shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2],
// and p[0] is unchanged.
mx := p[1].X
p[4].X = p[2].X
p[3].X = (p[4].X + mx) / 2
p[1].X = (p[0].X + mx) / 2
p[2].X = (p[1].X + p[3].X) / 2
my := p[1].Y
p[4].Y = p[2].Y
p[3].Y = (p[4].Y + my) / 2
p[1].Y = (p[0].Y + my) / 2
p[2].Y = (p[1].Y + p[3].Y) / 2
// The two shorter curves have one less split to do.
sStack[i] = s - 1
sStack[i+1] = s - 1
i++
} else {
// Replace the level-0 quadratic with a two-linear-piece
// approximation.
midx := (p[0].X + 2*p[1].X + p[2].X) / 4
midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
r.Add1(fixed.Point26_6{midx, midy})
r.Add1(p[0])
i--
}
}
}
// Add3 adds a cubic segment to the current curve.
func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) {
// Calculate nSplit (the number of recursive decompositions) based on how
// 'curvy' it is.
dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2)
dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3)
nsplit := 0
for dev2 > 0 || dev3 > 0 {
dev2 /= 8
dev3 /= 4
nsplit++
}
// devN is 32-bit, and nsplit++ every time we shift off 2 bits, so
// maxNsplit is 16.
const maxNsplit = 16
if nsplit > maxNsplit {
panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
}
// Recursively decompose the curve nSplit levels deep.
var (
pStack [3*maxNsplit + 4]fixed.Point26_6
sStack [maxNsplit + 1]int
i int
)
sStack[0] = nsplit
pStack[0] = d
pStack[1] = c
pStack[2] = b
pStack[3] = r.a
for i >= 0 {
s := sStack[i]
p := pStack[3*i:]
if s > 0 {
// Split the cubic curve p[:4] into an equivalent set of two
// shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3],
// and p[0] is unchanged.
m01x := (p[0].X + p[1].X) / 2
m12x := (p[1].X + p[2].X) / 2
m23x := (p[2].X + p[3].X) / 2
p[6].X = p[3].X
p[5].X = m23x
p[1].X = m01x
p[2].X = (m01x + m12x) / 2
p[4].X = (m12x + m23x) / 2
p[3].X = (p[2].X + p[4].X) / 2
m01y := (p[0].Y + p[1].Y) / 2
m12y := (p[1].Y + p[2].Y) / 2
m23y := (p[2].Y + p[3].Y) / 2
p[6].Y = p[3].Y
p[5].Y = m23y
p[1].Y = m01y
p[2].Y = (m01y + m12y) / 2
p[4].Y = (m12y + m23y) / 2
p[3].Y = (p[2].Y + p[4].Y) / 2
// The two shorter curves have one less split to do.
sStack[i] = s - 1
sStack[i+1] = s - 1
i++
} else {
// Replace the level-0 cubic with a two-linear-piece approximation.
midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
r.Add1(fixed.Point26_6{midx, midy})
r.Add1(p[0])
i--
}
}
}
// AddPath adds the given Path.
func (r *Rasterizer) AddPath(p Path) {
for i := 0; i < len(p); {
switch p[i] {
case 0:
r.Start(
fixed.Point26_6{p[i+1], p[i+2]},
)
i += 4
case 1:
r.Add1(
fixed.Point26_6{p[i+1], p[i+2]},
)
i += 4
case 2:
r.Add2(
fixed.Point26_6{p[i+1], p[i+2]},
fixed.Point26_6{p[i+3], p[i+4]},
)
i += 6
case 3:
r.Add3(
fixed.Point26_6{p[i+1], p[i+2]},
fixed.Point26_6{p[i+3], p[i+4]},
fixed.Point26_6{p[i+5], p[i+6]},
)
i += 8
default:
panic("freetype/raster: bad path")
}
}
}
// AddStroke adds a stroked Path.
func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
Stroke(r, q, width, cr, jr)
}
// areaToAlpha converts an area value to a uint32 alpha value. A completely
// filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The
// conversion of area values greater than this depends on the winding rule:
// even-odd or non-zero.
func (r *Rasterizer) areaToAlpha(area int) uint32 {
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
// without the +1. Round-to-nearest gives a more symmetric result than
// round-down. The C implementation also returns 8-bit alpha, not 16-bit
// alpha.
a := (area + 1) >> 1
if a < 0 {
a = -a
}
alpha := uint32(a)
if r.UseNonZeroWinding {
if alpha > 0x0fff {
alpha = 0x0fff
}
} else {
alpha &= 0x1fff
if alpha > 0x1000 {
alpha = 0x2000 - alpha
} else if alpha == 0x1000 {
alpha = 0x0fff
}
}
// alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to
// 16-bit alpha.
return alpha<<4 | alpha>>8
}
// Rasterize converts r's accumulated curves into Spans for p. The Spans passed
// to p are non-overlapping, and sorted by Y and then X. They all have non-zero
// width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
// Span, which has Y, X0, X1 and A all equal to zero.
func (r *Rasterizer) Rasterize(p Painter) {
r.saveCell()
s := 0
for yi := 0; yi < len(r.cellIndex); yi++ {
xi, cover := 0, 0
for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
if cover != 0 && r.cell[c].xi > xi {
alpha := r.areaToAlpha(cover * 64 * 2)
if alpha != 0 {
xi0, xi1 := xi, r.cell[c].xi
if xi0 < 0 {
xi0 = 0
}
if xi1 >= r.width {
xi1 = r.width
}
if xi0 < xi1 {
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
s++
}
}
}
cover += r.cell[c].cover
alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area)
xi = r.cell[c].xi + 1
if alpha != 0 {
xi0, xi1 := r.cell[c].xi, xi
if xi0 < 0 {
xi0 = 0
}
if xi1 >= r.width {
xi1 = r.width
}
if xi0 < xi1 {
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
s++
}
}
if s > len(r.spanBuf)-2 {
p.Paint(r.spanBuf[:s], false)
s = 0
}
}
}
p.Paint(r.spanBuf[:s], true)
}
// Clear cancels any previous calls to r.Start or r.AddXxx.
func (r *Rasterizer) Clear() {
r.a = fixed.Point26_6{}
r.xi = 0
r.yi = 0
r.area = 0
r.cover = 0
r.cell = r.cell[:0]
for i := 0; i < len(r.cellIndex); i++ {
r.cellIndex[i] = -1
}
}
// SetBounds sets the maximum width and height of the rasterized image and
// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
func (r *Rasterizer) SetBounds(width, height int) {
if width < 0 {
width = 0
}
if height < 0 {
height = 0
}
// Use the same ssN heuristic as the C Freetype (version 2.4.0)
// implementation.
ss2, ss3 := 32, 16
if width > 24 || height > 24 {
ss2, ss3 = 2*ss2, 2*ss3
if width > 120 || height > 120 {
ss2, ss3 = 2*ss2, 2*ss3
}
}
r.width = width
r.splitScale2 = ss2
r.splitScale3 = ss3
r.cell = r.cellBuf[:0]
if height > len(r.cellIndexBuf) {
r.cellIndex = make([]int, height)
} else {
r.cellIndex = r.cellIndexBuf[:height]
}
r.Clear()
}
// NewRasterizer creates a new Rasterizer with the given bounds.
func NewRasterizer(width, height int) *Rasterizer {
r := new(Rasterizer)
r.SetBounds(width, height)
return r
}

483
vendor/github.com/golang/freetype/raster/stroke.go generated vendored Normal file

@ -0,0 +1,483 @@
// Copyright 2010 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 raster
import (
"golang.org/x/image/math/fixed"
)
// Two points are considered practically equal if the square of the distance
// between them is less than one quarter (i.e. 1024 / 4096).
const epsilon = fixed.Int52_12(1024)
// A Capper signifies how to begin or end a stroked path.
type Capper interface {
// Cap adds a cap to p given a pivot point and the normal vector of a
// terminal segment. The normal's length is half of the stroke width.
Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6)
}
// The CapperFunc type adapts an ordinary function to be a Capper.
type CapperFunc func(Adder, fixed.Int26_6, fixed.Point26_6, fixed.Point26_6)
func (f CapperFunc) Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
f(p, halfWidth, pivot, n1)
}
// A Joiner signifies how to join interior nodes of a stroked path.
type Joiner interface {
// Join adds a join to the two sides of a stroked path given a pivot
// point and the normal vectors of the trailing and leading segments.
// Both normals have length equal to half of the stroke width.
Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
}
// The JoinerFunc type adapts an ordinary function to be a Joiner.
type JoinerFunc func(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
f(lhs, rhs, halfWidth, pivot, n0, n1)
}
// RoundCapper adds round caps to a stroked path.
var RoundCapper Capper = CapperFunc(roundCapper)
func roundCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
// The cubic Bézier approximation to a circle involves the magic number
// (√2 - 1) * 4/3, which is approximately 35/64.
const k = 35
e := pRot90CCW(n1)
side := pivot.Add(e)
start, end := pivot.Sub(n1), pivot.Add(n1)
d, e := n1.Mul(k), e.Mul(k)
p.Add3(start.Add(e), side.Sub(d), side)
p.Add3(side.Add(d), end.Add(e), end)
}
// ButtCapper adds butt caps to a stroked path.
var ButtCapper Capper = CapperFunc(buttCapper)
func buttCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
p.Add1(pivot.Add(n1))
}
// SquareCapper adds square caps to a stroked path.
var SquareCapper Capper = CapperFunc(squareCapper)
func squareCapper(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
e := pRot90CCW(n1)
side := pivot.Add(e)
p.Add1(side.Sub(n1))
p.Add1(side.Add(n1))
p.Add1(pivot.Add(n1))
}
// RoundJoiner adds round joins to a stroked path.
var RoundJoiner Joiner = JoinerFunc(roundJoiner)
func roundJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
dot := pDot(pRot90CW(n0), n1)
if dot >= 0 {
addArc(lhs, pivot, n0, n1)
rhs.Add1(pivot.Sub(n1))
} else {
lhs.Add1(pivot.Add(n1))
addArc(rhs, pivot, pNeg(n0), pNeg(n1))
}
}
// BevelJoiner adds bevel joins to a stroked path.
var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
func bevelJoiner(lhs, rhs Adder, haflWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6) {
lhs.Add1(pivot.Add(n1))
rhs.Add1(pivot.Sub(n1))
}
// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The
// two vectors n0 and n1 must be of equal length.
func addArc(p Adder, pivot, n0, n1 fixed.Point26_6) {
// r2 is the square of the length of n0.
r2 := pDot(n0, n0)
if r2 < epsilon {
// The arc radius is so small that we collapse to a straight line.
p.Add1(pivot.Add(n1))
return
}
// We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
// a final quadratic segment from s to n1. Each 45-degree segment has
// control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
// rotated and translated. tan(π/8) is approximately 27/64.
const tpo8 = 27
var s fixed.Point26_6
// We determine which octant the angle between n0 and n1 is in via three
// dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135
// degrees.
m0 := pRot45CW(n0)
m1 := pRot90CW(n0)
m2 := pRot90CW(m0)
if pDot(m1, n1) >= 0 {
if pDot(n0, n1) >= 0 {
if pDot(m2, n1) <= 0 {
// n1 is between 0 and 45 degrees clockwise of n0.
s = n0
} else {
// n1 is between 45 and 90 degrees clockwise of n0.
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
s = m0
}
} else {
pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
p.Add2(pm1.Add(n0t), pm1)
if pDot(m0, n1) >= 0 {
// n1 is between 90 and 135 degrees clockwise of n0.
s = m1
} else {
// n1 is between 135 and 180 degrees clockwise of n0.
p.Add2(pm1.Sub(n0t), pivot.Add(m2))
s = m2
}
}
} else {
if pDot(n0, n1) >= 0 {
if pDot(m0, n1) >= 0 {
// n1 is between 0 and 45 degrees counter-clockwise of n0.
s = n0
} else {
// n1 is between 45 and 90 degrees counter-clockwise of n0.
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
s = pNeg(m2)
}
} else {
pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
p.Add2(pm1.Add(n0t), pm1)
if pDot(m2, n1) <= 0 {
// n1 is between 90 and 135 degrees counter-clockwise of n0.
s = pNeg(m1)
} else {
// n1 is between 135 and 180 degrees counter-clockwise of n0.
p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
s = pNeg(m0)
}
}
}
// The final quadratic segment has two endpoints s and n1 and the middle
// control point is a multiple of s.Add(n1), i.e. it is on the angle
// bisector of those two points. The multiple ranges between 128/256 and
// 150/256 as the angle between s and n1 ranges between 0 and 45 degrees.
//
// When the angle is 0 degrees (i.e. s and n1 are coincident) then
// s.Add(n1) is twice s and so the middle control point of the degenerate
// quadratic segment should be half s.Add(n1), and half = 128/256.
//
// When the angle is 45 degrees then 150/256 is the ratio of the lengths of
// the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
//
// d is the normalized dot product between s and n1. Since the angle ranges
// between 0 and 45 degrees then d ranges between 256/256 and 181/256.
d := 256 * pDot(s, n1) / r2
multiple := fixed.Int26_6(150-(150-128)*(d-181)/(256-181)) >> 2
p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
}
// midpoint returns the midpoint of two Points.
func midpoint(a, b fixed.Point26_6) fixed.Point26_6 {
return fixed.Point26_6{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
}
// angleGreaterThan45 returns whether the angle between two vectors is more
// than 45 degrees.
func angleGreaterThan45(v0, v1 fixed.Point26_6) bool {
v := pRot45CCW(v0)
return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0
}
// interpolate returns the point (1-t)*a + t*b.
func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 {
s := 1<<12 - t
x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X)
y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y)
return fixed.Point26_6{fixed.Int26_6(x >> 12), fixed.Int26_6(y >> 12)}
}
// curviest2 returns the value of t for which the quadratic parametric curve
// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
//
// The curvature of the parametric curve f(t) = (x(t), y(t)) is
// |xy″-yx″| / (x²+y²)^(3/2).
//
// Let d = b-a and e = c-2*b+a, so that f(t) = 2*d+2*e*t and f″(t) = 2*e.
// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
//
// Thus, curvature is extreme where the denominator is extreme, i.e. where
// (x²+y²) is extreme. The first order condition is that
// 2*x*x″+2*y*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
func curviest2(a, b, c fixed.Point26_6) fixed.Int52_12 {
dx := int64(b.X - a.X)
dy := int64(b.Y - a.Y)
ex := int64(c.X - 2*b.X + a.X)
ey := int64(c.Y - 2*b.Y + a.Y)
if ex == 0 && ey == 0 {
return 2048
}
return fixed.Int52_12(-4096 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
}
// A stroker holds state for stroking a path.
type stroker struct {
// p is the destination that records the stroked path.
p Adder
// u is the half-width of the stroke.
u fixed.Int26_6
// cr and jr specify how to end and connect path segments.
cr Capper
jr Joiner
// r is the reverse path. Stroking a path involves constructing two
// parallel paths 2*u apart. The first path is added immediately to p,
// the second path is accumulated in r and eventually added in reverse.
r Path
// a is the most recent segment point. anorm is the segment normal of
// length u at that point.
a, anorm fixed.Point26_6
}
// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
func (k *stroker) addNonCurvy2(b, c fixed.Point26_6) {
// We repeatedly divide the segment at its middle until it is straight
// enough to approximate the stroke by just translating the control points.
// ds and ps are stacks of depths and points. t is the top of the stack.
const maxDepth = 5
var (
ds [maxDepth + 1]int
ps [2*maxDepth + 3]fixed.Point26_6
t int
)
// Initially the ps stack has one quadratic segment of depth zero.
ds[0] = 0
ps[2] = k.a
ps[1] = b
ps[0] = c
anorm := k.anorm
var cnorm fixed.Point26_6
for {
depth := ds[t]
a := ps[2*t+2]
b := ps[2*t+1]
c := ps[2*t+0]
ab := b.Sub(a)
bc := c.Sub(b)
abIsSmall := pDot(ab, ab) < fixed.Int52_12(1<<12)
bcIsSmall := pDot(bc, bc) < fixed.Int52_12(1<<12)
if abIsSmall && bcIsSmall {
// Approximate the segment by a circular arc.
cnorm = pRot90CCW(pNorm(bc, k.u))
mac := midpoint(a, c)
addArc(k.p, mac, anorm, cnorm)
addArc(&k.r, mac, pNeg(anorm), pNeg(cnorm))
} else if depth < maxDepth && angleGreaterThan45(ab, bc) {
// Divide the segment in two and push both halves on the stack.
mab := midpoint(a, b)
mbc := midpoint(b, c)
t++
ds[t+0] = depth + 1
ds[t-1] = depth + 1
ps[2*t+2] = a
ps[2*t+1] = mab
ps[2*t+0] = midpoint(mab, mbc)
ps[2*t-1] = mbc
continue
} else {
// Translate the control points.
bnorm := pRot90CCW(pNorm(c.Sub(a), k.u))
cnorm = pRot90CCW(pNorm(bc, k.u))
k.p.Add2(b.Add(bnorm), c.Add(cnorm))
k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
}
if t == 0 {
k.a, k.anorm = c, cnorm
return
}
t--
anorm = cnorm
}
panic("unreachable")
}
// Add1 adds a linear segment to the stroker.
func (k *stroker) Add1(b fixed.Point26_6) {
bnorm := pRot90CCW(pNorm(b.Sub(k.a), k.u))
if len(k.r) == 0 {
k.p.Start(k.a.Add(bnorm))
k.r.Start(k.a.Sub(bnorm))
} else {
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
}
k.p.Add1(b.Add(bnorm))
k.r.Add1(b.Sub(bnorm))
k.a, k.anorm = b, bnorm
}
// Add2 adds a quadratic segment to the stroker.
func (k *stroker) Add2(b, c fixed.Point26_6) {
ab := b.Sub(k.a)
bc := c.Sub(b)
abnorm := pRot90CCW(pNorm(ab, k.u))
if len(k.r) == 0 {
k.p.Start(k.a.Add(abnorm))
k.r.Start(k.a.Sub(abnorm))
} else {
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
}
// Approximate nearly-degenerate quadratics by linear segments.
abIsSmall := pDot(ab, ab) < epsilon
bcIsSmall := pDot(bc, bc) < epsilon
if abIsSmall || bcIsSmall {
acnorm := pRot90CCW(pNorm(c.Sub(k.a), k.u))
k.p.Add1(c.Add(acnorm))
k.r.Add1(c.Sub(acnorm))
k.a, k.anorm = c, acnorm
return
}
// The quadratic segment (k.a, b, c) has a point of maximum curvature.
// If this occurs at an end point, we process the segment as a whole.
t := curviest2(k.a, b, c)
if t <= 0 || 4096 <= t {
k.addNonCurvy2(b, c)
return
}
// Otherwise, we perform a de Casteljau decomposition at the point of
// maximum curvature and process the two straighter parts.
mab := interpolate(k.a, b, t)
mbc := interpolate(b, c, t)
mabc := interpolate(mab, mbc, t)
// If the vectors ab and bc are close to being in opposite directions,
// then the decomposition can become unstable, so we approximate the
// quadratic segment by two linear segments joined by an arc.
bcnorm := pRot90CCW(pNorm(bc, k.u))
if pDot(abnorm, bcnorm) < -fixed.Int52_12(k.u)*fixed.Int52_12(k.u)*2047/2048 {
pArc := pDot(abnorm, bc) < 0
k.p.Add1(mabc.Add(abnorm))
if pArc {
z := pRot90CW(abnorm)
addArc(k.p, mabc, abnorm, z)
addArc(k.p, mabc, z, bcnorm)
}
k.p.Add1(mabc.Add(bcnorm))
k.p.Add1(c.Add(bcnorm))
k.r.Add1(mabc.Sub(abnorm))
if !pArc {
z := pRot90CW(abnorm)
addArc(&k.r, mabc, pNeg(abnorm), z)
addArc(&k.r, mabc, z, pNeg(bcnorm))
}
k.r.Add1(mabc.Sub(bcnorm))
k.r.Add1(c.Sub(bcnorm))
k.a, k.anorm = c, bcnorm
return
}
// Process the decomposed parts.
k.addNonCurvy2(mab, mabc)
k.addNonCurvy2(mbc, c)
}
// Add3 adds a cubic segment to the stroker.
func (k *stroker) Add3(b, c, d fixed.Point26_6) {
panic("freetype/raster: stroke unimplemented for cubic segments")
}
// stroke adds the stroked Path q to p, where q consists of exactly one curve.
func (k *stroker) stroke(q Path) {
// Stroking is implemented by deriving two paths each k.u apart from q.
// The left-hand-side path is added immediately to k.p; the right-hand-side
// path is accumulated in k.r. Once we've finished adding the LHS to k.p,
// we add the RHS in reverse order.
k.r = make(Path, 0, len(q))
k.a = fixed.Point26_6{q[1], q[2]}
for i := 4; i < len(q); {
switch q[i] {
case 1:
k.Add1(
fixed.Point26_6{q[i+1], q[i+2]},
)
i += 4
case 2:
k.Add2(
fixed.Point26_6{q[i+1], q[i+2]},
fixed.Point26_6{q[i+3], q[i+4]},
)
i += 6
case 3:
k.Add3(
fixed.Point26_6{q[i+1], q[i+2]},
fixed.Point26_6{q[i+3], q[i+4]},
fixed.Point26_6{q[i+5], q[i+6]},
)
i += 8
default:
panic("freetype/raster: bad path")
}
}
if len(k.r) == 0 {
return
}
// TODO(nigeltao): if q is a closed curve then we should join the first and
// last segments instead of capping them.
k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm))
addPathReversed(k.p, k.r)
pivot := q.firstPoint()
k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]}))
}
// Stroke adds q stroked with the given width to p. The result is typically
// self-intersecting and should be rasterized with UseNonZeroWinding.
// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
func Stroke(p Adder, q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
if len(q) == 0 {
return
}
if cr == nil {
cr = RoundCapper
}
if jr == nil {
jr = RoundJoiner
}
if q[0] != 0 {
panic("freetype/raster: bad path")
}
s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
i := 0
for j := 4; j < len(q); {
switch q[j] {
case 0:
s.stroke(q[i:j])
i, j = j, j+4
case 1:
j += 4
case 2:
j += 6
case 3:
j += 8
default:
panic("freetype/raster: bad path")
}
}
s.stroke(q[i:])
}

507
vendor/github.com/golang/freetype/truetype/face.go generated vendored Normal file

@ -0,0 +1,507 @@
// 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"
"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
}
// 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
// GlyphCacheEntries is the number of entries in the glyph mask image
// cache.
//
// If non-zero, it must be a power of 2.
//
// A zero value means to use 512 entries.
GlyphCacheEntries int
// SubPixelsX is the number of sub-pixel locations a glyph's dot is
// quantized to, in the horizontal direction. For example, a value of 8
// means that the dot is quantized to 1/8th of a pixel. This quantization
// only affects the glyph mask image, not its bounding box or advance
// width. A higher value gives a more faithful glyph image, but reduces the
// effectiveness of the glyph cache.
//
// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
//
// A zero value means to use 4 sub-pixel locations.
SubPixelsX int
// SubPixelsY is the number of sub-pixel locations a glyph's dot is
// quantized to, in the vertical direction. For example, a value of 8
// means that the dot is quantized to 1/8th of a pixel. This quantization
// only affects the glyph mask image, not its bounding box or advance
// width. A higher value gives a more faithful glyph image, but reduces the
// effectiveness of the glyph cache.
//
// If non-zero, it must be a power of 2, and be between 1 and 64 inclusive.
//
// A zero value means to use 1 sub-pixel location.
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:
// TODO: support vertical hinting.
return font.HintingFull
}
}
return font.HintingNone
}
func (o *Options) glyphCacheEntries() int {
if o != nil && powerOf2(o.GlyphCacheEntries) {
return o.GlyphCacheEntries
}
// 512 is 128 * 4 * 1, which lets us cache 128 glyphs at 4 * 1 subpixel
// locations in the X and Y direction.
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)
}
}
// This default value of 4 isn't based on anything scientific, merely as
// small a number as possible that looks almost as good as no quantization,
// or returning subPixels(64).
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)
}
}
// This default value of 1 isn't based on anything scientific, merely that
// vertical sub-pixel glyph rendering is pretty rare. Baseline locations
// can usually afford to snap to the pixel grid, so the vertical direction
// doesn't have the deal with the horizontal's fractional advance widths.
return subPixels(1)
}
// subPixels returns q and the bias and mask that leads to q quantized
// sub-pixel locations per full pixel.
//
// For example, q == 4 leads to a bias of 8 and a mask of 0xfffffff0, or -16,
// because we want to round fractions of fixed.Int26_6 as:
// - 0 to 7 rounds to 0.
// - 8 to 23 rounds to 16.
// - 24 to 39 rounds to 32.
// - 40 to 55 rounds to 48.
// - 56 to 63 rounds to 64.
// which means to add 8 and then bitwise-and with -16, in two's complement
// representation.
//
// When q == 1, we want bias == 32 and mask == -64.
// When q == 2, we want bias == 16 and mask == -32.
// When q == 4, we want bias == 8 and mask == -16.
// ...
// When q == 64, we want bias == 0 and mask == -1. (The no-op case).
// The pattern is clear.
func subPixels(q int) (value uint32, bias, mask fixed.Int26_6) {
return uint32(q), 32 / fixed.Int26_6(q), -64 / fixed.Int26_6(q)
}
// glyphCacheEntry caches the arguments and return values of rasterize.
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
}
// 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)),
glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()),
}
a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX()
a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY()
// Fill the cache with invalid entries. Valid glyph cache entries have fx
// and fy in the range [0, 64). Valid index cache entries have rune >= 0.
for i := range a.glyphCache {
a.glyphCache[i].key.fy = 0xff
}
for i := range a.indexCache {
a.indexCache[i].rune = -1
}
// Set the rasterizer's bounds to be big enough to handle the largest glyph.
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
// TODO: clip rectangle?
}
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
}
// Close satisfies the font.Face interface.
func (a *face) Close() error { return nil }
// Metrics satisfies the font.Face interface.
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)),
}
}
// Kern satisfies the font.Face interface.
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
}
// Glyph satisfies the font.Face interface.
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) {
// Quantize to the sub-pixel granularity.
dotX := (dot.X + a.subPixelBiasX) & a.subPixelMaskX
dotY := (dot.Y + a.subPixelBiasY) & a.subPixelMaskY
// Split the coordinates into their integer and fractional parts.
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
}
// rasterize returns the advance width, integer-pixel offset to render at, and
// the width and height of 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) (v glyphCacheVal, ok bool) {
if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
return glyphCacheVal{}, false
}
// Calculate the integer-pixel bounds for the glyph.
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
}
// 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()
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
}
}
// 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 + 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 {
// 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)
}
}
// facePainter is like a raster.AlphaSrcPainter, with an additional Y offset
// (face.paintOffset) to the painted spans.
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
}
}
}

522
vendor/github.com/golang/freetype/truetype/glyph.go generated vendored Normal file

@ -0,0 +1,522 @@
// Copyright 2010 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 (
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
// TODO: implement VerticalHinting.
// A Point is a co-ordinate pair plus whether it is 'on' a contour or an 'off'
// control point.
type Point struct {
X, Y fixed.Int26_6
// The Flags' LSB means whether or not this Point is 'on' the contour.
// Other bits are reserved for internal use.
Flags uint32
}
// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
// series of glyphs from a Font.
type GlyphBuf struct {
// AdvanceWidth is the glyph's advance width.
AdvanceWidth fixed.Int26_6
// Bounds is the glyph's bounding box.
Bounds fixed.Rectangle26_6
// Points contains all Points from all contours of the glyph. If hinting
// was used to load a glyph then Unhinted contains those Points before they
// were hinted, and InFontUnits contains those Points before they were
// hinted and scaled.
Points, Unhinted, InFontUnits []Point
// Ends is the point indexes of the end point of each contour. The length
// of Ends is the number of contours in the glyph. The i'th contour
// consists of points Points[Ends[i-1]:Ends[i]], where Ends[-1] is
// interpreted to mean zero.
Ends []int
font *Font
scale fixed.Int26_6
hinting font.Hinting
hinter hinter
// phantomPoints are the co-ordinates of the synthetic phantom points
// used for hinting and bounding box calculations.
phantomPoints [4]Point
// pp1x is the X co-ordinate of the first phantom point. The '1' is
// using 1-based indexing; pp1x is almost always phantomPoints[0].X.
// TODO: eliminate this and consistently use phantomPoints[0].X.
pp1x fixed.Int26_6
// metricsSet is whether the glyph's metrics have been set yet. For a
// compound glyph, a sub-glyph may override the outer glyph's metrics.
metricsSet bool
// tmp is a scratch buffer.
tmp []Point
}
// Flags for decoding a glyph's contours. These flags are documented at
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
const (
flagOnCurve = 1 << iota
flagXShortVector
flagYShortVector
flagRepeat
flagPositiveXShortVector
flagPositiveYShortVector
// The remaining flags are for internal use.
flagTouchedX
flagTouchedY
)
// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
// dependent on the value of the flag{X,Y}ShortVector bits.
const (
flagThisXIsSame = flagPositiveXShortVector
flagThisYIsSame = flagPositiveYShortVector
)
// Load loads a glyph's contours from a Font, overwriting any previously loaded
// contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
// 1 em, i is the glyph index, and h is the hinting policy.
func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) error {
g.Points = g.Points[:0]
g.Unhinted = g.Unhinted[:0]
g.InFontUnits = g.InFontUnits[:0]
g.Ends = g.Ends[:0]
g.font = f
g.hinting = h
g.scale = scale
g.pp1x = 0
g.phantomPoints = [4]Point{}
g.metricsSet = false
if h != font.HintingNone {
if err := g.hinter.init(f, scale); err != nil {
return err
}
}
if err := g.load(0, i, true); err != nil {
return err
}
// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
// and should be cleaned up once we have all the testScaling tests passing,
// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
pp1x := g.pp1x
if h != font.HintingNone {
pp1x = g.phantomPoints[0].X
}
if pp1x != 0 {
for i := range g.Points {
g.Points[i].X -= pp1x
}
}
advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
if h != font.HintingNone {
if len(f.hdmx) >= 8 {
if n := u32(f.hdmx, 4); n > 3+uint32(i) {
for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
if fixed.Int26_6(hdmx[0]) == scale>>6 {
advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
break
}
}
}
}
advanceWidth = (advanceWidth + 32) &^ 63
}
g.AdvanceWidth = advanceWidth
// Set g.Bounds to the 'control box', which is the bounding box of the
// Bézier curves' control points. This is easier to calculate, no smaller
// than and often equal to the tightest possible bounding box of the curves
// themselves. This approach is what C Freetype does. We can't just scale
// the nominal bounding box in the glyf data as the hinting process and
// phantom point adjustment may move points outside of that box.
if len(g.Points) == 0 {
g.Bounds = fixed.Rectangle26_6{}
} else {
p := g.Points[0]
g.Bounds.Min.X = p.X
g.Bounds.Max.X = p.X
g.Bounds.Min.Y = p.Y
g.Bounds.Max.Y = p.Y
for _, p := range g.Points[1:] {
if g.Bounds.Min.X > p.X {
g.Bounds.Min.X = p.X
} else if g.Bounds.Max.X < p.X {
g.Bounds.Max.X = p.X
}
if g.Bounds.Min.Y > p.Y {
g.Bounds.Min.Y = p.Y
} else if g.Bounds.Max.Y < p.Y {
g.Bounds.Max.Y = p.Y
}
}
// Snap the box to the grid, if hinting is on.
if h != font.HintingNone {
g.Bounds.Min.X &^= 63
g.Bounds.Min.Y &^= 63
g.Bounds.Max.X += 63
g.Bounds.Max.X &^= 63
g.Bounds.Max.Y += 63
g.Bounds.Max.Y &^= 63
}
}
return nil
}
func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
// The recursion limit here is arbitrary, but defends against malformed glyphs.
if recursion >= 32 {
return UnsupportedError("excessive compound glyph recursion")
}
// Find the relevant slice of g.font.glyf.
var g0, g1 uint32
if g.font.locaOffsetFormat == locaOffsetFormatShort {
g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
} else {
g0 = u32(g.font.loca, 4*int(i))
g1 = u32(g.font.loca, 4*int(i)+4)
}
// Decode the contour count and nominal bounding box, from the first
// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
// and 6, are unused.
glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
if g0+10 <= g1 {
glyf = g.font.glyf[g0:g1]
ne = int(int16(u16(glyf, 0)))
boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
}
// Create the phantom points.
uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
uvm := g.font.unscaledVMetric(i, boundsYMax)
g.phantomPoints = [4]Point{
{X: boundsXMin - uhm.LeftSideBearing},
{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
}
if len(glyf) == 0 {
g.addPhantomsAndScale(len(g.Points), len(g.Points), true, true)
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
g.Points = g.Points[:len(g.Points)-4]
// TODO: also trim g.InFontUnits and g.Unhinted?
return nil
}
// Load and hint the contours.
if ne < 0 {
if ne != -1 {
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
// "the values -2, -3, and so forth, are reserved for future use."
return UnsupportedError("negative number of contours")
}
pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
return err
}
} else {
np0, ne0 := len(g.Points), len(g.Ends)
program := g.loadSimple(glyf, ne)
g.addPhantomsAndScale(np0, np0, true, true)
pp1x = g.Points[len(g.Points)-4].X
if g.hinting != font.HintingNone {
if len(program) != 0 {
err := g.hinter.run(
program,
g.Points[np0:],
g.Unhinted[np0:],
g.InFontUnits[np0:],
g.Ends[ne0:],
)
if err != nil {
return err
}
}
// Drop the four phantom points.
g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
}
if useMyMetrics {
copy(g.phantomPoints[:], g.Points[len(g.Points)-4:])
}
g.Points = g.Points[:len(g.Points)-4]
if np0 != 0 {
// The hinting program expects the []Ends values to be indexed
// relative to the inner glyph, not the outer glyph, so we delay
// adding np0 until after the hinting program (if any) has run.
for i := ne0; i < len(g.Ends); i++ {
g.Ends[i] += np0
}
}
}
if useMyMetrics && !g.metricsSet {
g.metricsSet = true
g.pp1x = pp1x
}
return nil
}
// loadOffset is the initial offset for loadSimple and loadCompound. The first
// 10 bytes are the number of contours and the bounding box.
const loadOffset = 10
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
offset := loadOffset
for i := 0; i < ne; i++ {
g.Ends = append(g.Ends, 1+int(u16(glyf, offset)))
offset += 2
}
// Note the TrueType hinting instructions.
instrLen := int(u16(glyf, offset))
offset += 2
program = glyf[offset : offset+instrLen]
offset += instrLen
if ne == 0 {
return program
}
np0 := len(g.Points)
np1 := np0 + int(g.Ends[len(g.Ends)-1])
// Decode the flags.
for i := np0; i < np1; {
c := uint32(glyf[offset])
offset++
g.Points = append(g.Points, Point{Flags: c})
i++
if c&flagRepeat != 0 {
count := glyf[offset]
offset++
for ; count > 0; count-- {
g.Points = append(g.Points, Point{Flags: c})
i++
}
}
}
// Decode the co-ordinates.
var x int16
for i := np0; i < np1; i++ {
f := g.Points[i].Flags
if f&flagXShortVector != 0 {
dx := int16(glyf[offset])
offset++
if f&flagPositiveXShortVector == 0 {
x -= dx
} else {
x += dx
}
} else if f&flagThisXIsSame == 0 {
x += int16(u16(glyf, offset))
offset += 2
}
g.Points[i].X = fixed.Int26_6(x)
}
var y int16
for i := np0; i < np1; i++ {
f := g.Points[i].Flags
if f&flagYShortVector != 0 {
dy := int16(glyf[offset])
offset++
if f&flagPositiveYShortVector == 0 {
y -= dy
} else {
y += dy
}
} else if f&flagThisYIsSame == 0 {
y += int16(u16(glyf, offset))
offset += 2
}
g.Points[i].Y = fixed.Int26_6(y)
}
return program
}
func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
glyf []byte, useMyMetrics bool) error {
// Flags for decoding a compound glyph. These flags are documented at
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
const (
flagArg1And2AreWords = 1 << iota
flagArgsAreXYValues
flagRoundXYToGrid
flagWeHaveAScale
flagUnused
flagMoreComponents
flagWeHaveAnXAndYScale
flagWeHaveATwoByTwo
flagWeHaveInstructions
flagUseMyMetrics
flagOverlapCompound
)
np0, ne0 := len(g.Points), len(g.Ends)
offset := loadOffset
for {
flags := u16(glyf, offset)
component := Index(u16(glyf, offset+2))
dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
if flags&flagArg1And2AreWords != 0 {
dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
offset += 8
} else {
dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
offset += 6
}
if flags&flagArgsAreXYValues == 0 {
return UnsupportedError("compound glyph transform vector")
}
if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
hasTransform = true
switch {
case flags&flagWeHaveAScale != 0:
transform[0] = int16(u16(glyf, offset+0))
transform[3] = transform[0]
offset += 2
case flags&flagWeHaveAnXAndYScale != 0:
transform[0] = int16(u16(glyf, offset+0))
transform[3] = int16(u16(glyf, offset+2))
offset += 4
case flags&flagWeHaveATwoByTwo != 0:
transform[0] = int16(u16(glyf, offset+0))
transform[1] = int16(u16(glyf, offset+2))
transform[2] = int16(u16(glyf, offset+4))
transform[3] = int16(u16(glyf, offset+6))
offset += 8
}
}
savedPP := g.phantomPoints
np0 := len(g.Points)
componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
if err := g.load(recursion+1, component, componentUMM); err != nil {
return err
}
if flags&flagUseMyMetrics == 0 {
g.phantomPoints = savedPP
}
if hasTransform {
for j := np0; j < len(g.Points); j++ {
p := &g.Points[j]
newX := 0 +
fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
newY := 0 +
fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
p.X, p.Y = newX, newY
}
}
dx = g.font.scale(g.scale * dx)
dy = g.font.scale(g.scale * dy)
if flags&flagRoundXYToGrid != 0 {
dx = (dx + 32) &^ 63
dy = (dy + 32) &^ 63
}
for j := np0; j < len(g.Points); j++ {
p := &g.Points[j]
p.X += dx
p.Y += dy
}
// TODO: also adjust g.InFontUnits and g.Unhinted?
if flags&flagMoreComponents == 0 {
break
}
}
instrLen := 0
if g.hinting != font.HintingNone && offset+2 <= len(glyf) {
instrLen = int(u16(glyf, offset))
offset += 2
}
g.addPhantomsAndScale(np0, len(g.Points), false, instrLen > 0)
points, ends := g.Points[np0:], g.Ends[ne0:]
g.Points = g.Points[:len(g.Points)-4]
for j := range points {
points[j].Flags &^= flagTouchedX | flagTouchedY
}
if instrLen == 0 {
if !g.metricsSet {
copy(g.phantomPoints[:], points[len(points)-4:])
}
return nil
}
// Hint the compound glyph.
program := glyf[offset : offset+instrLen]
// Temporarily adjust the ends to be relative to this compound glyph.
if np0 != 0 {
for i := range ends {
ends[i] -= np0
}
}
// Hinting instructions of a composite glyph completely refer to the
// (already) hinted subglyphs.
g.tmp = append(g.tmp[:0], points...)
if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
return err
}
if np0 != 0 {
for i := range ends {
ends[i] += np0
}
}
if !g.metricsSet {
copy(g.phantomPoints[:], points[len(points)-4:])
}
return nil
}
func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
// Add the four phantom points.
g.Points = append(g.Points, g.phantomPoints[:]...)
// Scale the points.
if simple && g.hinting != font.HintingNone {
g.InFontUnits = append(g.InFontUnits, g.Points[np1:]...)
}
for i := np1; i < len(g.Points); i++ {
p := &g.Points[i]
p.X = g.font.scale(g.scale * p.X)
p.Y = g.font.scale(g.scale * p.Y)
}
if g.hinting == font.HintingNone {
return
}
// Round the 1st phantom point to the grid, shifting all other points equally.
// Note that "all other points" starts from np0, not np1.
// TODO: delete this adjustment and the np0/np1 distinction, when
// we update the compatibility tests to C Freetype 2.5.3.
// See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
if adjust {
pp1x := g.Points[len(g.Points)-4].X
if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
for i := np0; i < len(g.Points); i++ {
g.Points[i].X += dx
}
}
}
if simple {
g.Unhinted = append(g.Unhinted, g.Points[np1:]...)
}
// Round the 2nd and 4th phantom point to the grid.
p := &g.Points[len(g.Points)-3]
p.X = (p.X + 32) &^ 63
p = &g.Points[len(g.Points)-1]
p.Y = (p.Y + 32) &^ 63
}

1770
vendor/github.com/golang/freetype/truetype/hint.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

289
vendor/github.com/golang/freetype/truetype/opcodes.go generated vendored Normal file

@ -0,0 +1,289 @@
// Copyright 2012 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
// The Truetype opcodes are summarized at
// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
const (
opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
opSVTCA1 = 0x01 // .
opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
opSPVTCA1 = 0x03 // .
opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
opSFVTCA1 = 0x05 // .
opSPVTL0 = 0x06 // Set Projection Vector To Line
opSPVTL1 = 0x07 // .
opSFVTL0 = 0x08 // Set Freedom Vector To Line
opSFVTL1 = 0x09 // .
opSPVFS = 0x0a // Set Projection Vector From Stack
opSFVFS = 0x0b // Set Freedom Vector From Stack
opGPV = 0x0c // Get Projection Vector
opGFV = 0x0d // Get Freedom Vector
opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
opISECT = 0x0f // moves point p to the InterSECTion of two lines
opSRP0 = 0x10 // Set Reference Point 0
opSRP1 = 0x11 // Set Reference Point 1
opSRP2 = 0x12 // Set Reference Point 2
opSZP0 = 0x13 // Set Zone Pointer 0
opSZP1 = 0x14 // Set Zone Pointer 1
opSZP2 = 0x15 // Set Zone Pointer 2
opSZPS = 0x16 // Set Zone PointerS
opSLOOP = 0x17 // Set LOOP variable
opRTG = 0x18 // Round To Grid
opRTHG = 0x19 // Round To Half Grid
opSMD = 0x1a // Set Minimum Distance
opELSE = 0x1b // ELSE clause
opJMPR = 0x1c // JuMP Relative
opSCVTCI = 0x1d // Set Control Value Table Cut-In
opSSWCI = 0x1e // Set Single Width Cut-In
opSSW = 0x1f // Set Single Width
opDUP = 0x20 // DUPlicate top stack element
opPOP = 0x21 // POP top stack element
opCLEAR = 0x22 // CLEAR the stack
opSWAP = 0x23 // SWAP the top two elements on the stack
opDEPTH = 0x24 // DEPTH of the stack
opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
opALIGNPTS = 0x27 // ALIGN PoinTS
op_0x28 = 0x28 // deprecated
opUTP = 0x29 // UnTouch Point
opLOOPCALL = 0x2a // LOOP and CALL function
opCALL = 0x2b // CALL function
opFDEF = 0x2c // Function DEFinition
opENDF = 0x2d // END Function definition
opMDAP0 = 0x2e // Move Direct Absolute Point
opMDAP1 = 0x2f // .
opIUP0 = 0x30 // Interpolate Untouched Points through the outline
opIUP1 = 0x31 // .
opSHP0 = 0x32 // SHift Point using reference point
opSHP1 = 0x33 // .
opSHC0 = 0x34 // SHift Contour using reference point
opSHC1 = 0x35 // .
opSHZ0 = 0x36 // SHift Zone using reference point
opSHZ1 = 0x37 // .
opSHPIX = 0x38 // SHift point by a PIXel amount
opIP = 0x39 // Interpolate Point
opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
opMSIRP1 = 0x3b // .
opALIGNRP = 0x3c // ALIGN to Reference Point
opRTDG = 0x3d // Round To Double Grid
opMIAP0 = 0x3e // Move Indirect Absolute Point
opMIAP1 = 0x3f // .
opNPUSHB = 0x40 // PUSH N Bytes
opNPUSHW = 0x41 // PUSH N Words
opWS = 0x42 // Write Store
opRS = 0x43 // Read Store
opWCVTP = 0x44 // Write Control Value Table in Pixel units
opRCVT = 0x45 // Read Control Value Table entry
opGC0 = 0x46 // Get Coordinate projected onto the projection vector
opGC1 = 0x47 // .
opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
opMD0 = 0x49 // Measure Distance
opMD1 = 0x4a // .
opMPPEM = 0x4b // Measure Pixels Per EM
opMPS = 0x4c // Measure Point Size
opFLIPON = 0x4d // set the auto FLIP Boolean to ON
opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
opDEBUG = 0x4f // DEBUG call
opLT = 0x50 // Less Than
opLTEQ = 0x51 // Less Than or EQual
opGT = 0x52 // Greater Than
opGTEQ = 0x53 // Greater Than or EQual
opEQ = 0x54 // EQual
opNEQ = 0x55 // Not EQual
opODD = 0x56 // ODD
opEVEN = 0x57 // EVEN
opIF = 0x58 // IF test
opEIF = 0x59 // End IF
opAND = 0x5a // logical AND
opOR = 0x5b // logical OR
opNOT = 0x5c // logical NOT
opDELTAP1 = 0x5d // DELTA exception P1
opSDB = 0x5e // Set Delta Base in the graphics state
opSDS = 0x5f // Set Delta Shift in the graphics state
opADD = 0x60 // ADD
opSUB = 0x61 // SUBtract
opDIV = 0x62 // DIVide
opMUL = 0x63 // MULtiply
opABS = 0x64 // ABSolute value
opNEG = 0x65 // NEGate
opFLOOR = 0x66 // FLOOR
opCEILING = 0x67 // CEILING
opROUND00 = 0x68 // ROUND value
opROUND01 = 0x69 // .
opROUND10 = 0x6a // .
opROUND11 = 0x6b // .
opNROUND00 = 0x6c // No ROUNDing of value
opNROUND01 = 0x6d // .
opNROUND10 = 0x6e // .
opNROUND11 = 0x6f // .
opWCVTF = 0x70 // Write Control Value Table in Funits
opDELTAP2 = 0x71 // DELTA exception P2
opDELTAP3 = 0x72 // DELTA exception P3
opDELTAC1 = 0x73 // DELTA exception C1
opDELTAC2 = 0x74 // DELTA exception C2
opDELTAC3 = 0x75 // DELTA exception C3
opSROUND = 0x76 // Super ROUND
opS45ROUND = 0x77 // Super ROUND 45 degrees
opJROT = 0x78 // Jump Relative On True
opJROF = 0x79 // Jump Relative On False
opROFF = 0x7a // Round OFF
op_0x7b = 0x7b // deprecated
opRUTG = 0x7c // Round Up To Grid
opRDTG = 0x7d // Round Down To Grid
opSANGW = 0x7e // Set ANGle Weight
opAA = 0x7f // Adjust Angle
opFLIPPT = 0x80 // FLIP PoinT
opFLIPRGON = 0x81 // FLIP RanGe ON
opFLIPRGOFF = 0x82 // FLIP RanGe OFF
op_0x83 = 0x83 // deprecated
op_0x84 = 0x84 // deprecated
opSCANCTRL = 0x85 // SCAN conversion ConTRoL
opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
opSDPVTL1 = 0x87 // .
opGETINFO = 0x88 // GET INFOrmation
opIDEF = 0x89 // Instruction DEFinition
opROLL = 0x8a // ROLL the top three stack elements
opMAX = 0x8b // MAXimum of top two stack elements
opMIN = 0x8c // MINimum of top two stack elements
opSCANTYPE = 0x8d // SCANTYPE
opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
op_0x8f = 0x8f
op_0x90 = 0x90
op_0x91 = 0x91
op_0x92 = 0x92
op_0x93 = 0x93
op_0x94 = 0x94
op_0x95 = 0x95
op_0x96 = 0x96
op_0x97 = 0x97
op_0x98 = 0x98
op_0x99 = 0x99
op_0x9a = 0x9a
op_0x9b = 0x9b
op_0x9c = 0x9c
op_0x9d = 0x9d
op_0x9e = 0x9e
op_0x9f = 0x9f
op_0xa0 = 0xa0
op_0xa1 = 0xa1
op_0xa2 = 0xa2
op_0xa3 = 0xa3
op_0xa4 = 0xa4
op_0xa5 = 0xa5
op_0xa6 = 0xa6
op_0xa7 = 0xa7
op_0xa8 = 0xa8
op_0xa9 = 0xa9
op_0xaa = 0xaa
op_0xab = 0xab
op_0xac = 0xac
op_0xad = 0xad
op_0xae = 0xae
op_0xaf = 0xaf
opPUSHB000 = 0xb0 // PUSH Bytes
opPUSHB001 = 0xb1 // .
opPUSHB010 = 0xb2 // .
opPUSHB011 = 0xb3 // .
opPUSHB100 = 0xb4 // .
opPUSHB101 = 0xb5 // .
opPUSHB110 = 0xb6 // .
opPUSHB111 = 0xb7 // .
opPUSHW000 = 0xb8 // PUSH Words
opPUSHW001 = 0xb9 // .
opPUSHW010 = 0xba // .
opPUSHW011 = 0xbb // .
opPUSHW100 = 0xbc // .
opPUSHW101 = 0xbd // .
opPUSHW110 = 0xbe // .
opPUSHW111 = 0xbf // .
opMDRP00000 = 0xc0 // Move Direct Relative Point
opMDRP00001 = 0xc1 // .
opMDRP00010 = 0xc2 // .
opMDRP00011 = 0xc3 // .
opMDRP00100 = 0xc4 // .
opMDRP00101 = 0xc5 // .
opMDRP00110 = 0xc6 // .
opMDRP00111 = 0xc7 // .
opMDRP01000 = 0xc8 // .
opMDRP01001 = 0xc9 // .
opMDRP01010 = 0xca // .
opMDRP01011 = 0xcb // .
opMDRP01100 = 0xcc // .
opMDRP01101 = 0xcd // .
opMDRP01110 = 0xce // .
opMDRP01111 = 0xcf // .
opMDRP10000 = 0xd0 // .
opMDRP10001 = 0xd1 // .
opMDRP10010 = 0xd2 // .
opMDRP10011 = 0xd3 // .
opMDRP10100 = 0xd4 // .
opMDRP10101 = 0xd5 // .
opMDRP10110 = 0xd6 // .
opMDRP10111 = 0xd7 // .
opMDRP11000 = 0xd8 // .
opMDRP11001 = 0xd9 // .
opMDRP11010 = 0xda // .
opMDRP11011 = 0xdb // .
opMDRP11100 = 0xdc // .
opMDRP11101 = 0xdd // .
opMDRP11110 = 0xde // .
opMDRP11111 = 0xdf // .
opMIRP00000 = 0xe0 // Move Indirect Relative Point
opMIRP00001 = 0xe1 // .
opMIRP00010 = 0xe2 // .
opMIRP00011 = 0xe3 // .
opMIRP00100 = 0xe4 // .
opMIRP00101 = 0xe5 // .
opMIRP00110 = 0xe6 // .
opMIRP00111 = 0xe7 // .
opMIRP01000 = 0xe8 // .
opMIRP01001 = 0xe9 // .
opMIRP01010 = 0xea // .
opMIRP01011 = 0xeb // .
opMIRP01100 = 0xec // .
opMIRP01101 = 0xed // .
opMIRP01110 = 0xee // .
opMIRP01111 = 0xef // .
opMIRP10000 = 0xf0 // .
opMIRP10001 = 0xf1 // .
opMIRP10010 = 0xf2 // .
opMIRP10011 = 0xf3 // .
opMIRP10100 = 0xf4 // .
opMIRP10101 = 0xf5 // .
opMIRP10110 = 0xf6 // .
opMIRP10111 = 0xf7 // .
opMIRP11000 = 0xf8 // .
opMIRP11001 = 0xf9 // .
opMIRP11010 = 0xfa // .
opMIRP11011 = 0xfb // .
opMIRP11100 = 0xfc // .
opMIRP11101 = 0xfd // .
opMIRP11110 = 0xfe // .
opMIRP11111 = 0xff // .
)
// popCount is the number of stack elements that each opcode pops.
var popCount = [256]uint8{
// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
}

653
vendor/github.com/golang/freetype/truetype/truetype.go generated vendored Normal file

@ -0,0 +1,653 @@
// Copyright 2010 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 provides a parser for the TTF and TTC file formats.
// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
// and http://www.microsoft.com/typography/otspec/
//
// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
// metrics and control points. All these methods take a scale parameter, which
// is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
// example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
// fixed.Int26_6(10 << 6).
//
// To measure a TrueType font in ideal FUnit space, use scale equal to
// font.FUnitsPerEm().
package truetype // import "github.com/golang/freetype/truetype"
import (
"fmt"
"golang.org/x/image/math/fixed"
)
// An Index is a Font's index of a rune.
type Index uint16
// A NameID identifies a name table entry.
//
// See https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
type NameID uint16
const (
NameIDCopyright NameID = 0
NameIDFontFamily = 1
NameIDFontSubfamily = 2
NameIDUniqueSubfamilyID = 3
NameIDFontFullName = 4
NameIDNameTableVersion = 5
NameIDPostscriptName = 6
NameIDTrademarkNotice = 7
NameIDManufacturerName = 8
NameIDDesignerName = 9
NameIDFontDescription = 10
NameIDFontVendorURL = 11
NameIDFontDesignerURL = 12
NameIDFontLicense = 13
NameIDFontLicenseURL = 14
NameIDPreferredFamily = 16
NameIDPreferredSubfamily = 17
NameIDCompatibleName = 18
NameIDSampleText = 19
)
const (
// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
// least-significant 16-bit Platform Specific ID. The magic numbers are
// specified at https://www.microsoft.com/typography/otspec/name.htm
unicodeEncodingBMPOnly = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0 BMP Only)
unicodeEncodingFull = 0x00000004 // PID = 0 (Unicode), PSID = 4 (Unicode 2.0 Full Repertoire)
microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
)
// An HMetric holds the horizontal metrics of a single glyph.
type HMetric struct {
AdvanceWidth, LeftSideBearing fixed.Int26_6
}
// A VMetric holds the vertical metrics of a single glyph.
type VMetric struct {
AdvanceHeight, TopSideBearing fixed.Int26_6
}
// A FormatError reports that the input is not a valid TrueType font.
type FormatError string
func (e FormatError) Error() string {
return "freetype: invalid TrueType format: " + string(e)
}
// An UnsupportedError reports that the input uses a valid but unimplemented
// TrueType feature.
type UnsupportedError string
func (e UnsupportedError) Error() string {
return "freetype: unsupported TrueType feature: " + string(e)
}
// u32 returns the big-endian uint32 at b[i:].
func u32(b []byte, i int) uint32 {
return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
}
// u16 returns the big-endian uint16 at b[i:].
func u16(b []byte, i int) uint16 {
return uint16(b[i])<<8 | uint16(b[i+1])
}
// readTable returns a slice of the TTF data given by a table's directory entry.
func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
offset := int(u32(offsetLength, 0))
if offset < 0 {
return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
}
length := int(u32(offsetLength, 4))
if length < 0 {
return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
}
end := offset + length
if end < 0 || end > len(ttf) {
return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
}
return ttf[offset:end], nil
}
// parseSubtables returns the offset and platformID of the best subtable in
// table, where best favors a Unicode cmap encoding, and failing that, a
// Microsoft cmap encoding. offset is the offset of the first subtable in
// table, and size is the size of each subtable.
//
// If pred is non-nil, then only subtables that satisfy that predicate will be
// considered.
func parseSubtables(table []byte, name string, offset, size int, pred func([]byte) bool) (
bestOffset int, bestPID uint32, retErr error) {
if len(table) < 4 {
return 0, 0, FormatError(name + " too short")
}
nSubtables := int(u16(table, 2))
if len(table) < size*nSubtables+offset {
return 0, 0, FormatError(name + " too short")
}
ok := false
for i := 0; i < nSubtables; i, offset = i+1, offset+size {
if pred != nil && !pred(table[offset:]) {
continue
}
// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
// All values are big-endian.
pidPsid := u32(table, offset)
// We prefer the Unicode cmap encoding. Failing to find that, we fall
// back onto the Microsoft cmap encoding.
if pidPsid == unicodeEncodingBMPOnly || pidPsid == unicodeEncodingFull {
bestOffset, bestPID, ok = offset, pidPsid>>16, true
break
} else if pidPsid == microsoftSymbolEncoding ||
pidPsid == microsoftUCS2Encoding ||
pidPsid == microsoftUCS4Encoding {
bestOffset, bestPID, ok = offset, pidPsid>>16, true
// We don't break out of the for loop, so that Unicode can override Microsoft.
}
}
if !ok {
return 0, 0, UnsupportedError(name + " encoding")
}
return bestOffset, bestPID, nil
}
const (
locaOffsetFormatUnknown int = iota
locaOffsetFormatShort
locaOffsetFormatLong
)
// A cm holds a parsed cmap entry.
type cm struct {
start, end, delta, offset uint32
}
// A Font represents a Truetype font.
type Font struct {
// Tables sliced from the TTF data. The different tables are documented
// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, name, os2, prep, vmtx []byte
cmapIndexes []byte
// Cached values derived from the raw ttf data.
cm []cm
locaOffsetFormat int
nGlyph, nHMetric, nKern int
fUnitsPerEm int32
ascent int32 // In FUnits.
descent int32 // In FUnits; typically negative.
bounds fixed.Rectangle26_6 // In FUnits.
// Values from the maxp section.
maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
}
func (f *Font) parseCmap() error {
const (
cmapFormat4 = 4
cmapFormat12 = 12
languageIndependent = 0
)
offset, _, err := parseSubtables(f.cmap, "cmap", 4, 8, nil)
if err != nil {
return err
}
offset = int(u32(f.cmap, offset+4))
if offset <= 0 || offset > len(f.cmap) {
return FormatError("bad cmap offset")
}
cmapFormat := u16(f.cmap, offset)
switch cmapFormat {
case cmapFormat4:
language := u16(f.cmap, offset+4)
if language != languageIndependent {
return UnsupportedError(fmt.Sprintf("language: %d", language))
}
segCountX2 := int(u16(f.cmap, offset+6))
if segCountX2%2 == 1 {
return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
}
segCount := segCountX2 / 2
offset += 14
f.cm = make([]cm, segCount)
for i := 0; i < segCount; i++ {
f.cm[i].end = uint32(u16(f.cmap, offset))
offset += 2
}
offset += 2
for i := 0; i < segCount; i++ {
f.cm[i].start = uint32(u16(f.cmap, offset))
offset += 2
}
for i := 0; i < segCount; i++ {
f.cm[i].delta = uint32(u16(f.cmap, offset))
offset += 2
}
for i := 0; i < segCount; i++ {
f.cm[i].offset = uint32(u16(f.cmap, offset))
offset += 2
}
f.cmapIndexes = f.cmap[offset:]
return nil
case cmapFormat12:
if u16(f.cmap, offset+2) != 0 {
return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
}
length := u32(f.cmap, offset+4)
language := u32(f.cmap, offset+8)
if language != languageIndependent {
return UnsupportedError(fmt.Sprintf("language: %d", language))
}
nGroups := u32(f.cmap, offset+12)
if length != 12*nGroups+16 {
return FormatError("inconsistent cmap length")
}
offset += 16
f.cm = make([]cm, nGroups)
for i := uint32(0); i < nGroups; i++ {
f.cm[i].start = u32(f.cmap, offset+0)
f.cm[i].end = u32(f.cmap, offset+4)
f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
offset += 12
}
return nil
}
return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
}
func (f *Font) parseHead() error {
if len(f.head) != 54 {
return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
}
f.fUnitsPerEm = int32(u16(f.head, 18))
f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
switch i := u16(f.head, 50); i {
case 0:
f.locaOffsetFormat = locaOffsetFormatShort
case 1:
f.locaOffsetFormat = locaOffsetFormatLong
default:
return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
}
return nil
}
func (f *Font) parseHhea() error {
if len(f.hhea) != 36 {
return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
}
f.ascent = int32(int16(u16(f.hhea, 4)))
f.descent = int32(int16(u16(f.hhea, 6)))
f.nHMetric = int(u16(f.hhea, 34))
if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
}
return nil
}
func (f *Font) parseKern() error {
// Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
// "Previous versions of the 'kern' table defined both the version and nTables fields in the header
// as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
// (although AAT can sense an old kerning table and still make correct use of it). Microsoft
// Windows still uses the older format for the 'kern' table and will not recognize the newer one.
// Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
// and Windows should use the old format."
// Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
// just like the C Freetype implementation.
if len(f.kern) == 0 {
if f.nKern != 0 {
return FormatError("bad kern table length")
}
return nil
}
if len(f.kern) < 18 {
return FormatError("kern data too short")
}
version, offset := u16(f.kern, 0), 2
if version != 0 {
return UnsupportedError(fmt.Sprintf("kern version: %d", version))
}
n, offset := u16(f.kern, offset), offset+2
if n == 0 {
return UnsupportedError("kern nTables: 0")
}
// TODO: support multiple subtables. In practice, almost all .ttf files
// have only one subtable, if they have a kern table at all. But it's not
// impossible. Xolonium Regular (https://fontlibrary.org/en/font/xolonium)
// has 3 subtables. Those subtables appear to be disjoint, rather than
// being the same kerning pairs encoded in three different ways.
//
// For now, we'll use only the first subtable.
offset += 2 // Skip the version.
length, offset := int(u16(f.kern, offset)), offset+2
coverage, offset := u16(f.kern, offset), offset+2
if coverage != 0x0001 {
// We only support horizontal kerning.
return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
}
f.nKern, offset = int(u16(f.kern, offset)), offset+2
if 6*f.nKern != length-14 {
return FormatError("bad kern table length")
}
return nil
}
func (f *Font) parseMaxp() error {
if len(f.maxp) != 32 {
return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
}
f.nGlyph = int(u16(f.maxp, 4))
f.maxTwilightPoints = u16(f.maxp, 16)
f.maxStorage = u16(f.maxp, 18)
f.maxFunctionDefs = u16(f.maxp, 20)
f.maxStackElements = u16(f.maxp, 24)
return nil
}
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
if x >= 0 {
x += fixed.Int26_6(f.fUnitsPerEm) / 2
} else {
x -= fixed.Int26_6(f.fUnitsPerEm) / 2
}
return x / fixed.Int26_6(f.fUnitsPerEm)
}
// Bounds returns the union of a Font's glyphs' bounds.
func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
b := f.bounds
b.Min.X = f.scale(scale * b.Min.X)
b.Min.Y = f.scale(scale * b.Min.Y)
b.Max.X = f.scale(scale * b.Max.X)
b.Max.Y = f.scale(scale * b.Max.Y)
return b
}
// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
func (f *Font) FUnitsPerEm() int32 {
return f.fUnitsPerEm
}
// Index returns a Font's index for the given rune.
func (f *Font) Index(x rune) Index {
c := uint32(x)
for i, j := 0, len(f.cm); i < j; {
h := i + (j-i)/2
cm := &f.cm[h]
if c < cm.start {
j = h
} else if cm.end < c {
i = h + 1
} else if cm.offset == 0 {
return Index(c + cm.delta)
} else {
offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
return Index(u16(f.cmapIndexes, offset))
}
}
return 0
}
// Name returns the Font's name value for the given NameID. It returns "" if
// there was an error, or if that name was not found.
func (f *Font) Name(id NameID) string {
x, platformID, err := parseSubtables(f.name, "name", 6, 12, func(b []byte) bool {
return NameID(u16(b, 6)) == id
})
if err != nil {
return ""
}
offset, length := u16(f.name, 4)+u16(f.name, x+10), u16(f.name, x+8)
// Return the ASCII value of the encoded string.
// The string is encoded as UTF-16 on non-Apple platformIDs; Apple is platformID 1.
src := f.name[offset : offset+length]
var dst []byte
if platformID != 1 { // UTF-16.
if len(src)&1 != 0 {
return ""
}
dst = make([]byte, len(src)/2)
for i := range dst {
dst[i] = printable(u16(src, 2*i))
}
} else { // ASCII.
dst = make([]byte, len(src))
for i, c := range src {
dst[i] = printable(uint16(c))
}
}
return string(dst)
}
func printable(r uint16) byte {
if 0x20 <= r && r < 0x7f {
return byte(r)
}
return '?'
}
// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
// the given index.
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
j := int(i)
if j < 0 || f.nGlyph <= j {
return HMetric{}
}
if j >= f.nHMetric {
p := 4 * (f.nHMetric - 1)
return HMetric{
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, p)),
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
}
}
return HMetric{
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, 4*j)),
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
}
}
// HMetric returns the horizontal metrics for the glyph with the given index.
func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
h := f.unscaledHMetric(i)
h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
return h
}
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
// the given index. yMax is the top of the glyph's bounding box.
func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
j := int(i)
if j < 0 || f.nGlyph <= j {
return VMetric{}
}
if 4*j+4 <= len(f.vmtx) {
return VMetric{
AdvanceHeight: fixed.Int26_6(u16(f.vmtx, 4*j)),
TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
}
}
// The OS/2 table has grown over time.
// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
// says that it was originally 68 bytes. Optional fields, including
// the ascender and descender, are described at
// http://www.microsoft.com/typography/otspec/os2.htm
if len(f.os2) >= 72 {
sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
return VMetric{
AdvanceHeight: sTypoAscender - sTypoDescender,
TopSideBearing: sTypoAscender - yMax,
}
}
return VMetric{
AdvanceHeight: fixed.Int26_6(f.fUnitsPerEm),
TopSideBearing: 0,
}
}
// VMetric returns the vertical metrics for the glyph with the given index.
func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
// TODO: should 0 be bounds.YMax?
v := f.unscaledVMetric(i, 0)
v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
v.TopSideBearing = f.scale(scale * v.TopSideBearing)
return v
}
// Kern returns the horizontal adjustment for the given glyph pair. A positive
// kern means to move the glyphs further apart.
func (f *Font) Kern(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
if f.nKern == 0 {
return 0
}
g := uint32(i0)<<16 | uint32(i1)
lo, hi := 0, f.nKern
for lo < hi {
i := (lo + hi) / 2
ig := u32(f.kern, 18+6*i)
if ig < g {
lo = i + 1
} else if ig > g {
hi = i
} else {
return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
}
}
return 0
}
// Parse returns a new Font for the given TTF or TTC data.
//
// For TrueType Collections, the first font in the collection is parsed.
func Parse(ttf []byte) (font *Font, err error) {
return parse(ttf, 0)
}
func parse(ttf []byte, offset int) (font *Font, err error) {
if len(ttf)-offset < 12 {
err = FormatError("TTF data is too short")
return
}
originalOffset := offset
magic, offset := u32(ttf, offset), offset+4
switch magic {
case 0x00010000:
// No-op.
case 0x74746366: // "ttcf" as a big-endian uint32.
if originalOffset != 0 {
err = FormatError("recursive TTC")
return
}
ttcVersion, offset := u32(ttf, offset), offset+4
if ttcVersion != 0x00010000 && ttcVersion != 0x00020000 {
err = FormatError("bad TTC version")
return
}
numFonts, offset := int(u32(ttf, offset)), offset+4
if numFonts <= 0 {
err = FormatError("bad number of TTC fonts")
return
}
if len(ttf[offset:])/4 < numFonts {
err = FormatError("TTC offset table is too short")
return
}
// TODO: provide an API to select which font in a TrueType collection to return,
// not just the first one. This may require an API to parse a TTC's name tables,
// so users of this package can select the font in a TTC by name.
offset = int(u32(ttf, offset))
if offset <= 0 || offset > len(ttf) {
err = FormatError("bad TTC offset")
return
}
return parse(ttf, offset)
default:
err = FormatError("bad TTF version")
return
}
n, offset := int(u16(ttf, offset)), offset+2
offset += 6 // Skip the searchRange, entrySelector and rangeShift.
if len(ttf) < 16*n+offset {
err = FormatError("TTF data is too short")
return
}
f := new(Font)
// Assign the table slices.
for i := 0; i < n; i++ {
x := 16*i + offset
switch string(ttf[x : x+4]) {
case "cmap":
f.cmap, err = readTable(ttf, ttf[x+8:x+16])
case "cvt ":
f.cvt, err = readTable(ttf, ttf[x+8:x+16])
case "fpgm":
f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
case "glyf":
f.glyf, err = readTable(ttf, ttf[x+8:x+16])
case "hdmx":
f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
case "head":
f.head, err = readTable(ttf, ttf[x+8:x+16])
case "hhea":
f.hhea, err = readTable(ttf, ttf[x+8:x+16])
case "hmtx":
f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
case "kern":
f.kern, err = readTable(ttf, ttf[x+8:x+16])
case "loca":
f.loca, err = readTable(ttf, ttf[x+8:x+16])
case "maxp":
f.maxp, err = readTable(ttf, ttf[x+8:x+16])
case "name":
f.name, err = readTable(ttf, ttf[x+8:x+16])
case "OS/2":
f.os2, err = readTable(ttf, ttf[x+8:x+16])
case "prep":
f.prep, err = readTable(ttf, ttf[x+8:x+16])
case "vmtx":
f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
}
if err != nil {
return
}
}
// Parse and sanity-check the TTF data.
if err = f.parseHead(); err != nil {
return
}
if err = f.parseMaxp(); err != nil {
return
}
if err = f.parseCmap(); err != nil {
return
}
if err = f.parseKern(); err != nil {
return
}
if err = f.parseHhea(); err != nil {
return
}
font = f
return
}