Use the fixed.Int26_6 and fixed.Point26_6 types.
This commit is contained in:
parent
d91c1d6844
commit
856a70c395
7 changed files with 298 additions and 306 deletions
|
@ -20,12 +20,13 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/golang/freetype/raster"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
func p(x, y int) raster.Point {
|
||||
return raster.Point{
|
||||
X: raster.Fix32(x * 256),
|
||||
Y: raster.Fix32(y * 256),
|
||||
func p(x, y int) fixed.Point26_6 {
|
||||
return fixed.Point26_6{
|
||||
X: fixed.Int26_6(x * 64),
|
||||
Y: fixed.Int26_6(y * 64),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/golang/freetype/raster"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
|
@ -87,11 +88,11 @@ var inside = []node{
|
|||
node{686, 1274, -1},
|
||||
}
|
||||
|
||||
func p(n node) raster.Point {
|
||||
func p(n node) fixed.Point26_6 {
|
||||
x, y := 20+n.x/4, 380-n.y/4
|
||||
return raster.Point{
|
||||
X: raster.Fix32(x * 256),
|
||||
Y: raster.Fix32(y * 256),
|
||||
return fixed.Point26_6{
|
||||
X: fixed.Int26_6(x << 6),
|
||||
Y: fixed.Int26_6(y << 6),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +122,7 @@ func contour(r *raster.Rasterizer, ns []node) {
|
|||
func showNodes(m *image.RGBA, ns []node) {
|
||||
for _, n := range ns {
|
||||
p := p(n)
|
||||
x, y := int(p.X)/256, int(p.Y)/256
|
||||
x, y := int(p.X)/64, int(p.Y)/64
|
||||
if !(image.Point{x, y}).In(m.Bounds()) {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -27,22 +27,23 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/golang/freetype/raster"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// pDot returns the dot product p·q.
|
||||
func pDot(p, q raster.Point) raster.Fix64 {
|
||||
func pDot(p, q fixed.Point26_6) fixed.Int52_12 {
|
||||
px, py := int64(p.X), int64(p.Y)
|
||||
qx, qy := int64(q.X), int64(q.Y)
|
||||
return raster.Fix64(px*qx + py*qy)
|
||||
return fixed.Int52_12(px*qx + py*qy)
|
||||
}
|
||||
|
||||
func main() {
|
||||
const (
|
||||
n = 17
|
||||
r = 256 * 80
|
||||
r = 64 * 80
|
||||
)
|
||||
s := raster.Fix32(r * math.Sqrt(2) / 2)
|
||||
t := raster.Fix32(r * math.Tan(math.Pi/8))
|
||||
s := fixed.Int26_6(r * math.Sqrt(2) / 2)
|
||||
t := fixed.Int26_6(r * math.Tan(math.Pi/8))
|
||||
|
||||
m := image.NewRGBA(image.Rect(0, 0, 800, 600))
|
||||
draw.Draw(m, m.Bounds(), image.NewUniform(color.RGBA{63, 63, 63, 255}), image.ZP, draw.Src)
|
||||
|
@ -51,38 +52,38 @@ func main() {
|
|||
z := raster.NewRasterizer(800, 600)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
cx := raster.Fix32(25600 + 51200*(i%4))
|
||||
cy := raster.Fix32(2560 + 32000*(i/4))
|
||||
c := raster.Point{X: cx, Y: cy}
|
||||
cx := fixed.Int26_6(6400 + 12800*(i%4))
|
||||
cy := fixed.Int26_6(640 + 8000*(i/4))
|
||||
c := fixed.Point26_6{X: cx, Y: cy}
|
||||
theta := math.Pi * (0.5 + 0.5*float64(i)/(n-1))
|
||||
dx := raster.Fix32(r * math.Cos(theta))
|
||||
dy := raster.Fix32(r * math.Sin(theta))
|
||||
d := raster.Point{X: dx, Y: dy}
|
||||
dx := fixed.Int26_6(r * math.Cos(theta))
|
||||
dy := fixed.Int26_6(r * math.Sin(theta))
|
||||
d := fixed.Point26_6{X: dx, Y: dy}
|
||||
// Draw a quarter-circle approximated by two quadratic segments,
|
||||
// with each segment spanning 45 degrees.
|
||||
z.Start(c)
|
||||
z.Add1(c.Add(raster.Point{X: r, Y: 0}))
|
||||
z.Add2(c.Add(raster.Point{X: r, Y: t}), c.Add(raster.Point{X: s, Y: s}))
|
||||
z.Add2(c.Add(raster.Point{X: t, Y: r}), c.Add(raster.Point{X: 0, Y: r}))
|
||||
// Add another quadratic segment whose angle ranges between 0 and 90 degrees.
|
||||
// For an explanation of the magic constants 22, 150, 181 and 256, read the
|
||||
// comments in the freetype/raster package.
|
||||
dot := 256 * pDot(d, raster.Point{X: 0, Y: r}) / (r * r)
|
||||
multiple := raster.Fix32(150 - 22*(dot-181)/(256-181))
|
||||
z.Add2(c.Add(raster.Point{X: dx, Y: r + dy}.Mul(multiple)), c.Add(d))
|
||||
z.Add1(c.Add(fixed.Point26_6{X: r, Y: 0}))
|
||||
z.Add2(c.Add(fixed.Point26_6{X: r, Y: t}), c.Add(fixed.Point26_6{X: s, Y: s}))
|
||||
z.Add2(c.Add(fixed.Point26_6{X: t, Y: r}), c.Add(fixed.Point26_6{X: 0, Y: r}))
|
||||
// Add another quadratic segment whose angle ranges between 0 and 90
|
||||
// degrees. For an explanation of the magic constants 128, 150, 181 and
|
||||
// 256, read the comments in the freetype/raster package.
|
||||
dot := 256 * pDot(d, fixed.Point26_6{X: 0, Y: r}) / (r * r)
|
||||
multiple := fixed.Int26_6(150-(150-128)*(dot-181)/(256-181)) >> 2
|
||||
z.Add2(c.Add(fixed.Point26_6{X: dx, Y: r + dy}.Mul(multiple)), c.Add(d))
|
||||
// Close the curve.
|
||||
z.Add1(c)
|
||||
}
|
||||
z.Rasterize(mp)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
cx := raster.Fix32(25600 + 51200*(i%4))
|
||||
cy := raster.Fix32(2560 + 32000*(i/4))
|
||||
cx := fixed.Int26_6(6400 + 12800*(i%4))
|
||||
cy := fixed.Int26_6(640 + 8000*(i/4))
|
||||
for j := 0; j < n; j++ {
|
||||
theta := math.Pi * float64(j) / (n - 1)
|
||||
dx := raster.Fix32(r * math.Cos(theta))
|
||||
dy := raster.Fix32(r * math.Sin(theta))
|
||||
m.Set(int((cx+dx)/256), int((cy+dy)/256), color.RGBA{255, 255, 0, 255})
|
||||
dx := fixed.Int26_6(r * math.Cos(theta))
|
||||
dy := fixed.Int26_6(r * math.Sin(theta))
|
||||
m.Set(int((cx+dx)/64), int((cy+dy)/64), color.RGBA{255, 255, 0, 255})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
94
freetype.go
94
freetype.go
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
"github.com/golang/freetype/raster"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// These constants determine the size of the glyph cache. The cache is keyed
|
||||
|
@ -33,7 +34,7 @@ const (
|
|||
type cacheEntry struct {
|
||||
valid bool
|
||||
glyph truetype.Index
|
||||
advanceWidth raster.Fix32
|
||||
advanceWidth fixed.Int26_6
|
||||
mask *image.Alpha
|
||||
offset image.Point
|
||||
}
|
||||
|
@ -45,12 +46,12 @@ func ParseFont(b []byte) (*truetype.Font, error) {
|
|||
return truetype.Parse(b)
|
||||
}
|
||||
|
||||
// Pt converts from a co-ordinate pair measured in pixels to a raster.Point
|
||||
// co-ordinate pair measured in raster.Fix32 units.
|
||||
func Pt(x, y int) raster.Point {
|
||||
return raster.Point{
|
||||
X: raster.Fix32(x << 8),
|
||||
Y: raster.Fix32(y << 8),
|
||||
// Pt converts from a co-ordinate pair measured in pixels to a fixed.Point26_6
|
||||
// co-ordinate pair measured in fixed.Int26_6 units.
|
||||
func Pt(x, y int) fixed.Point26_6 {
|
||||
return fixed.Point26_6{
|
||||
X: fixed.Int26_6(x << 6),
|
||||
Y: fixed.Int26_6(y << 6),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,12 +86,12 @@ type Context struct {
|
|||
|
||||
// PointToFix32 converts the given number of points (as in ``a 12 point font'')
|
||||
// into fixed point units.
|
||||
func (c *Context) PointToFix32(x float64) raster.Fix32 {
|
||||
return raster.Fix32(x * float64(c.dpi) * (256.0 / 72.0))
|
||||
func (c *Context) PointToFix32(x float64) fixed.Int26_6 {
|
||||
return fixed.Int26_6(x * float64(c.dpi) * (64.0 / 72.0))
|
||||
}
|
||||
|
||||
// drawContour draws the given closed contour with the given offset.
|
||||
func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
|
||||
func (c *Context) drawContour(ps []truetype.Point, dx, dy fixed.Int26_6) {
|
||||
if len(ps) == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -105,23 +106,23 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
|
|||
// 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 := raster.Point{
|
||||
X: dx + raster.Fix32(ps[0].X<<2),
|
||||
Y: dy - raster.Fix32(ps[0].Y<<2),
|
||||
start := fixed.Point26_6{
|
||||
X: dx + fixed.Int26_6(ps[0].X),
|
||||
Y: dy - fixed.Int26_6(ps[0].Y),
|
||||
}
|
||||
others := []truetype.Point(nil)
|
||||
if ps[0].Flags&0x01 != 0 {
|
||||
others = ps[1:]
|
||||
} else {
|
||||
last := raster.Point{
|
||||
X: dx + raster.Fix32(ps[len(ps)-1].X<<2),
|
||||
Y: dy - raster.Fix32(ps[len(ps)-1].Y<<2),
|
||||
last := fixed.Point26_6{
|
||||
X: dx + fixed.Int26_6(ps[len(ps)-1].X),
|
||||
Y: dy - fixed.Int26_6(ps[len(ps)-1].Y),
|
||||
}
|
||||
if ps[len(ps)-1].Flags&0x01 != 0 {
|
||||
start = last
|
||||
others = ps[:len(ps)-1]
|
||||
} else {
|
||||
start = raster.Point{
|
||||
start = fixed.Point26_6{
|
||||
X: (start.X + last.X) / 2,
|
||||
Y: (start.Y + last.Y) / 2,
|
||||
}
|
||||
|
@ -131,9 +132,9 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
|
|||
c.r.Start(start)
|
||||
q0, on0 := start, true
|
||||
for _, p := range others {
|
||||
q := raster.Point{
|
||||
X: dx + raster.Fix32(p.X<<2),
|
||||
Y: dy - raster.Fix32(p.Y<<2),
|
||||
q := fixed.Point26_6{
|
||||
X: dx + fixed.Int26_6(p.X),
|
||||
Y: dy - fixed.Int26_6(p.Y),
|
||||
}
|
||||
on := p.Flags&0x01 != 0
|
||||
if on {
|
||||
|
@ -146,7 +147,7 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
|
|||
if on0 {
|
||||
// No-op.
|
||||
} else {
|
||||
mid := raster.Point{
|
||||
mid := fixed.Point26_6{
|
||||
X: (q0.X + q.X) / 2,
|
||||
Y: (q0.Y + q.Y) / 2,
|
||||
}
|
||||
|
@ -166,27 +167,27 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
|
|||
// rasterize returns the advance width, glyph mask and integer-pixel offset
|
||||
// to render the given glyph at the given sub-pixel offsets.
|
||||
// The 24.8 fixed point arguments fx and fy must be in the range [0, 1).
|
||||
func (c *Context) rasterize(glyph truetype.Index, fx, fy raster.Fix32) (
|
||||
raster.Fix32, *image.Alpha, image.Point, error) {
|
||||
func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) (
|
||||
fixed.Int26_6, *image.Alpha, image.Point, error) {
|
||||
|
||||
if err := c.glyphBuf.Load(c.font, c.scale, glyph, truetype.Hinting(c.hinting)); err != nil {
|
||||
return 0, nil, image.Point{}, err
|
||||
}
|
||||
// Calculate the integer-pixel bounds for the glyph.
|
||||
xmin := int(fx+raster.Fix32(c.glyphBuf.B.XMin<<2)) >> 8
|
||||
ymin := int(fy-raster.Fix32(c.glyphBuf.B.YMax<<2)) >> 8
|
||||
xmax := int(fx+raster.Fix32(c.glyphBuf.B.XMax<<2)+0xff) >> 8
|
||||
ymax := int(fy-raster.Fix32(c.glyphBuf.B.YMin<<2)+0xff) >> 8
|
||||
xmin := int(fx+fixed.Int26_6(c.glyphBuf.B.XMin)) >> 6
|
||||
ymin := int(fy-fixed.Int26_6(c.glyphBuf.B.YMax)) >> 6
|
||||
xmax := int(fx+fixed.Int26_6(c.glyphBuf.B.XMax)+0xff) >> 6
|
||||
ymax := int(fy-fixed.Int26_6(c.glyphBuf.B.YMin)+0xff) >> 6
|
||||
if xmin > xmax || ymin > ymax {
|
||||
return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph")
|
||||
}
|
||||
// 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 += raster.Fix32(-xmin << 8)
|
||||
fy += raster.Fix32(-ymin << 8)
|
||||
// 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.
|
||||
c.r.Clear()
|
||||
e0 := 0
|
||||
|
@ -196,23 +197,23 @@ func (c *Context) rasterize(glyph truetype.Index, fx, fy raster.Fix32) (
|
|||
}
|
||||
a := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
|
||||
c.r.Rasterize(raster.NewAlphaSrcPainter(a))
|
||||
return raster.Fix32(c.glyphBuf.AdvanceWidth << 2), a, image.Point{xmin, ymin}, nil
|
||||
return fixed.Int26_6(c.glyphBuf.AdvanceWidth), a, image.Point{xmin, ymin}, nil
|
||||
}
|
||||
|
||||
// glyph returns the advance width, glyph mask and integer-pixel offset to
|
||||
// render the given glyph at the given sub-pixel point. It is a cache for the
|
||||
// rasterize method. Unlike rasterize, p's co-ordinates do not have to be in
|
||||
// the range [0, 1).
|
||||
func (c *Context) glyph(glyph truetype.Index, p raster.Point) (
|
||||
raster.Fix32, *image.Alpha, image.Point, error) {
|
||||
func (c *Context) glyph(glyph truetype.Index, p fixed.Point26_6) (
|
||||
fixed.Int26_6, *image.Alpha, image.Point, error) {
|
||||
|
||||
// Split p.X and p.Y into their integer and fractional parts.
|
||||
ix, fx := int(p.X>>8), p.X&0xff
|
||||
iy, fy := int(p.Y>>8), p.Y&0xff
|
||||
ix, fx := int(p.X>>6), p.X&0x3f
|
||||
iy, fy := int(p.Y>>6), p.Y&0x3f
|
||||
// Calculate the index t into the cache array.
|
||||
tg := int(glyph) % nGlyphs
|
||||
tx := int(fx) / (256 / nXFractions)
|
||||
ty := int(fy) / (256 / nYFractions)
|
||||
tx := int(fx) / (64 / nXFractions)
|
||||
ty := int(fy) / (64 / nYFractions)
|
||||
t := ((tg*nXFractions)+tx)*nYFractions + ty
|
||||
// Check for a cache hit.
|
||||
if e := c.cache[t]; e.valid && e.glyph == glyph {
|
||||
|
@ -233,24 +234,25 @@ func (c *Context) glyph(glyph truetype.Index, p raster.Point) (
|
|||
// above and to the right of the point, but some may be below or to the left.
|
||||
// For example, drawing a string that starts with a 'J' in an italic font may
|
||||
// affect pixels below and left of the point.
|
||||
// p is a raster.Point and can therefore represent sub-pixel positions.
|
||||
func (c *Context) DrawString(s string, p raster.Point) (raster.Point, error) {
|
||||
//
|
||||
// p is a fixed.Point26_6 and can therefore represent sub-pixel positions.
|
||||
func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, error) {
|
||||
if c.font == nil {
|
||||
return raster.Point{}, errors.New("freetype: DrawText called with a nil font")
|
||||
return fixed.Point26_6{}, errors.New("freetype: DrawText called with a nil font")
|
||||
}
|
||||
prev, hasPrev := truetype.Index(0), false
|
||||
for _, rune := range s {
|
||||
index := c.font.Index(rune)
|
||||
if hasPrev {
|
||||
kern := raster.Fix32(c.font.Kerning(c.scale, prev, index)) << 2
|
||||
kern := fixed.Int26_6(c.font.Kerning(c.scale, prev, index))
|
||||
if c.hinting != NoHinting {
|
||||
kern = (kern + 128) &^ 255
|
||||
kern = (kern + 32) &^ 63
|
||||
}
|
||||
p.X += kern
|
||||
}
|
||||
advanceWidth, mask, offset, err := c.glyph(index, p)
|
||||
if err != nil {
|
||||
return raster.Point{}, err
|
||||
return fixed.Point26_6{}, err
|
||||
}
|
||||
p.X += advanceWidth
|
||||
glyphRect := mask.Bounds().Add(offset)
|
||||
|
|
149
raster/geom.go
149
raster/geom.go
|
@ -8,36 +8,12 @@ package raster
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// A Fix32 is a 24.8 fixed point number.
|
||||
type Fix32 int32
|
||||
|
||||
// A Fix64 is a 48.16 fixed point number.
|
||||
type Fix64 int64
|
||||
|
||||
// String returns a human-readable representation of a 24.8 fixed point number.
|
||||
// For example, the number one-and-a-quarter becomes "1:064".
|
||||
func (x Fix32) String() string {
|
||||
if x < 0 {
|
||||
x = -x
|
||||
return fmt.Sprintf("-%d:%03d", int32(x/256), int32(x%256))
|
||||
}
|
||||
return fmt.Sprintf("%d:%03d", int32(x/256), int32(x%256))
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of a 48.16 fixed point number.
|
||||
// For example, the number one-and-a-quarter becomes "1:16384".
|
||||
func (x Fix64) String() string {
|
||||
if x < 0 {
|
||||
x = -x
|
||||
return fmt.Sprintf("-%d:%05d", int64(x/65536), int64(x%65536))
|
||||
}
|
||||
return fmt.Sprintf("%d:%05d", int64(x/65536), int64(x%65536))
|
||||
}
|
||||
|
||||
// maxAbs returns the maximum of abs(a) and abs(b).
|
||||
func maxAbs(a, b Fix32) Fix32 {
|
||||
func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
|
@ -50,138 +26,112 @@ func maxAbs(a, b Fix32) Fix32 {
|
|||
return a
|
||||
}
|
||||
|
||||
// A Point represents a two-dimensional point or vector, in 24.8 fixed point
|
||||
// format.
|
||||
type Point struct {
|
||||
X, Y Fix32
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of a Point.
|
||||
func (p Point) String() string {
|
||||
return "(" + p.X.String() + ", " + p.Y.String() + ")"
|
||||
}
|
||||
|
||||
// Add returns the vector p + q.
|
||||
func (p Point) Add(q Point) Point {
|
||||
return Point{p.X + q.X, p.Y + q.Y}
|
||||
}
|
||||
|
||||
// Sub returns the vector p - q.
|
||||
func (p Point) Sub(q Point) Point {
|
||||
return Point{p.X - q.X, p.Y - q.Y}
|
||||
}
|
||||
|
||||
// Mul returns the vector k * p.
|
||||
func (p Point) Mul(k Fix32) Point {
|
||||
return Point{p.X * k / 256, p.Y * k / 256}
|
||||
}
|
||||
|
||||
// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
|
||||
func pNeg(p Point) Point {
|
||||
return Point{-p.X, -p.Y}
|
||||
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, q Point) Fix64 {
|
||||
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 Fix64(px*qx + py*qy)
|
||||
return fixed.Int52_12(px*qx + py*qy)
|
||||
}
|
||||
|
||||
// pLen returns the length of the vector p.
|
||||
func pLen(p Point) Fix32 {
|
||||
func pLen(p fixed.Point26_6) fixed.Int26_6 {
|
||||
// TODO(nigeltao): use fixed point math.
|
||||
x := float64(p.X)
|
||||
y := float64(p.Y)
|
||||
return Fix32(math.Sqrt(x*x + y*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 Point, length Fix32) Point {
|
||||
func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
|
||||
d := pLen(p)
|
||||
if d == 0 {
|
||||
return Point{}
|
||||
return fixed.Point26_6{}
|
||||
}
|
||||
s, t := int64(length), int64(d)
|
||||
x := int64(p.X) * s / t
|
||||
y := int64(p.Y) * s / t
|
||||
return Point{Fix32(x), Fix32(y)}
|
||||
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 Point) Point {
|
||||
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 Point{Fix32(qx), Fix32(qy)}
|
||||
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 Point) Point {
|
||||
return Point{-p.Y, p.X}
|
||||
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 Point) Point {
|
||||
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 Point{Fix32(qx), Fix32(qy)}
|
||||
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 Point) Point {
|
||||
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 Point{Fix32(qx), Fix32(qy)}
|
||||
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 Point) Point {
|
||||
return Point{p.Y, -p.X}
|
||||
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 Point) Point {
|
||||
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 Point{Fix32(qx), Fix32(qy)}
|
||||
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 Point)
|
||||
Start(a fixed.Point26_6)
|
||||
// Add1 adds a linear segment to the current curve.
|
||||
Add1(b Point)
|
||||
Add1(b fixed.Point26_6)
|
||||
// Add2 adds a quadratic segment to the current curve.
|
||||
Add2(b, c Point)
|
||||
Add2(b, c fixed.Point26_6)
|
||||
// Add3 adds a cubic segment to the current curve.
|
||||
Add3(b, c, d Point)
|
||||
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 []Fix32
|
||||
type Path []fixed.Int26_6
|
||||
|
||||
// String returns a human-readable representation of a Path.
|
||||
func (p Path) String() string {
|
||||
|
@ -192,16 +142,16 @@ func (p Path) String() string {
|
|||
}
|
||||
switch p[i] {
|
||||
case 0:
|
||||
s += "S0" + fmt.Sprint([]Fix32(p[i+1:i+3]))
|
||||
s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
|
||||
i += 4
|
||||
case 1:
|
||||
s += "A1" + fmt.Sprint([]Fix32(p[i+1:i+3]))
|
||||
s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
|
||||
i += 4
|
||||
case 2:
|
||||
s += "A2" + fmt.Sprint([]Fix32(p[i+1:i+5]))
|
||||
s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5]))
|
||||
i += 6
|
||||
case 3:
|
||||
s += "A3" + fmt.Sprint([]Fix32(p[i+1:i+7]))
|
||||
s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7]))
|
||||
i += 8
|
||||
default:
|
||||
panic("freetype/raster: bad path")
|
||||
|
@ -216,22 +166,22 @@ func (p *Path) Clear() {
|
|||
}
|
||||
|
||||
// Start starts a new curve at the given point.
|
||||
func (p *Path) Start(a 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 Point) {
|
||||
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 Point) {
|
||||
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 Point) {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -241,18 +191,18 @@ func (p *Path) AddPath(q Path) {
|
|||
}
|
||||
|
||||
// AddStroke adds a stroked Path.
|
||||
func (p *Path) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
|
||||
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() Point {
|
||||
return Point{p[1], p[2]}
|
||||
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() Point {
|
||||
return Point{p[len(p)-3], p[len(p)-2]}
|
||||
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.
|
||||
|
@ -272,13 +222,22 @@ func addPathReversed(p Adder, q Path) {
|
|||
return
|
||||
case 1:
|
||||
i -= 4
|
||||
p.Add1(Point{q[i-2], q[i-1]})
|
||||
p.Add1(
|
||||
fixed.Point26_6{q[i-2], q[i-1]},
|
||||
)
|
||||
case 2:
|
||||
i -= 6
|
||||
p.Add2(Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]})
|
||||
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(Point{q[i+4], q[i+5]}, Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]})
|
||||
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")
|
||||
}
|
||||
|
|
187
raster/raster.go
187
raster/raster.go
|
@ -3,20 +3,22 @@
|
|||
// FreeType License or the GNU General Public License version 2 (or
|
||||
// any later version), both of which can be found in the LICENSE file.
|
||||
|
||||
// The raster package provides an anti-aliasing 2-D rasterizer.
|
||||
// Package raster provides an anti-aliasing 2-D rasterizer.
|
||||
//
|
||||
// It is part of the larger Freetype-Go suite of font-related packages,
|
||||
// but the raster package is not specific to font rasterization, and can
|
||||
// be used standalone without any other Freetype-Go package.
|
||||
// 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
|
||||
// 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
|
||||
|
@ -41,7 +43,7 @@ type Rasterizer struct {
|
|||
splitScale2, splitScale3 int
|
||||
|
||||
// The current pen position.
|
||||
a Point
|
||||
a fixed.Point26_6
|
||||
// The current cell and its area/coverage being accumulated.
|
||||
xi, yi int
|
||||
area, cover int
|
||||
|
@ -116,12 +118,12 @@ func (r *Rasterizer) setCell(xi, yi int) {
|
|||
// scan accumulates area/coverage for the yi'th scanline, going from
|
||||
// x0 to x1 in the horizontal direction (in 24.8 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 Fix32) {
|
||||
// Break the 24.8 fixed point X co-ordinates into integral and fractional parts.
|
||||
x0i := int(x0) / 256
|
||||
x0f := x0 - Fix32(256*x0i)
|
||||
x1i := int(x1) / 256
|
||||
x1f := x1 - Fix32(256*x1i)
|
||||
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 {
|
||||
|
@ -137,17 +139,17 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
|
|||
}
|
||||
// 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 256 units in 24.8 fixed point format.
|
||||
// or 64 units in 26.6 fixed point format.
|
||||
var (
|
||||
p, q, edge0, edge1 Fix32
|
||||
p, q, edge0, edge1 fixed.Int26_6
|
||||
xiDelta int
|
||||
)
|
||||
if dx > 0 {
|
||||
p, q = (256-x0f)*dy, dx
|
||||
edge0, edge1, xiDelta = 0, 256, 1
|
||||
p, q = (64-x0f)*dy, dx
|
||||
edge0, edge1, xiDelta = 0, 64, 1
|
||||
} else {
|
||||
p, q = x0f*dy, -dx
|
||||
edge0, edge1, xiDelta = 256, 0, -1
|
||||
edge0, edge1, xiDelta = 64, 0, -1
|
||||
}
|
||||
yDelta, yRem := p/q, p%q
|
||||
if yRem < 0 {
|
||||
|
@ -162,7 +164,7 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
|
|||
r.setCell(xi, yi)
|
||||
if xi != x1i {
|
||||
// Do all the intermediate cells.
|
||||
p = 256 * (y1f - y + yDelta)
|
||||
p = 64 * (y1f - y + yDelta)
|
||||
fullDelta, fullRem := p/q, p%q
|
||||
if fullRem < 0 {
|
||||
fullDelta -= 1
|
||||
|
@ -176,7 +178,7 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
|
|||
yDelta += 1
|
||||
yRem -= q
|
||||
}
|
||||
r.area += int(256 * yDelta)
|
||||
r.area += int(64 * yDelta)
|
||||
r.cover += int(yDelta)
|
||||
xi, y = xi+xiDelta, y+yDelta
|
||||
r.setCell(xi, yi)
|
||||
|
@ -189,21 +191,22 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
|
|||
}
|
||||
|
||||
// Start starts a new curve at the given point.
|
||||
func (r *Rasterizer) Start(a Point) {
|
||||
r.setCell(int(a.X/256), int(a.Y/256))
|
||||
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 Point) {
|
||||
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 24.8 fixed point Y co-ordinates into integral and fractional parts.
|
||||
y0i := int(y0) / 256
|
||||
y0f := y0 - Fix32(256*y0i)
|
||||
y1i := int(y1) / 256
|
||||
y1f := y1 - Fix32(256*y1i)
|
||||
// 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.
|
||||
|
@ -213,16 +216,16 @@ func (r *Rasterizer) Add1(b Point) {
|
|||
// This is a vertical line segment. We avoid calling r.scan and instead
|
||||
// manipulate r.area and r.cover directly.
|
||||
var (
|
||||
edge0, edge1 Fix32
|
||||
edge0, edge1 fixed.Int26_6
|
||||
yiDelta int
|
||||
)
|
||||
if dy > 0 {
|
||||
edge0, edge1, yiDelta = 0, 256, 1
|
||||
edge0, edge1, yiDelta = 0, 64, 1
|
||||
} else {
|
||||
edge0, edge1, yiDelta = 256, 0, -1
|
||||
edge0, edge1, yiDelta = 64, 0, -1
|
||||
}
|
||||
x0i, yi := int(x0)/256, y0i
|
||||
x0fTimes2 := (int(x0) - (256 * x0i)) * 2
|
||||
x0i, yi := int(x0)/64, y0i
|
||||
x0fTimes2 := (int(x0) - (64 * x0i)) * 2
|
||||
// Do the first pixel.
|
||||
dcover := int(edge1 - y0f)
|
||||
darea := int(x0fTimes2 * dcover)
|
||||
|
@ -246,19 +249,19 @@ func (r *Rasterizer) Add1(b Point) {
|
|||
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 256
|
||||
// units in 24.8 fixed point format.
|
||||
// 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 Fix32
|
||||
p, q, edge0, edge1 fixed.Int26_6
|
||||
yiDelta int
|
||||
)
|
||||
if dy > 0 {
|
||||
p, q = (256-y0f)*dx, dy
|
||||
edge0, edge1, yiDelta = 0, 256, 1
|
||||
p, q = (64-y0f)*dx, dy
|
||||
edge0, edge1, yiDelta = 0, 64, 1
|
||||
} else {
|
||||
p, q = y0f*dx, -dy
|
||||
edge0, edge1, yiDelta = 256, 0, -1
|
||||
edge0, edge1, yiDelta = 64, 0, -1
|
||||
}
|
||||
xDelta, xRem := p/q, p%q
|
||||
if xRem < 0 {
|
||||
|
@ -269,10 +272,10 @@ func (r *Rasterizer) Add1(b Point) {
|
|||
x, yi := x0, y0i
|
||||
r.scan(yi, x, y0f, x+xDelta, edge1)
|
||||
x, yi = x+xDelta, yi+yiDelta
|
||||
r.setCell(int(x)/256, yi)
|
||||
r.setCell(int(x)/64, yi)
|
||||
if yi != y1i {
|
||||
// Do all the intermediate scanlines.
|
||||
p = 256 * dx
|
||||
p = 64 * dx
|
||||
fullDelta, fullRem := p/q, p%q
|
||||
if fullRem < 0 {
|
||||
fullDelta -= 1
|
||||
|
@ -288,7 +291,7 @@ func (r *Rasterizer) Add1(b Point) {
|
|||
}
|
||||
r.scan(yi, x, edge0, x+xDelta, edge1)
|
||||
x, yi = x+xDelta, yi+yiDelta
|
||||
r.setCell(int(x)/256, yi)
|
||||
r.setCell(int(x)/64, yi)
|
||||
}
|
||||
}
|
||||
// Do the last scanline.
|
||||
|
@ -299,10 +302,11 @@ func (r *Rasterizer) Add1(b Point) {
|
|||
}
|
||||
|
||||
// Add2 adds a quadratic segment to the current curve.
|
||||
func (r *Rasterizer) Add2(b, c Point) {
|
||||
// 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) / Fix32(r.splitScale2)
|
||||
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
|
||||
|
@ -315,7 +319,7 @@ func (r *Rasterizer) Add2(b, c Point) {
|
|||
}
|
||||
// Recursively decompose the curve nSplit levels deep.
|
||||
var (
|
||||
pStack [2*maxNsplit + 3]Point
|
||||
pStack [2*maxNsplit + 3]fixed.Point26_6
|
||||
sStack [maxNsplit + 1]int
|
||||
i int
|
||||
)
|
||||
|
@ -347,7 +351,7 @@ func (r *Rasterizer) Add2(b, c Point) {
|
|||
// 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(Point{midx, midy})
|
||||
r.Add1(fixed.Point26_6{midx, midy})
|
||||
r.Add1(p[0])
|
||||
i--
|
||||
}
|
||||
|
@ -355,10 +359,10 @@ func (r *Rasterizer) Add2(b, c Point) {
|
|||
}
|
||||
|
||||
// Add3 adds a cubic segment to the current curve.
|
||||
func (r *Rasterizer) Add3(b, c, d Point) {
|
||||
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) / Fix32(r.splitScale2)
|
||||
dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / Fix32(r.splitScale3)
|
||||
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
|
||||
|
@ -372,7 +376,7 @@ func (r *Rasterizer) Add3(b, c, d Point) {
|
|||
}
|
||||
// Recursively decompose the curve nSplit levels deep.
|
||||
var (
|
||||
pStack [3*maxNsplit + 4]Point
|
||||
pStack [3*maxNsplit + 4]fixed.Point26_6
|
||||
sStack [maxNsplit + 1]int
|
||||
i int
|
||||
)
|
||||
|
@ -413,7 +417,7 @@ func (r *Rasterizer) Add3(b, c, d Point) {
|
|||
// 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(Point{midx, midy})
|
||||
r.Add1(fixed.Point26_6{midx, midy})
|
||||
r.Add1(p[0])
|
||||
i--
|
||||
}
|
||||
|
@ -425,16 +429,27 @@ func (r *Rasterizer) AddPath(p Path) {
|
|||
for i := 0; i < len(p); {
|
||||
switch p[i] {
|
||||
case 0:
|
||||
r.Start(Point{p[i+1], p[i+2]})
|
||||
r.Start(
|
||||
fixed.Point26_6{p[i+1], p[i+2]},
|
||||
)
|
||||
i += 4
|
||||
case 1:
|
||||
r.Add1(Point{p[i+1], p[i+2]})
|
||||
r.Add1(
|
||||
fixed.Point26_6{p[i+1], p[i+2]},
|
||||
)
|
||||
i += 4
|
||||
case 2:
|
||||
r.Add2(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]})
|
||||
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(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]}, Point{p[i+5], p[i+6]})
|
||||
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")
|
||||
|
@ -443,43 +458,44 @@ func (r *Rasterizer) AddPath(p Path) {
|
|||
}
|
||||
|
||||
// AddStroke adds a stroked Path.
|
||||
func (r *Rasterizer) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
|
||||
func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
|
||||
Stroke(r, q, width, cr, jr)
|
||||
}
|
||||
|
||||
// Converts an area value to a uint32 alpha value. A completely filled pixel
|
||||
// corresponds to an area of 256*256*2, and an alpha of 1<<32-1. The
|
||||
// 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 1<<32-1. 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 32-bit alpha.
|
||||
// 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 32-bit
|
||||
// alpha.
|
||||
a := (area + 1) >> 1
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
alpha := uint32(a)
|
||||
if r.UseNonZeroWinding {
|
||||
if alpha > 0xffff {
|
||||
alpha = 0xffff
|
||||
if alpha > 0x0fff {
|
||||
alpha = 0x0fff
|
||||
}
|
||||
} else {
|
||||
alpha &= 0x1ffff
|
||||
if alpha > 0x10000 {
|
||||
alpha = 0x20000 - alpha
|
||||
} else if alpha == 0x10000 {
|
||||
alpha = 0x0ffff
|
||||
alpha &= 0x1fff
|
||||
if alpha > 0x1000 {
|
||||
alpha = 0x2000 - alpha
|
||||
} else if alpha == 0x1000 {
|
||||
alpha = 0x0fff
|
||||
}
|
||||
}
|
||||
alpha |= alpha << 16
|
||||
return alpha
|
||||
alpha >>= 4
|
||||
return alpha * 0x01010101
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
|
@ -487,7 +503,7 @@ func (r *Rasterizer) Rasterize(p Painter) {
|
|||
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 * 256 * 2)
|
||||
alpha := r.areaToAlpha(cover * 64 * 2)
|
||||
if alpha != 0 {
|
||||
xi0, xi1 := xi, r.cell[c].xi
|
||||
if xi0 < 0 {
|
||||
|
@ -503,7 +519,7 @@ func (r *Rasterizer) Rasterize(p Painter) {
|
|||
}
|
||||
}
|
||||
cover += r.cell[c].cover
|
||||
alpha := r.areaToAlpha(cover*256*2 - r.cell[c].area)
|
||||
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
|
||||
|
@ -529,7 +545,7 @@ func (r *Rasterizer) Rasterize(p Painter) {
|
|||
|
||||
// Clear cancels any previous calls to r.Start or r.AddXxx.
|
||||
func (r *Rasterizer) Clear() {
|
||||
r.a = Point{}
|
||||
r.a = fixed.Point26_6{}
|
||||
r.xi = 0
|
||||
r.yi = 0
|
||||
r.area = 0
|
||||
|
@ -541,7 +557,7 @@ func (r *Rasterizer) Clear() {
|
|||
}
|
||||
|
||||
// SetBounds sets the maximum width and height of the rasterized image and
|
||||
// calls Clear. The width and height are in pixels, not Fix32 units.
|
||||
// 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
|
||||
|
@ -549,10 +565,9 @@ func (r *Rasterizer) SetBounds(width, height int) {
|
|||
if height < 0 {
|
||||
height = 0
|
||||
}
|
||||
// Use the same ssN heuristic as the C Freetype implementation.
|
||||
// The C implementation uses the values 32, 16, but those are in
|
||||
// 26.6 fixed point units, and we use 24.8 fixed point everywhere.
|
||||
ss2, ss3 := 128, 64
|
||||
// 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 {
|
||||
|
|
103
raster/stroke.go
103
raster/stroke.go
|
@ -5,21 +5,25 @@
|
|||
|
||||
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. 16384 / 65536 in Fix64).
|
||||
const epsilon = 16384
|
||||
// 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 Fix32, pivot, n1 Point)
|
||||
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, Fix32, Point, Point)
|
||||
type CapperFunc func(Adder, fixed.Int26_6, fixed.Point26_6, fixed.Point26_6)
|
||||
|
||||
func (f CapperFunc) Cap(p Adder, halfWidth Fix32, pivot, n1 Point) {
|
||||
func (f CapperFunc) Cap(p Adder, halfWidth fixed.Int26_6, pivot, n1 fixed.Point26_6) {
|
||||
f(p, halfWidth, pivot, n1)
|
||||
}
|
||||
|
||||
|
@ -28,20 +32,20 @@ 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 Fix32, pivot, n0, n1 Point)
|
||||
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 Fix32, pivot, n0, n1 Point)
|
||||
type JoinerFunc func(lhs, rhs Adder, halfWidth fixed.Int26_6, pivot, n0, n1 fixed.Point26_6)
|
||||
|
||||
func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point) {
|
||||
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 Fix32, pivot, n1 Point) {
|
||||
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 141/256.
|
||||
const k = 141
|
||||
|
@ -56,14 +60,14 @@ func roundCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
|
|||
// ButtCapper adds butt caps to a stroked path.
|
||||
var ButtCapper Capper = CapperFunc(buttCapper)
|
||||
|
||||
func buttCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
|
||||
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 Fix32, pivot, n1 Point) {
|
||||
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))
|
||||
|
@ -74,7 +78,7 @@ func squareCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
|
|||
// RoundJoiner adds round joins to a stroked path.
|
||||
var RoundJoiner Joiner = JoinerFunc(roundJoiner)
|
||||
|
||||
func roundJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
|
||||
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)
|
||||
|
@ -88,7 +92,7 @@ func roundJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
|
|||
// BevelJoiner adds bevel joins to a stroked path.
|
||||
var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
|
||||
|
||||
func bevelJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
|
||||
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))
|
||||
}
|
||||
|
@ -96,7 +100,7 @@ func bevelJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
|
|||
// 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 Point) {
|
||||
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 {
|
||||
|
@ -109,7 +113,7 @@ func addArc(p Adder, pivot, n0, n1 Point) {
|
|||
// control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
|
||||
// rotated and translated. tan(π/8) is approximately 106/256.
|
||||
const tpo8 = 106
|
||||
var s Point
|
||||
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.
|
||||
|
@ -178,28 +182,28 @@ func addArc(p Adder, pivot, n0, n1 Point) {
|
|||
// 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 := Fix32(150 - 22*(d-181)/(256-181))
|
||||
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 Point) Point {
|
||||
return Point{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
|
||||
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 Point) bool {
|
||||
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 Point, t Fix64) Point {
|
||||
s := 1<<16 - t
|
||||
x := s*Fix64(a.X) + t*Fix64(b.X)
|
||||
y := s*Fix64(a.Y) + t*Fix64(b.Y)
|
||||
return Point{Fix32(x >> 16), Fix32(y >> 16)}
|
||||
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
|
||||
|
@ -216,15 +220,15 @@ func interpolate(a, b Point, t Fix64) Point {
|
|||
// (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 Point) Fix64 {
|
||||
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 32768
|
||||
return 2048
|
||||
}
|
||||
return Fix64(-65536 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
|
||||
return fixed.Int52_12(-4096 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
|
||||
}
|
||||
|
||||
// A stroker holds state for stroking a path.
|
||||
|
@ -232,7 +236,7 @@ type stroker struct {
|
|||
// p is the destination that records the stroked path.
|
||||
p Adder
|
||||
// u is the half-width of the stroke.
|
||||
u Fix32
|
||||
u fixed.Int26_6
|
||||
// cr and jr specify how to end and connect path segments.
|
||||
cr Capper
|
||||
jr Joiner
|
||||
|
@ -242,19 +246,19 @@ type stroker struct {
|
|||
r Path
|
||||
// a is the most recent segment point. anorm is the segment normal of
|
||||
// length u at that point.
|
||||
a, anorm 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 Point) {
|
||||
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]Point
|
||||
ps [2*maxDepth + 3]fixed.Point26_6
|
||||
t int
|
||||
)
|
||||
// Initially the ps stack has one quadratic segment of depth zero.
|
||||
|
@ -263,7 +267,7 @@ func (k *stroker) addNonCurvy2(b, c Point) {
|
|||
ps[1] = b
|
||||
ps[0] = c
|
||||
anorm := k.anorm
|
||||
var cnorm Point
|
||||
var cnorm fixed.Point26_6
|
||||
|
||||
for {
|
||||
depth := ds[t]
|
||||
|
@ -272,8 +276,8 @@ func (k *stroker) addNonCurvy2(b, c Point) {
|
|||
c := ps[2*t+0]
|
||||
ab := b.Sub(a)
|
||||
bc := c.Sub(b)
|
||||
abIsSmall := pDot(ab, ab) < Fix64(1<<16)
|
||||
bcIsSmall := pDot(bc, bc) < Fix64(1<<16)
|
||||
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))
|
||||
|
@ -310,7 +314,7 @@ func (k *stroker) addNonCurvy2(b, c Point) {
|
|||
}
|
||||
|
||||
// Add1 adds a linear segment to the stroker.
|
||||
func (k *stroker) Add1(b Point) {
|
||||
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))
|
||||
|
@ -324,7 +328,7 @@ func (k *stroker) Add1(b Point) {
|
|||
}
|
||||
|
||||
// Add2 adds a quadratic segment to the stroker.
|
||||
func (k *stroker) Add2(b, c Point) {
|
||||
func (k *stroker) Add2(b, c fixed.Point26_6) {
|
||||
ab := b.Sub(k.a)
|
||||
bc := c.Sub(b)
|
||||
abnorm := pRot90CCW(pNorm(ab, k.u))
|
||||
|
@ -349,7 +353,7 @@ func (k *stroker) Add2(b, c Point) {
|
|||
// 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 || 65536 <= t {
|
||||
if t <= 0 || 4096 <= t {
|
||||
k.addNonCurvy2(b, c)
|
||||
return
|
||||
}
|
||||
|
@ -364,7 +368,7 @@ func (k *stroker) Add2(b, c Point) {
|
|||
// 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) < -Fix64(k.u)*Fix64(k.u)*2047/2048 {
|
||||
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))
|
||||
|
@ -395,7 +399,7 @@ func (k *stroker) Add2(b, c Point) {
|
|||
}
|
||||
|
||||
// Add3 adds a cubic segment to the stroker.
|
||||
func (k *stroker) Add3(b, c, d Point) {
|
||||
func (k *stroker) Add3(b, c, d fixed.Point26_6) {
|
||||
panic("freetype/raster: stroke unimplemented for cubic segments")
|
||||
}
|
||||
|
||||
|
@ -406,17 +410,26 @@ func (k *stroker) stroke(q Path) {
|
|||
// 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 = Point{q[1], q[2]}
|
||||
k.a = fixed.Point26_6{q[1], q[2]}
|
||||
for i := 4; i < len(q); {
|
||||
switch q[i] {
|
||||
case 1:
|
||||
k.Add1(Point{q[i+1], q[i+2]})
|
||||
k.Add1(
|
||||
fixed.Point26_6{q[i+1], q[i+2]},
|
||||
)
|
||||
i += 4
|
||||
case 2:
|
||||
k.Add2(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]})
|
||||
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(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]}, Point{q[i+5], q[i+6]})
|
||||
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")
|
||||
|
@ -430,13 +443,13 @@ func (k *stroker) stroke(q Path) {
|
|||
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(Point{k.r[1], k.r[2]}))
|
||||
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 Fix32, cr Capper, jr Joiner) {
|
||||
func Stroke(p Adder, q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
|
||||
if len(q) == 0 {
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue