Use the fixed.Int26_6 and fixed.Point26_6 types.
This commit is contained in:
parent
d91c1d6844
commit
856a70c395
|
@ -20,12 +20,13 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/golang/freetype/raster"
|
"github.com/golang/freetype/raster"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
func p(x, y int) raster.Point {
|
func p(x, y int) fixed.Point26_6 {
|
||||||
return raster.Point{
|
return fixed.Point26_6{
|
||||||
X: raster.Fix32(x * 256),
|
X: fixed.Int26_6(x * 64),
|
||||||
Y: raster.Fix32(y * 256),
|
Y: fixed.Int26_6(y * 64),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/golang/freetype/raster"
|
"github.com/golang/freetype/raster"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
|
@ -87,11 +88,11 @@ var inside = []node{
|
||||||
node{686, 1274, -1},
|
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
|
x, y := 20+n.x/4, 380-n.y/4
|
||||||
return raster.Point{
|
return fixed.Point26_6{
|
||||||
X: raster.Fix32(x * 256),
|
X: fixed.Int26_6(x << 6),
|
||||||
Y: raster.Fix32(y * 256),
|
Y: fixed.Int26_6(y << 6),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +122,7 @@ func contour(r *raster.Rasterizer, ns []node) {
|
||||||
func showNodes(m *image.RGBA, ns []node) {
|
func showNodes(m *image.RGBA, ns []node) {
|
||||||
for _, n := range ns {
|
for _, n := range ns {
|
||||||
p := p(n)
|
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()) {
|
if !(image.Point{x, y}).In(m.Bounds()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,22 +27,23 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/golang/freetype/raster"
|
"github.com/golang/freetype/raster"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// pDot returns the dot product p·q.
|
// 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)
|
px, py := int64(p.X), int64(p.Y)
|
||||||
qx, qy := int64(q.X), int64(q.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() {
|
func main() {
|
||||||
const (
|
const (
|
||||||
n = 17
|
n = 17
|
||||||
r = 256 * 80
|
r = 64 * 80
|
||||||
)
|
)
|
||||||
s := raster.Fix32(r * math.Sqrt(2) / 2)
|
s := fixed.Int26_6(r * math.Sqrt(2) / 2)
|
||||||
t := raster.Fix32(r * math.Tan(math.Pi/8))
|
t := fixed.Int26_6(r * math.Tan(math.Pi/8))
|
||||||
|
|
||||||
m := image.NewRGBA(image.Rect(0, 0, 800, 600))
|
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)
|
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)
|
z := raster.NewRasterizer(800, 600)
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
cx := raster.Fix32(25600 + 51200*(i%4))
|
cx := fixed.Int26_6(6400 + 12800*(i%4))
|
||||||
cy := raster.Fix32(2560 + 32000*(i/4))
|
cy := fixed.Int26_6(640 + 8000*(i/4))
|
||||||
c := raster.Point{X: cx, Y: cy}
|
c := fixed.Point26_6{X: cx, Y: cy}
|
||||||
theta := math.Pi * (0.5 + 0.5*float64(i)/(n-1))
|
theta := math.Pi * (0.5 + 0.5*float64(i)/(n-1))
|
||||||
dx := raster.Fix32(r * math.Cos(theta))
|
dx := fixed.Int26_6(r * math.Cos(theta))
|
||||||
dy := raster.Fix32(r * math.Sin(theta))
|
dy := fixed.Int26_6(r * math.Sin(theta))
|
||||||
d := raster.Point{X: dx, Y: dy}
|
d := fixed.Point26_6{X: dx, Y: dy}
|
||||||
// Draw a quarter-circle approximated by two quadratic segments,
|
// Draw a quarter-circle approximated by two quadratic segments,
|
||||||
// with each segment spanning 45 degrees.
|
// with each segment spanning 45 degrees.
|
||||||
z.Start(c)
|
z.Start(c)
|
||||||
z.Add1(c.Add(raster.Point{X: r, Y: 0}))
|
z.Add1(c.Add(fixed.Point26_6{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(fixed.Point26_6{X: r, Y: t}), c.Add(fixed.Point26_6{X: s, Y: s}))
|
||||||
z.Add2(c.Add(raster.Point{X: t, Y: r}), c.Add(raster.Point{X: 0, Y: r}))
|
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.
|
// Add another quadratic segment whose angle ranges between 0 and 90
|
||||||
// For an explanation of the magic constants 22, 150, 181 and 256, read the
|
// degrees. For an explanation of the magic constants 128, 150, 181 and
|
||||||
// comments in the freetype/raster package.
|
// 256, read the comments in the freetype/raster package.
|
||||||
dot := 256 * pDot(d, raster.Point{X: 0, Y: r}) / (r * r)
|
dot := 256 * pDot(d, fixed.Point26_6{X: 0, Y: r}) / (r * r)
|
||||||
multiple := raster.Fix32(150 - 22*(dot-181)/(256-181))
|
multiple := fixed.Int26_6(150-(150-128)*(dot-181)/(256-181)) >> 2
|
||||||
z.Add2(c.Add(raster.Point{X: dx, Y: r + dy}.Mul(multiple)), c.Add(d))
|
z.Add2(c.Add(fixed.Point26_6{X: dx, Y: r + dy}.Mul(multiple)), c.Add(d))
|
||||||
// Close the curve.
|
// Close the curve.
|
||||||
z.Add1(c)
|
z.Add1(c)
|
||||||
}
|
}
|
||||||
z.Rasterize(mp)
|
z.Rasterize(mp)
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
cx := raster.Fix32(25600 + 51200*(i%4))
|
cx := fixed.Int26_6(6400 + 12800*(i%4))
|
||||||
cy := raster.Fix32(2560 + 32000*(i/4))
|
cy := fixed.Int26_6(640 + 8000*(i/4))
|
||||||
for j := 0; j < n; j++ {
|
for j := 0; j < n; j++ {
|
||||||
theta := math.Pi * float64(j) / (n - 1)
|
theta := math.Pi * float64(j) / (n - 1)
|
||||||
dx := raster.Fix32(r * math.Cos(theta))
|
dx := fixed.Int26_6(r * math.Cos(theta))
|
||||||
dy := raster.Fix32(r * math.Sin(theta))
|
dy := fixed.Int26_6(r * math.Sin(theta))
|
||||||
m.Set(int((cx+dx)/256), int((cy+dy)/256), color.RGBA{255, 255, 0, 255})
|
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/raster"
|
||||||
"github.com/golang/freetype/truetype"
|
"github.com/golang/freetype/truetype"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These constants determine the size of the glyph cache. The cache is keyed
|
// These constants determine the size of the glyph cache. The cache is keyed
|
||||||
|
@ -33,7 +34,7 @@ const (
|
||||||
type cacheEntry struct {
|
type cacheEntry struct {
|
||||||
valid bool
|
valid bool
|
||||||
glyph truetype.Index
|
glyph truetype.Index
|
||||||
advanceWidth raster.Fix32
|
advanceWidth fixed.Int26_6
|
||||||
mask *image.Alpha
|
mask *image.Alpha
|
||||||
offset image.Point
|
offset image.Point
|
||||||
}
|
}
|
||||||
|
@ -45,12 +46,12 @@ func ParseFont(b []byte) (*truetype.Font, error) {
|
||||||
return truetype.Parse(b)
|
return truetype.Parse(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pt converts from a co-ordinate pair measured in pixels to a raster.Point
|
// Pt converts from a co-ordinate pair measured in pixels to a fixed.Point26_6
|
||||||
// co-ordinate pair measured in raster.Fix32 units.
|
// co-ordinate pair measured in fixed.Int26_6 units.
|
||||||
func Pt(x, y int) raster.Point {
|
func Pt(x, y int) fixed.Point26_6 {
|
||||||
return raster.Point{
|
return fixed.Point26_6{
|
||||||
X: raster.Fix32(x << 8),
|
X: fixed.Int26_6(x << 6),
|
||||||
Y: raster.Fix32(y << 8),
|
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'')
|
// PointToFix32 converts the given number of points (as in ``a 12 point font'')
|
||||||
// into fixed point units.
|
// into fixed point units.
|
||||||
func (c *Context) PointToFix32(x float64) raster.Fix32 {
|
func (c *Context) PointToFix32(x float64) fixed.Int26_6 {
|
||||||
return raster.Fix32(x * float64(c.dpi) * (256.0 / 72.0))
|
return fixed.Int26_6(x * float64(c.dpi) * (64.0 / 72.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawContour draws the given closed contour with the given offset.
|
// 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 {
|
if len(ps) == 0 {
|
||||||
return
|
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
|
// 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
|
// upwards. start is the same thing measured in fixed point units and
|
||||||
// positive Y going downwards, and offset by (dx, dy).
|
// positive Y going downwards, and offset by (dx, dy).
|
||||||
start := raster.Point{
|
start := fixed.Point26_6{
|
||||||
X: dx + raster.Fix32(ps[0].X<<2),
|
X: dx + fixed.Int26_6(ps[0].X),
|
||||||
Y: dy - raster.Fix32(ps[0].Y<<2),
|
Y: dy - fixed.Int26_6(ps[0].Y),
|
||||||
}
|
}
|
||||||
others := []truetype.Point(nil)
|
others := []truetype.Point(nil)
|
||||||
if ps[0].Flags&0x01 != 0 {
|
if ps[0].Flags&0x01 != 0 {
|
||||||
others = ps[1:]
|
others = ps[1:]
|
||||||
} else {
|
} else {
|
||||||
last := raster.Point{
|
last := fixed.Point26_6{
|
||||||
X: dx + raster.Fix32(ps[len(ps)-1].X<<2),
|
X: dx + fixed.Int26_6(ps[len(ps)-1].X),
|
||||||
Y: dy - raster.Fix32(ps[len(ps)-1].Y<<2),
|
Y: dy - fixed.Int26_6(ps[len(ps)-1].Y),
|
||||||
}
|
}
|
||||||
if ps[len(ps)-1].Flags&0x01 != 0 {
|
if ps[len(ps)-1].Flags&0x01 != 0 {
|
||||||
start = last
|
start = last
|
||||||
others = ps[:len(ps)-1]
|
others = ps[:len(ps)-1]
|
||||||
} else {
|
} else {
|
||||||
start = raster.Point{
|
start = fixed.Point26_6{
|
||||||
X: (start.X + last.X) / 2,
|
X: (start.X + last.X) / 2,
|
||||||
Y: (start.Y + last.Y) / 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)
|
c.r.Start(start)
|
||||||
q0, on0 := start, true
|
q0, on0 := start, true
|
||||||
for _, p := range others {
|
for _, p := range others {
|
||||||
q := raster.Point{
|
q := fixed.Point26_6{
|
||||||
X: dx + raster.Fix32(p.X<<2),
|
X: dx + fixed.Int26_6(p.X),
|
||||||
Y: dy - raster.Fix32(p.Y<<2),
|
Y: dy - fixed.Int26_6(p.Y),
|
||||||
}
|
}
|
||||||
on := p.Flags&0x01 != 0
|
on := p.Flags&0x01 != 0
|
||||||
if on {
|
if on {
|
||||||
|
@ -146,7 +147,7 @@ func (c *Context) drawContour(ps []truetype.Point, dx, dy raster.Fix32) {
|
||||||
if on0 {
|
if on0 {
|
||||||
// No-op.
|
// No-op.
|
||||||
} else {
|
} else {
|
||||||
mid := raster.Point{
|
mid := fixed.Point26_6{
|
||||||
X: (q0.X + q.X) / 2,
|
X: (q0.X + q.X) / 2,
|
||||||
Y: (q0.Y + q.Y) / 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
|
// rasterize returns the advance width, glyph mask and integer-pixel offset
|
||||||
// to render the given glyph at the given sub-pixel offsets.
|
// 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).
|
// 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) (
|
func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) (
|
||||||
raster.Fix32, *image.Alpha, image.Point, error) {
|
fixed.Int26_6, *image.Alpha, image.Point, error) {
|
||||||
|
|
||||||
if err := c.glyphBuf.Load(c.font, c.scale, glyph, truetype.Hinting(c.hinting)); err != nil {
|
if err := c.glyphBuf.Load(c.font, c.scale, glyph, truetype.Hinting(c.hinting)); err != nil {
|
||||||
return 0, nil, image.Point{}, err
|
return 0, nil, image.Point{}, err
|
||||||
}
|
}
|
||||||
// Calculate the integer-pixel bounds for the glyph.
|
// Calculate the integer-pixel bounds for the glyph.
|
||||||
xmin := int(fx+raster.Fix32(c.glyphBuf.B.XMin<<2)) >> 8
|
xmin := int(fx+fixed.Int26_6(c.glyphBuf.B.XMin)) >> 6
|
||||||
ymin := int(fy-raster.Fix32(c.glyphBuf.B.YMax<<2)) >> 8
|
ymin := int(fy-fixed.Int26_6(c.glyphBuf.B.YMax)) >> 6
|
||||||
xmax := int(fx+raster.Fix32(c.glyphBuf.B.XMax<<2)+0xff) >> 8
|
xmax := int(fx+fixed.Int26_6(c.glyphBuf.B.XMax)+0xff) >> 6
|
||||||
ymax := int(fy-raster.Fix32(c.glyphBuf.B.YMin<<2)+0xff) >> 8
|
ymax := int(fy-fixed.Int26_6(c.glyphBuf.B.YMin)+0xff) >> 6
|
||||||
if xmin > xmax || ymin > ymax {
|
if xmin > xmax || ymin > ymax {
|
||||||
return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph")
|
return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph")
|
||||||
}
|
}
|
||||||
// A TrueType's glyph's nodes can have negative co-ordinates, but the
|
// 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
|
// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
|
||||||
// are the pixel offsets, based on the font's FUnit metrics, that let
|
// the pixel offsets, based on the font's FUnit metrics, that let a
|
||||||
// a negative co-ordinate in TrueType space be non-negative in
|
// negative co-ordinate in TrueType space be non-negative in rasterizer
|
||||||
// rasterizer space. xmin and ymin are typically <= 0.
|
// space. xmin and ymin are typically <= 0.
|
||||||
fx += raster.Fix32(-xmin << 8)
|
fx += fixed.Int26_6(-xmin << 6)
|
||||||
fy += raster.Fix32(-ymin << 8)
|
fy += fixed.Int26_6(-ymin << 6)
|
||||||
// Rasterize the glyph's vectors.
|
// Rasterize the glyph's vectors.
|
||||||
c.r.Clear()
|
c.r.Clear()
|
||||||
e0 := 0
|
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))
|
a := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
|
||||||
c.r.Rasterize(raster.NewAlphaSrcPainter(a))
|
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
|
// 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
|
// 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
|
// rasterize method. Unlike rasterize, p's co-ordinates do not have to be in
|
||||||
// the range [0, 1).
|
// the range [0, 1).
|
||||||
func (c *Context) glyph(glyph truetype.Index, p raster.Point) (
|
func (c *Context) glyph(glyph truetype.Index, p fixed.Point26_6) (
|
||||||
raster.Fix32, *image.Alpha, image.Point, error) {
|
fixed.Int26_6, *image.Alpha, image.Point, error) {
|
||||||
|
|
||||||
// Split p.X and p.Y into their integer and fractional parts.
|
// Split p.X and p.Y into their integer and fractional parts.
|
||||||
ix, fx := int(p.X>>8), p.X&0xff
|
ix, fx := int(p.X>>6), p.X&0x3f
|
||||||
iy, fy := int(p.Y>>8), p.Y&0xff
|
iy, fy := int(p.Y>>6), p.Y&0x3f
|
||||||
// Calculate the index t into the cache array.
|
// Calculate the index t into the cache array.
|
||||||
tg := int(glyph) % nGlyphs
|
tg := int(glyph) % nGlyphs
|
||||||
tx := int(fx) / (256 / nXFractions)
|
tx := int(fx) / (64 / nXFractions)
|
||||||
ty := int(fy) / (256 / nYFractions)
|
ty := int(fy) / (64 / nYFractions)
|
||||||
t := ((tg*nXFractions)+tx)*nYFractions + ty
|
t := ((tg*nXFractions)+tx)*nYFractions + ty
|
||||||
// Check for a cache hit.
|
// Check for a cache hit.
|
||||||
if e := c.cache[t]; e.valid && e.glyph == glyph {
|
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.
|
// 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
|
// For example, drawing a string that starts with a 'J' in an italic font may
|
||||||
// affect pixels below and left of the point.
|
// 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 {
|
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
|
prev, hasPrev := truetype.Index(0), false
|
||||||
for _, rune := range s {
|
for _, rune := range s {
|
||||||
index := c.font.Index(rune)
|
index := c.font.Index(rune)
|
||||||
if hasPrev {
|
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 {
|
if c.hinting != NoHinting {
|
||||||
kern = (kern + 128) &^ 255
|
kern = (kern + 32) &^ 63
|
||||||
}
|
}
|
||||||
p.X += kern
|
p.X += kern
|
||||||
}
|
}
|
||||||
advanceWidth, mask, offset, err := c.glyph(index, p)
|
advanceWidth, mask, offset, err := c.glyph(index, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return raster.Point{}, err
|
return fixed.Point26_6{}, err
|
||||||
}
|
}
|
||||||
p.X += advanceWidth
|
p.X += advanceWidth
|
||||||
glyphRect := mask.Bounds().Add(offset)
|
glyphRect := mask.Bounds().Add(offset)
|
||||||
|
|
149
raster/geom.go
149
raster/geom.go
|
@ -8,36 +8,12 @@ package raster
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"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).
|
// 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 {
|
if a < 0 {
|
||||||
a = -a
|
a = -a
|
||||||
}
|
}
|
||||||
|
@ -50,138 +26,112 @@ func maxAbs(a, b Fix32) Fix32 {
|
||||||
return a
|
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.
|
// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
|
||||||
func pNeg(p Point) Point {
|
func pNeg(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
return Point{-p.X, -p.Y}
|
return fixed.Point26_6{-p.X, -p.Y}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pDot returns the dot product p·q.
|
// 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)
|
px, py := int64(p.X), int64(p.Y)
|
||||||
qx, qy := int64(q.X), int64(q.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.
|
// 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.
|
// TODO(nigeltao): use fixed point math.
|
||||||
x := float64(p.X)
|
x := float64(p.X)
|
||||||
y := float64(p.Y)
|
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
|
// pNorm returns the vector p normalized to the given length, or zero if p is
|
||||||
// degenerate.
|
// degenerate.
|
||||||
func pNorm(p Point, length Fix32) Point {
|
func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
|
||||||
d := pLen(p)
|
d := pLen(p)
|
||||||
if d == 0 {
|
if d == 0 {
|
||||||
return Point{}
|
return fixed.Point26_6{}
|
||||||
}
|
}
|
||||||
s, t := int64(length), int64(d)
|
s, t := int64(length), int64(d)
|
||||||
x := int64(p.X) * s / t
|
x := int64(p.X) * s / t
|
||||||
y := int64(p.Y) * 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.
|
// 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}.
|
// 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).
|
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||||
px, py := int64(p.X), int64(p.Y)
|
px, py := int64(p.X), int64(p.Y)
|
||||||
qx := (+px - py) * 181 / 256
|
qx := (+px - py) * 181 / 256
|
||||||
qy := (+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.
|
// pRot90CW returns the vector p rotated clockwise by 90 degrees.
|
||||||
//
|
//
|
||||||
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
|
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
|
||||||
func pRot90CW(p Point) Point {
|
func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
return Point{-p.Y, p.X}
|
return fixed.Point26_6{-p.Y, p.X}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pRot135CW returns the vector p rotated clockwise by 135 degrees.
|
// 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}.
|
// 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).
|
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||||
px, py := int64(p.X), int64(p.Y)
|
px, py := int64(p.X), int64(p.Y)
|
||||||
qx := (-px - py) * 181 / 256
|
qx := (-px - py) * 181 / 256
|
||||||
qy := (+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.
|
// 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}.
|
// 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).
|
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||||
px, py := int64(p.X), int64(p.Y)
|
px, py := int64(p.X), int64(p.Y)
|
||||||
qx := (+px + py) * 181 / 256
|
qx := (+px + py) * 181 / 256
|
||||||
qy := (-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.
|
// 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}.
|
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
|
||||||
func pRot90CCW(p Point) Point {
|
func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
|
||||||
return Point{p.Y, -p.X}
|
return fixed.Point26_6{p.Y, -p.X}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
|
// 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}.
|
// 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).
|
// 181/256 is approximately 1/√2, or sin(π/4).
|
||||||
px, py := int64(p.X), int64(p.Y)
|
px, py := int64(p.X), int64(p.Y)
|
||||||
qx := (-px + py) * 181 / 256
|
qx := (-px + py) * 181 / 256
|
||||||
qy := (-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.
|
// An Adder accumulates points on a curve.
|
||||||
type Adder interface {
|
type Adder interface {
|
||||||
// Start starts a new curve at the given point.
|
// 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 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 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 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
|
// A Path is a sequence of curves, and a curve is a start point followed by a
|
||||||
// sequence of linear, quadratic or cubic segments.
|
// sequence of linear, quadratic or cubic segments.
|
||||||
type Path []Fix32
|
type Path []fixed.Int26_6
|
||||||
|
|
||||||
// String returns a human-readable representation of a Path.
|
// String returns a human-readable representation of a Path.
|
||||||
func (p Path) String() string {
|
func (p Path) String() string {
|
||||||
|
@ -192,16 +142,16 @@ func (p Path) String() string {
|
||||||
}
|
}
|
||||||
switch p[i] {
|
switch p[i] {
|
||||||
case 0:
|
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
|
i += 4
|
||||||
case 1:
|
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
|
i += 4
|
||||||
case 2:
|
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
|
i += 6
|
||||||
case 3:
|
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
|
i += 8
|
||||||
default:
|
default:
|
||||||
panic("freetype/raster: bad path")
|
panic("freetype/raster: bad path")
|
||||||
|
@ -216,22 +166,22 @@ func (p *Path) Clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts a new curve at the given point.
|
// 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)
|
*p = append(*p, 0, a.X, a.Y, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add1 adds a linear segment to the current curve.
|
// 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)
|
*p = append(*p, 1, b.X, b.Y, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add2 adds a quadratic segment to the current curve.
|
// 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)
|
*p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add3 adds a cubic segment to the current curve.
|
// 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)
|
*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.
|
// 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)
|
Stroke(p, q, width, cr, jr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// firstPoint returns the first point in a non-empty Path.
|
// firstPoint returns the first point in a non-empty Path.
|
||||||
func (p Path) firstPoint() Point {
|
func (p Path) firstPoint() fixed.Point26_6 {
|
||||||
return Point{p[1], p[2]}
|
return fixed.Point26_6{p[1], p[2]}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lastPoint returns the last point in a non-empty Path.
|
// lastPoint returns the last point in a non-empty Path.
|
||||||
func (p Path) lastPoint() Point {
|
func (p Path) lastPoint() fixed.Point26_6 {
|
||||||
return Point{p[len(p)-3], p[len(p)-2]}
|
return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
|
||||||
}
|
}
|
||||||
|
|
||||||
// addPathReversed adds q reversed to p.
|
// addPathReversed adds q reversed to p.
|
||||||
|
@ -272,13 +222,22 @@ func addPathReversed(p Adder, q Path) {
|
||||||
return
|
return
|
||||||
case 1:
|
case 1:
|
||||||
i -= 4
|
i -= 4
|
||||||
p.Add1(Point{q[i-2], q[i-1]})
|
p.Add1(
|
||||||
|
fixed.Point26_6{q[i-2], q[i-1]},
|
||||||
|
)
|
||||||
case 2:
|
case 2:
|
||||||
i -= 6
|
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:
|
case 3:
|
||||||
i -= 8
|
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:
|
default:
|
||||||
panic("freetype/raster: bad path")
|
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
|
// FreeType License or the GNU General Public License version 2 (or
|
||||||
// any later version), both of which can be found in the LICENSE file.
|
// 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,
|
// It is part of the larger Freetype suite of font-related packages, but the
|
||||||
// but the raster package is not specific to font rasterization, and can
|
// raster package is not specific to font rasterization, and can be used
|
||||||
// be used standalone without any other Freetype-Go package.
|
// standalone without any other Freetype package.
|
||||||
//
|
//
|
||||||
// Rasterization is done by the same area/coverage accumulation algorithm
|
// Rasterization is done by the same area/coverage accumulation algorithm as
|
||||||
// as the Freetype "smooth" module, and the Anti-Grain Geometry library.
|
// the Freetype "smooth" module, and the Anti-Grain Geometry library. A
|
||||||
// A description of the area/coverage algorithm is at
|
// description of the area/coverage algorithm is at
|
||||||
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
|
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
|
||||||
package raster // import "github.com/golang/freetype/raster"
|
package raster // import "github.com/golang/freetype/raster"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
|
// 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
|
splitScale2, splitScale3 int
|
||||||
|
|
||||||
// The current pen position.
|
// The current pen position.
|
||||||
a Point
|
a fixed.Point26_6
|
||||||
// The current cell and its area/coverage being accumulated.
|
// The current cell and its area/coverage being accumulated.
|
||||||
xi, yi int
|
xi, yi int
|
||||||
area, cover 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
|
// 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)
|
// 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.
|
// and from y0f to y1f fractional vertical units within that scanline.
|
||||||
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
|
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
|
||||||
// Break the 24.8 fixed point X co-ordinates into integral and fractional parts.
|
// Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
|
||||||
x0i := int(x0) / 256
|
x0i := int(x0) / 64
|
||||||
x0f := x0 - Fix32(256*x0i)
|
x0f := x0 - fixed.Int26_6(64*x0i)
|
||||||
x1i := int(x1) / 256
|
x1i := int(x1) / 64
|
||||||
x1f := x1 - Fix32(256*x1i)
|
x1f := x1 - fixed.Int26_6(64*x1i)
|
||||||
|
|
||||||
// A perfectly horizontal scan.
|
// A perfectly horizontal scan.
|
||||||
if y0f == y1f {
|
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,
|
// There are at least two cells. Apart from the first and last cells,
|
||||||
// all intermediate cells go through the full width of the cell,
|
// 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 (
|
var (
|
||||||
p, q, edge0, edge1 Fix32
|
p, q, edge0, edge1 fixed.Int26_6
|
||||||
xiDelta int
|
xiDelta int
|
||||||
)
|
)
|
||||||
if dx > 0 {
|
if dx > 0 {
|
||||||
p, q = (256-x0f)*dy, dx
|
p, q = (64-x0f)*dy, dx
|
||||||
edge0, edge1, xiDelta = 0, 256, 1
|
edge0, edge1, xiDelta = 0, 64, 1
|
||||||
} else {
|
} else {
|
||||||
p, q = x0f*dy, -dx
|
p, q = x0f*dy, -dx
|
||||||
edge0, edge1, xiDelta = 256, 0, -1
|
edge0, edge1, xiDelta = 64, 0, -1
|
||||||
}
|
}
|
||||||
yDelta, yRem := p/q, p%q
|
yDelta, yRem := p/q, p%q
|
||||||
if yRem < 0 {
|
if yRem < 0 {
|
||||||
|
@ -162,7 +164,7 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
|
||||||
r.setCell(xi, yi)
|
r.setCell(xi, yi)
|
||||||
if xi != x1i {
|
if xi != x1i {
|
||||||
// Do all the intermediate cells.
|
// Do all the intermediate cells.
|
||||||
p = 256 * (y1f - y + yDelta)
|
p = 64 * (y1f - y + yDelta)
|
||||||
fullDelta, fullRem := p/q, p%q
|
fullDelta, fullRem := p/q, p%q
|
||||||
if fullRem < 0 {
|
if fullRem < 0 {
|
||||||
fullDelta -= 1
|
fullDelta -= 1
|
||||||
|
@ -176,7 +178,7 @@ func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
|
||||||
yDelta += 1
|
yDelta += 1
|
||||||
yRem -= q
|
yRem -= q
|
||||||
}
|
}
|
||||||
r.area += int(256 * yDelta)
|
r.area += int(64 * yDelta)
|
||||||
r.cover += int(yDelta)
|
r.cover += int(yDelta)
|
||||||
xi, y = xi+xiDelta, y+yDelta
|
xi, y = xi+xiDelta, y+yDelta
|
||||||
r.setCell(xi, yi)
|
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.
|
// Start starts a new curve at the given point.
|
||||||
func (r *Rasterizer) Start(a Point) {
|
func (r *Rasterizer) Start(a fixed.Point26_6) {
|
||||||
r.setCell(int(a.X/256), int(a.Y/256))
|
r.setCell(int(a.X/64), int(a.Y/64))
|
||||||
r.a = a
|
r.a = a
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add1 adds a linear segment to the current curve.
|
// 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
|
x0, y0 := r.a.X, r.a.Y
|
||||||
x1, y1 := b.X, b.Y
|
x1, y1 := b.X, b.Y
|
||||||
dx, dy := x1-x0, y1-y0
|
dx, dy := x1-x0, y1-y0
|
||||||
// Break the 24.8 fixed point Y co-ordinates into integral and fractional parts.
|
// Break the 26.6 fixed point Y co-ordinates into integral and fractional
|
||||||
y0i := int(y0) / 256
|
// parts.
|
||||||
y0f := y0 - Fix32(256*y0i)
|
y0i := int(y0) / 64
|
||||||
y1i := int(y1) / 256
|
y0f := y0 - fixed.Int26_6(64*y0i)
|
||||||
y1f := y1 - Fix32(256*y1i)
|
y1i := int(y1) / 64
|
||||||
|
y1f := y1 - fixed.Int26_6(64*y1i)
|
||||||
|
|
||||||
if y0i == y1i {
|
if y0i == y1i {
|
||||||
// There is only one scanline.
|
// 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
|
// This is a vertical line segment. We avoid calling r.scan and instead
|
||||||
// manipulate r.area and r.cover directly.
|
// manipulate r.area and r.cover directly.
|
||||||
var (
|
var (
|
||||||
edge0, edge1 Fix32
|
edge0, edge1 fixed.Int26_6
|
||||||
yiDelta int
|
yiDelta int
|
||||||
)
|
)
|
||||||
if dy > 0 {
|
if dy > 0 {
|
||||||
edge0, edge1, yiDelta = 0, 256, 1
|
edge0, edge1, yiDelta = 0, 64, 1
|
||||||
} else {
|
} else {
|
||||||
edge0, edge1, yiDelta = 256, 0, -1
|
edge0, edge1, yiDelta = 64, 0, -1
|
||||||
}
|
}
|
||||||
x0i, yi := int(x0)/256, y0i
|
x0i, yi := int(x0)/64, y0i
|
||||||
x0fTimes2 := (int(x0) - (256 * x0i)) * 2
|
x0fTimes2 := (int(x0) - (64 * x0i)) * 2
|
||||||
// Do the first pixel.
|
// Do the first pixel.
|
||||||
dcover := int(edge1 - y0f)
|
dcover := int(edge1 - y0f)
|
||||||
darea := int(x0fTimes2 * dcover)
|
darea := int(x0fTimes2 * dcover)
|
||||||
|
@ -246,19 +249,19 @@ func (r *Rasterizer) Add1(b Point) {
|
||||||
r.cover += dcover
|
r.cover += dcover
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// There are at least two scanlines. Apart from the first and last scanlines,
|
// There are at least two scanlines. Apart from the first and last
|
||||||
// all intermediate scanlines go through the full height of the row, or 256
|
// scanlines, all intermediate scanlines go through the full height of
|
||||||
// units in 24.8 fixed point format.
|
// the row, or 64 units in 26.6 fixed point format.
|
||||||
var (
|
var (
|
||||||
p, q, edge0, edge1 Fix32
|
p, q, edge0, edge1 fixed.Int26_6
|
||||||
yiDelta int
|
yiDelta int
|
||||||
)
|
)
|
||||||
if dy > 0 {
|
if dy > 0 {
|
||||||
p, q = (256-y0f)*dx, dy
|
p, q = (64-y0f)*dx, dy
|
||||||
edge0, edge1, yiDelta = 0, 256, 1
|
edge0, edge1, yiDelta = 0, 64, 1
|
||||||
} else {
|
} else {
|
||||||
p, q = y0f*dx, -dy
|
p, q = y0f*dx, -dy
|
||||||
edge0, edge1, yiDelta = 256, 0, -1
|
edge0, edge1, yiDelta = 64, 0, -1
|
||||||
}
|
}
|
||||||
xDelta, xRem := p/q, p%q
|
xDelta, xRem := p/q, p%q
|
||||||
if xRem < 0 {
|
if xRem < 0 {
|
||||||
|
@ -269,10 +272,10 @@ func (r *Rasterizer) Add1(b Point) {
|
||||||
x, yi := x0, y0i
|
x, yi := x0, y0i
|
||||||
r.scan(yi, x, y0f, x+xDelta, edge1)
|
r.scan(yi, x, y0f, x+xDelta, edge1)
|
||||||
x, yi = x+xDelta, yi+yiDelta
|
x, yi = x+xDelta, yi+yiDelta
|
||||||
r.setCell(int(x)/256, yi)
|
r.setCell(int(x)/64, yi)
|
||||||
if yi != y1i {
|
if yi != y1i {
|
||||||
// Do all the intermediate scanlines.
|
// Do all the intermediate scanlines.
|
||||||
p = 256 * dx
|
p = 64 * dx
|
||||||
fullDelta, fullRem := p/q, p%q
|
fullDelta, fullRem := p/q, p%q
|
||||||
if fullRem < 0 {
|
if fullRem < 0 {
|
||||||
fullDelta -= 1
|
fullDelta -= 1
|
||||||
|
@ -288,7 +291,7 @@ func (r *Rasterizer) Add1(b Point) {
|
||||||
}
|
}
|
||||||
r.scan(yi, x, edge0, x+xDelta, edge1)
|
r.scan(yi, x, edge0, x+xDelta, edge1)
|
||||||
x, yi = x+xDelta, yi+yiDelta
|
x, yi = x+xDelta, yi+yiDelta
|
||||||
r.setCell(int(x)/256, yi)
|
r.setCell(int(x)/64, yi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Do the last scanline.
|
// Do the last scanline.
|
||||||
|
@ -299,10 +302,11 @@ func (r *Rasterizer) Add1(b Point) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add2 adds a quadratic segment to the current curve.
|
// Add2 adds a quadratic segment to the current curve.
|
||||||
func (r *Rasterizer) Add2(b, c Point) {
|
func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
|
||||||
// Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
|
// Calculate nSplit (the number of recursive decompositions) based on how
|
||||||
// Specifically, how much the middle point b deviates from (a+c)/2.
|
// `curvy' it is. Specifically, how much the middle point b deviates from
|
||||||
dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / Fix32(r.splitScale2)
|
// (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
|
nsplit := 0
|
||||||
for dev > 0 {
|
for dev > 0 {
|
||||||
dev /= 4
|
dev /= 4
|
||||||
|
@ -315,7 +319,7 @@ func (r *Rasterizer) Add2(b, c Point) {
|
||||||
}
|
}
|
||||||
// Recursively decompose the curve nSplit levels deep.
|
// Recursively decompose the curve nSplit levels deep.
|
||||||
var (
|
var (
|
||||||
pStack [2*maxNsplit + 3]Point
|
pStack [2*maxNsplit + 3]fixed.Point26_6
|
||||||
sStack [maxNsplit + 1]int
|
sStack [maxNsplit + 1]int
|
||||||
i 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.
|
// Replace the level-0 quadratic with a two-linear-piece approximation.
|
||||||
midx := (p[0].X + 2*p[1].X + p[2].X) / 4
|
midx := (p[0].X + 2*p[1].X + p[2].X) / 4
|
||||||
midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 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])
|
r.Add1(p[0])
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
|
@ -355,10 +359,10 @@ func (r *Rasterizer) Add2(b, c Point) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add3 adds a cubic segment to the current curve.
|
// 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.
|
// 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)
|
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) / Fix32(r.splitScale3)
|
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
|
nsplit := 0
|
||||||
for dev2 > 0 || dev3 > 0 {
|
for dev2 > 0 || dev3 > 0 {
|
||||||
dev2 /= 8
|
dev2 /= 8
|
||||||
|
@ -372,7 +376,7 @@ func (r *Rasterizer) Add3(b, c, d Point) {
|
||||||
}
|
}
|
||||||
// Recursively decompose the curve nSplit levels deep.
|
// Recursively decompose the curve nSplit levels deep.
|
||||||
var (
|
var (
|
||||||
pStack [3*maxNsplit + 4]Point
|
pStack [3*maxNsplit + 4]fixed.Point26_6
|
||||||
sStack [maxNsplit + 1]int
|
sStack [maxNsplit + 1]int
|
||||||
i 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.
|
// 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
|
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
|
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])
|
r.Add1(p[0])
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
|
@ -425,16 +429,27 @@ func (r *Rasterizer) AddPath(p Path) {
|
||||||
for i := 0; i < len(p); {
|
for i := 0; i < len(p); {
|
||||||
switch p[i] {
|
switch p[i] {
|
||||||
case 0:
|
case 0:
|
||||||
r.Start(Point{p[i+1], p[i+2]})
|
r.Start(
|
||||||
|
fixed.Point26_6{p[i+1], p[i+2]},
|
||||||
|
)
|
||||||
i += 4
|
i += 4
|
||||||
case 1:
|
case 1:
|
||||||
r.Add1(Point{p[i+1], p[i+2]})
|
r.Add1(
|
||||||
|
fixed.Point26_6{p[i+1], p[i+2]},
|
||||||
|
)
|
||||||
i += 4
|
i += 4
|
||||||
case 2:
|
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
|
i += 6
|
||||||
case 3:
|
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
|
i += 8
|
||||||
default:
|
default:
|
||||||
panic("freetype/raster: bad path")
|
panic("freetype/raster: bad path")
|
||||||
|
@ -443,43 +458,44 @@ func (r *Rasterizer) AddPath(p Path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddStroke adds a stroked 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)
|
Stroke(r, q, width, cr, jr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts an area value to a uint32 alpha value. A completely filled pixel
|
// areaToAlpha converts an area value to a uint32 alpha value. A completely
|
||||||
// corresponds to an area of 256*256*2, and an alpha of 1<<32-1. The
|
// 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:
|
// conversion of area values greater than this depends on the winding rule:
|
||||||
// even-odd or non-zero.
|
// even-odd or non-zero.
|
||||||
func (r *Rasterizer) areaToAlpha(area int) uint32 {
|
func (r *Rasterizer) areaToAlpha(area int) uint32 {
|
||||||
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1" without
|
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
|
||||||
// the +1. Round-to-nearest gives a more symmetric result than round-down.
|
// without the +1. Round-to-nearest gives a more symmetric result than
|
||||||
// The C implementation also returns 8-bit alpha, not 32-bit alpha.
|
// round-down. The C implementation also returns 8-bit alpha, not 32-bit
|
||||||
|
// alpha.
|
||||||
a := (area + 1) >> 1
|
a := (area + 1) >> 1
|
||||||
if a < 0 {
|
if a < 0 {
|
||||||
a = -a
|
a = -a
|
||||||
}
|
}
|
||||||
alpha := uint32(a)
|
alpha := uint32(a)
|
||||||
if r.UseNonZeroWinding {
|
if r.UseNonZeroWinding {
|
||||||
if alpha > 0xffff {
|
if alpha > 0x0fff {
|
||||||
alpha = 0xffff
|
alpha = 0x0fff
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alpha &= 0x1ffff
|
alpha &= 0x1fff
|
||||||
if alpha > 0x10000 {
|
if alpha > 0x1000 {
|
||||||
alpha = 0x20000 - alpha
|
alpha = 0x2000 - alpha
|
||||||
} else if alpha == 0x10000 {
|
} else if alpha == 0x1000 {
|
||||||
alpha = 0x0ffff
|
alpha = 0x0fff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
alpha |= alpha << 16
|
alpha >>= 4
|
||||||
return alpha
|
return alpha * 0x01010101
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rasterize converts r's accumulated curves into Spans for p. The Spans
|
// Rasterize converts r's accumulated curves into Spans for p. The Spans passed
|
||||||
// passed to p are non-overlapping, and sorted by Y and then X. They all
|
// to p are non-overlapping, and sorted by Y and then X. They all have non-zero
|
||||||
// have non-zero width (and 0 <= X0 < X1 <= r.width) and non-zero A, except
|
// width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
|
||||||
// for the final Span, which has Y, X0, X1 and A all equal to zero.
|
// Span, which has Y, X0, X1 and A all equal to zero.
|
||||||
func (r *Rasterizer) Rasterize(p Painter) {
|
func (r *Rasterizer) Rasterize(p Painter) {
|
||||||
r.saveCell()
|
r.saveCell()
|
||||||
s := 0
|
s := 0
|
||||||
|
@ -487,7 +503,7 @@ func (r *Rasterizer) Rasterize(p Painter) {
|
||||||
xi, cover := 0, 0
|
xi, cover := 0, 0
|
||||||
for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
|
for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
|
||||||
if cover != 0 && r.cell[c].xi > xi {
|
if cover != 0 && r.cell[c].xi > xi {
|
||||||
alpha := r.areaToAlpha(cover * 256 * 2)
|
alpha := r.areaToAlpha(cover * 64 * 2)
|
||||||
if alpha != 0 {
|
if alpha != 0 {
|
||||||
xi0, xi1 := xi, r.cell[c].xi
|
xi0, xi1 := xi, r.cell[c].xi
|
||||||
if xi0 < 0 {
|
if xi0 < 0 {
|
||||||
|
@ -503,7 +519,7 @@ func (r *Rasterizer) Rasterize(p Painter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cover += r.cell[c].cover
|
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
|
xi = r.cell[c].xi + 1
|
||||||
if alpha != 0 {
|
if alpha != 0 {
|
||||||
xi0, xi1 := r.cell[c].xi, xi
|
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.
|
// Clear cancels any previous calls to r.Start or r.AddXxx.
|
||||||
func (r *Rasterizer) Clear() {
|
func (r *Rasterizer) Clear() {
|
||||||
r.a = Point{}
|
r.a = fixed.Point26_6{}
|
||||||
r.xi = 0
|
r.xi = 0
|
||||||
r.yi = 0
|
r.yi = 0
|
||||||
r.area = 0
|
r.area = 0
|
||||||
|
@ -541,7 +557,7 @@ func (r *Rasterizer) Clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBounds sets the maximum width and height of the rasterized image and
|
// 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) {
|
func (r *Rasterizer) SetBounds(width, height int) {
|
||||||
if width < 0 {
|
if width < 0 {
|
||||||
width = 0
|
width = 0
|
||||||
|
@ -549,10 +565,9 @@ func (r *Rasterizer) SetBounds(width, height int) {
|
||||||
if height < 0 {
|
if height < 0 {
|
||||||
height = 0
|
height = 0
|
||||||
}
|
}
|
||||||
// Use the same ssN heuristic as the C Freetype implementation.
|
// Use the same ssN heuristic as the C Freetype (version 2.4.0)
|
||||||
// The C implementation uses the values 32, 16, but those are in
|
// implementation.
|
||||||
// 26.6 fixed point units, and we use 24.8 fixed point everywhere.
|
ss2, ss3 := 32, 16
|
||||||
ss2, ss3 := 128, 64
|
|
||||||
if width > 24 || height > 24 {
|
if width > 24 || height > 24 {
|
||||||
ss2, ss3 = 2*ss2, 2*ss3
|
ss2, ss3 = 2*ss2, 2*ss3
|
||||||
if width > 120 || height > 120 {
|
if width > 120 || height > 120 {
|
||||||
|
|
103
raster/stroke.go
103
raster/stroke.go
|
@ -5,21 +5,25 @@
|
||||||
|
|
||||||
package raster
|
package raster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
// Two points are considered practically equal if the square of the distance
|
// 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).
|
// between them is less than one quarter (i.e. 1024 / 4096).
|
||||||
const epsilon = 16384
|
const epsilon = fixed.Int52_12(1024)
|
||||||
|
|
||||||
// A Capper signifies how to begin or end a stroked path.
|
// A Capper signifies how to begin or end a stroked path.
|
||||||
type Capper interface {
|
type Capper interface {
|
||||||
// Cap adds a cap to p given a pivot point and the normal vector of a
|
// 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.
|
// 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.
|
// 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)
|
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
|
// 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.
|
// point and the normal vectors of the trailing and leading segments.
|
||||||
// Both normals have length equal to half of the stroke width.
|
// 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.
|
// 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)
|
f(lhs, rhs, halfWidth, pivot, n0, n1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundCapper adds round caps to a stroked path.
|
// RoundCapper adds round caps to a stroked path.
|
||||||
var RoundCapper Capper = CapperFunc(roundCapper)
|
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
|
// The cubic Bézier approximation to a circle involves the magic number
|
||||||
// (√2 - 1) * 4/3, which is approximately 141/256.
|
// (√2 - 1) * 4/3, which is approximately 141/256.
|
||||||
const k = 141
|
const k = 141
|
||||||
|
@ -56,14 +60,14 @@ func roundCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
|
||||||
// ButtCapper adds butt caps to a stroked path.
|
// ButtCapper adds butt caps to a stroked path.
|
||||||
var ButtCapper Capper = CapperFunc(buttCapper)
|
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))
|
p.Add1(pivot.Add(n1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SquareCapper adds square caps to a stroked path.
|
// SquareCapper adds square caps to a stroked path.
|
||||||
var SquareCapper Capper = CapperFunc(squareCapper)
|
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)
|
e := pRot90CCW(n1)
|
||||||
side := pivot.Add(e)
|
side := pivot.Add(e)
|
||||||
p.Add1(side.Sub(n1))
|
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.
|
// RoundJoiner adds round joins to a stroked path.
|
||||||
var RoundJoiner Joiner = JoinerFunc(roundJoiner)
|
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)
|
dot := pDot(pRot90CW(n0), n1)
|
||||||
if dot >= 0 {
|
if dot >= 0 {
|
||||||
addArc(lhs, pivot, n0, n1)
|
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.
|
// BevelJoiner adds bevel joins to a stroked path.
|
||||||
var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
|
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))
|
lhs.Add1(pivot.Add(n1))
|
||||||
rhs.Add1(pivot.Sub(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
|
// 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
|
// the two possible arcs is taken, i.e. the one spanning <= 180 degrees. The
|
||||||
// two vectors n0 and n1 must be of equal length.
|
// 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 is the square of the length of n0.
|
||||||
r2 := pDot(n0, n0)
|
r2 := pDot(n0, n0)
|
||||||
if r2 < epsilon {
|
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,
|
// control points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled,
|
||||||
// rotated and translated. tan(π/8) is approximately 106/256.
|
// rotated and translated. tan(π/8) is approximately 106/256.
|
||||||
const tpo8 = 106
|
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
|
// 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
|
// dot products. m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135
|
||||||
// degrees.
|
// 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
|
// 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.
|
// between 0 and 45 degrees then d ranges between 256/256 and 181/256.
|
||||||
d := 256 * pDot(s, n1) / r2
|
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))
|
p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// midpoint returns the midpoint of two Points.
|
// midpoint returns the midpoint of two Points.
|
||||||
func midpoint(a, b Point) Point {
|
func midpoint(a, b fixed.Point26_6) fixed.Point26_6 {
|
||||||
return Point{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
|
return fixed.Point26_6{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
|
||||||
}
|
}
|
||||||
|
|
||||||
// angleGreaterThan45 returns whether the angle between two vectors is more
|
// angleGreaterThan45 returns whether the angle between two vectors is more
|
||||||
// than 45 degrees.
|
// than 45 degrees.
|
||||||
func angleGreaterThan45(v0, v1 Point) bool {
|
func angleGreaterThan45(v0, v1 fixed.Point26_6) bool {
|
||||||
v := pRot45CCW(v0)
|
v := pRot45CCW(v0)
|
||||||
return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0
|
return pDot(v, v1) < 0 || pDot(pRot90CW(v), v1) < 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// interpolate returns the point (1-t)*a + t*b.
|
// interpolate returns the point (1-t)*a + t*b.
|
||||||
func interpolate(a, b Point, t Fix64) Point {
|
func interpolate(a, b fixed.Point26_6, t fixed.Int52_12) fixed.Point26_6 {
|
||||||
s := 1<<16 - t
|
s := 1<<12 - t
|
||||||
x := s*Fix64(a.X) + t*Fix64(b.X)
|
x := s*fixed.Int52_12(a.X) + t*fixed.Int52_12(b.X)
|
||||||
y := s*Fix64(a.Y) + t*Fix64(b.Y)
|
y := s*fixed.Int52_12(a.Y) + t*fixed.Int52_12(b.Y)
|
||||||
return Point{Fix32(x >> 16), Fix32(y >> 16)}
|
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
|
// 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
|
// (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.
|
// 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).
|
// 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)
|
dx := int64(b.X - a.X)
|
||||||
dy := int64(b.Y - a.Y)
|
dy := int64(b.Y - a.Y)
|
||||||
ex := int64(c.X - 2*b.X + a.X)
|
ex := int64(c.X - 2*b.X + a.X)
|
||||||
ey := int64(c.Y - 2*b.Y + a.Y)
|
ey := int64(c.Y - 2*b.Y + a.Y)
|
||||||
if ex == 0 && ey == 0 {
|
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.
|
// 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 is the destination that records the stroked path.
|
||||||
p Adder
|
p Adder
|
||||||
// u is the half-width of the stroke.
|
// 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 and jr specify how to end and connect path segments.
|
||||||
cr Capper
|
cr Capper
|
||||||
jr Joiner
|
jr Joiner
|
||||||
|
@ -242,19 +246,19 @@ type stroker struct {
|
||||||
r Path
|
r Path
|
||||||
// a is the most recent segment point. anorm is the segment normal of
|
// a is the most recent segment point. anorm is the segment normal of
|
||||||
// length u at that point.
|
// length u at that point.
|
||||||
a, anorm Point
|
a, anorm fixed.Point26_6
|
||||||
}
|
}
|
||||||
|
|
||||||
// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
|
// 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.
|
// 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
|
// We repeatedly divide the segment at its middle until it is straight
|
||||||
// enough to approximate the stroke by just translating the control points.
|
// 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.
|
// ds and ps are stacks of depths and points. t is the top of the stack.
|
||||||
const maxDepth = 5
|
const maxDepth = 5
|
||||||
var (
|
var (
|
||||||
ds [maxDepth + 1]int
|
ds [maxDepth + 1]int
|
||||||
ps [2*maxDepth + 3]Point
|
ps [2*maxDepth + 3]fixed.Point26_6
|
||||||
t int
|
t int
|
||||||
)
|
)
|
||||||
// Initially the ps stack has one quadratic segment of depth zero.
|
// 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[1] = b
|
||||||
ps[0] = c
|
ps[0] = c
|
||||||
anorm := k.anorm
|
anorm := k.anorm
|
||||||
var cnorm Point
|
var cnorm fixed.Point26_6
|
||||||
|
|
||||||
for {
|
for {
|
||||||
depth := ds[t]
|
depth := ds[t]
|
||||||
|
@ -272,8 +276,8 @@ func (k *stroker) addNonCurvy2(b, c Point) {
|
||||||
c := ps[2*t+0]
|
c := ps[2*t+0]
|
||||||
ab := b.Sub(a)
|
ab := b.Sub(a)
|
||||||
bc := c.Sub(b)
|
bc := c.Sub(b)
|
||||||
abIsSmall := pDot(ab, ab) < Fix64(1<<16)
|
abIsSmall := pDot(ab, ab) < fixed.Int52_12(1<<12)
|
||||||
bcIsSmall := pDot(bc, bc) < Fix64(1<<16)
|
bcIsSmall := pDot(bc, bc) < fixed.Int52_12(1<<12)
|
||||||
if abIsSmall && bcIsSmall {
|
if abIsSmall && bcIsSmall {
|
||||||
// Approximate the segment by a circular arc.
|
// Approximate the segment by a circular arc.
|
||||||
cnorm = pRot90CCW(pNorm(bc, k.u))
|
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.
|
// 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))
|
bnorm := pRot90CCW(pNorm(b.Sub(k.a), k.u))
|
||||||
if len(k.r) == 0 {
|
if len(k.r) == 0 {
|
||||||
k.p.Start(k.a.Add(bnorm))
|
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.
|
// 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)
|
ab := b.Sub(k.a)
|
||||||
bc := c.Sub(b)
|
bc := c.Sub(b)
|
||||||
abnorm := pRot90CCW(pNorm(ab, k.u))
|
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.
|
// 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.
|
// If this occurs at an end point, we process the segment as a whole.
|
||||||
t := curviest2(k.a, b, c)
|
t := curviest2(k.a, b, c)
|
||||||
if t <= 0 || 65536 <= t {
|
if t <= 0 || 4096 <= t {
|
||||||
k.addNonCurvy2(b, c)
|
k.addNonCurvy2(b, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -364,7 +368,7 @@ func (k *stroker) Add2(b, c Point) {
|
||||||
// then the decomposition can become unstable, so we approximate the
|
// then the decomposition can become unstable, so we approximate the
|
||||||
// quadratic segment by two linear segments joined by an arc.
|
// quadratic segment by two linear segments joined by an arc.
|
||||||
bcnorm := pRot90CCW(pNorm(bc, k.u))
|
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
|
pArc := pDot(abnorm, bc) < 0
|
||||||
|
|
||||||
k.p.Add1(mabc.Add(abnorm))
|
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.
|
// 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")
|
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,
|
// path is accumulated in k.r. Once we've finished adding the LHS to k.p,
|
||||||
// we add the RHS in reverse order.
|
// we add the RHS in reverse order.
|
||||||
k.r = make(Path, 0, len(q))
|
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); {
|
for i := 4; i < len(q); {
|
||||||
switch q[i] {
|
switch q[i] {
|
||||||
case 1:
|
case 1:
|
||||||
k.Add1(Point{q[i+1], q[i+2]})
|
k.Add1(
|
||||||
|
fixed.Point26_6{q[i+1], q[i+2]},
|
||||||
|
)
|
||||||
i += 4
|
i += 4
|
||||||
case 2:
|
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
|
i += 6
|
||||||
case 3:
|
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
|
i += 8
|
||||||
default:
|
default:
|
||||||
panic("freetype/raster: bad path")
|
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))
|
k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm))
|
||||||
addPathReversed(k.p, k.r)
|
addPathReversed(k.p, k.r)
|
||||||
pivot := q.firstPoint()
|
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
|
// Stroke adds q stroked with the given width to p. The result is typically
|
||||||
// self-intersecting and should be rasterized with UseNonZeroWinding.
|
// self-intersecting and should be rasterized with UseNonZeroWinding.
|
||||||
// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
|
// 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 {
|
if len(q) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue