diff --git a/README b/README index 39b3d82..a0d70bc 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ The Freetype font rasterizer in the Go programming language. To download and install from source: -$ go get github.com/golang/freetype +$ go get github.com/tdewolff/freetype It is an incomplete port: * It only supports TrueType fonts, and not Type 1 fonts nor bitmap fonts. diff --git a/example/capjoin/main.go b/example/capjoin/main.go index 71f3356..a349ee7 100644 --- a/example/capjoin/main.go +++ b/example/capjoin/main.go @@ -5,7 +5,7 @@ // +build example // -// This build tag means that "go install github.com/golang/freetype/..." +// This build tag means that "go install ..." // doesn't install this example program. Use "go run main.go" to run it or "go // install -tags=example" to install it. @@ -21,7 +21,7 @@ import ( "log" "os" - "github.com/golang/freetype/raster" + "github.com/goki/freetype/raster" "golang.org/x/image/math/fixed" ) diff --git a/example/drawer/main.go b/example/drawer/main.go index d26d066..cc78df0 100644 --- a/example/drawer/main.go +++ b/example/drawer/main.go @@ -5,7 +5,7 @@ // +build example // -// This build tag means that "go install github.com/golang/freetype/..." +// This build tag means that "go install ..." // doesn't install this example program. Use "go run main.go" to run it or "go // install -tags=example" to install it. @@ -24,7 +24,7 @@ import ( "math" "os" - "github.com/golang/freetype/truetype" + "github.com/goki/freetype/truetype" "golang.org/x/image/font" "golang.org/x/image/math/fixed" ) diff --git a/example/freetype/main.go b/example/freetype/main.go index dfbde9a..beb3022 100644 --- a/example/freetype/main.go +++ b/example/freetype/main.go @@ -5,7 +5,7 @@ // +build example // -// This build tag means that "go install github.com/golang/freetype/..." +// This build tag means that "go install ..." // doesn't install this example program. Use "go run main.go" to run it or "go // install -tags=example" to install it. @@ -23,7 +23,7 @@ import ( "log" "os" - "github.com/golang/freetype" + "github.com/goki/freetype" "golang.org/x/image/font" ) diff --git a/example/gamma/main.go b/example/gamma/main.go index cdd50bc..71c73c8 100644 --- a/example/gamma/main.go +++ b/example/gamma/main.go @@ -5,7 +5,7 @@ // +build example // -// This build tag means that "go install github.com/golang/freetype/..." +// This build tag means that "go install ..." // doesn't install this example program. Use "go run main.go" to run it or "go // install -tags=example" to install it. @@ -20,7 +20,7 @@ import ( "log" "os" - "github.com/golang/freetype/raster" + "github.com/goki/freetype/raster" "golang.org/x/image/math/fixed" ) diff --git a/example/genbasicfont/main.go b/example/genbasicfont/main.go index 5b2f2bc..4d59c64 100644 --- a/example/genbasicfont/main.go +++ b/example/genbasicfont/main.go @@ -5,7 +5,7 @@ // +build example // -// This build tag means that "go install github.com/golang/freetype/..." +// This build tag means that "go install ..." // doesn't install this example program. Use "go run main.go" to run it or "go // install -tags=example" to install it. @@ -26,7 +26,7 @@ import ( "strings" "unicode" - "github.com/golang/freetype/truetype" + "github.com/goki/freetype/truetype" "golang.org/x/image/font" "golang.org/x/image/math/fixed" ) diff --git a/example/raster/main.go b/example/raster/main.go index 3e572e1..e4f9f00 100644 --- a/example/raster/main.go +++ b/example/raster/main.go @@ -5,7 +5,7 @@ // +build example // -// This build tag means that "go install github.com/golang/freetype/..." +// This build tag means that "go install ..." // doesn't install this example program. Use "go run main.go" to run it or "go // install -tags=example" to install it. @@ -21,7 +21,7 @@ import ( "log" "os" - "github.com/golang/freetype/raster" + "github.com/goki/freetype/raster" "golang.org/x/image/math/fixed" ) diff --git a/example/round/main.go b/example/round/main.go index 2920e83..12b26f6 100644 --- a/example/round/main.go +++ b/example/round/main.go @@ -5,7 +5,7 @@ // +build example // -// This build tag means that "go install github.com/golang/freetype/..." +// This build tag means that "go install ..." // doesn't install this example program. Use "go run main.go" to run it or "go // install -tags=example" to install it. @@ -27,7 +27,7 @@ import ( "math" "os" - "github.com/golang/freetype/raster" + "github.com/goki/freetype/raster" "golang.org/x/image/math/fixed" ) diff --git a/example/truetype/main.go b/example/truetype/main.go index e7db2d0..b5c738d 100644 --- a/example/truetype/main.go +++ b/example/truetype/main.go @@ -5,7 +5,7 @@ // +build example // -// This build tag means that "go install github.com/golang/freetype/..." +// This build tag means that "go install ..." // doesn't install this example program. Use "go run main.go" to run it or "go // install -tags=example" to install it. @@ -17,7 +17,7 @@ import ( "io/ioutil" "log" - "github.com/golang/freetype/truetype" + "github.com/goki/freetype/truetype" "golang.org/x/image/font" "golang.org/x/image/math/fixed" ) diff --git a/raster/stroke.go b/raster/stroke.go index bcc66b2..84a62f9 100644 --- a/raster/stroke.go +++ b/raster/stroke.go @@ -438,12 +438,20 @@ func (k *stroker) stroke(q Path) { if len(k.r) == 0 { return } - // TODO(nigeltao): if q is a closed curve then we should join the first and - // last segments instead of capping them. - k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm)) + + closed := q.firstPoint() == q.lastPoint() + if !closed { + k.cr.Cap(k.p, k.u, q.lastPoint(), pNeg(k.anorm)) + } else { + pivot := q.firstPoint() + k.jr.Join(k.p, &k.r, k.u, pivot, k.anorm, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]})) + k.p.Start(fixed.Point26_6{k.r[len(k.r)-3], k.r[len(k.r)-2]}) // reverse path is now separate + } addPathReversed(k.p, k.r) - pivot := q.firstPoint() - k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]})) + if !closed { + pivot := q.firstPoint() + k.cr.Cap(k.p, k.u, pivot, pivot.Sub(fixed.Point26_6{k.r[1], k.r[2]})) + } } // Stroke adds q stroked with the given width to p. The result is typically diff --git a/truetype/face.go b/truetype/face.go index f8697d1..b6abe82 100644 --- a/truetype/face.go +++ b/truetype/face.go @@ -66,6 +66,11 @@ type Options struct { // // A zero value means to use 1 sub-pixel location. SubPixelsY int + + // Stroke is the number of pixels that the font glyphs are being stroked. + // + // A zero values means no stroke. + Stroke int } func (o *Options) size() float64 { @@ -182,6 +187,7 @@ func NewFace(f *Font, opts *Options) font.Face { hinting: opts.hinting(), scale: fixed.Int26_6(0.5 + (opts.size() * opts.dpi() * 64 / 72)), glyphCache: make([]glyphCacheEntry, opts.glyphCacheEntries()), + stroke: fixed.I(opts.Stroke * 2), } a.subPixelX, a.subPixelBiasX, a.subPixelMaskX = opts.subPixelsX() a.subPixelY, a.subPixelBiasY, a.subPixelMaskY = opts.subPixelsY() @@ -207,6 +213,10 @@ func NewFace(f *Font, opts *Options) font.Face { a.r.SetBounds(a.maxw, a.maxh) a.p = facePainter{a} + if a.stroke != 0 { + a.r.UseNonZeroWinding = true + } + return a } @@ -220,6 +230,7 @@ type face struct { subPixelY uint32 subPixelBiasY fixed.Int26_6 subPixelMaskY fixed.Int26_6 + stroke fixed.Int26_6 masks *image.Alpha glyphCache []glyphCacheEntry r raster.Rasterizer @@ -333,6 +344,10 @@ func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.In if xmin > xmax || ymin > ymax { return fixed.Rectangle26_6{}, 0, false } + xmin -= a.stroke + ymin -= a.stroke + xmax += a.stroke + ymax += a.stroke return fixed.Rectangle26_6{ Min: fixed.Point26_6{ X: xmin, @@ -376,6 +391,10 @@ func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v glyphCacheVal, ok if xmin > xmax || ymin > ymax { return glyphCacheVal{}, false } + xmin -= int(a.stroke) >> 6 + ymin -= int(a.stroke) >> 6 + xmax += int(a.stroke) >> 6 + ymax += int(a.stroke) >> 6 // 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 @@ -446,7 +465,8 @@ func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) { others = ps } } - a.r.Start(start) + path := raster.Path{} + path.Start(start) q0, on0 := start, true for _, p := range others { q := fixed.Point26_6{ @@ -456,9 +476,9 @@ func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) { on := p.Flags&0x01 != 0 if on { if on0 { - a.r.Add1(q) + path.Add1(q) } else { - a.r.Add2(q0, q) + path.Add2(q0, q) } } else { if on0 { @@ -468,16 +488,22 @@ func (a *face) drawContour(ps []Point, dx, dy fixed.Int26_6) { X: (q0.X + q.X) / 2, Y: (q0.Y + q.Y) / 2, } - a.r.Add2(q0, mid) + path.Add2(q0, mid) } } q0, on0 = q, on } // Close the curve. if on0 { - a.r.Add1(start) + path.Add1(start) } else { - a.r.Add2(q0, start) + path.Add2(q0, start) + } + + if a.stroke == 0 { + a.r.AddPath(path) + } else { + a.r.AddStroke(path, a.stroke, raster.ButtCapper, raster.RoundJoiner) } } diff --git a/truetype/truetype.go b/truetype/truetype.go index 2ae677a..50320fa 100644 --- a/truetype/truetype.go +++ b/truetype/truetype.go @@ -15,6 +15,7 @@ // // To measure a TrueType font in ideal FUnit space, use scale equal to // font.FUnitsPerEm(). + package truetype import (