Freetype-Go: new freetype package to provide a convenience API to

draw text onto an image.

This is a simple API that doesn't handle line breaking, ligatures,
right-to-left or vertical scripts, and other fancy features.

R=r, rsc
CC=golang-dev
http://codereview.appspot.com/1121045
This commit is contained in:
Nigel Tao 2010-05-14 13:29:53 +10:00
parent 7b02573579
commit 25c38cfec1
9 changed files with 568 additions and 75 deletions

127
example/freetype/main.go Normal file
View file

@ -0,0 +1,127 @@
package main
import (
"bufio"
"exp/draw"
"flag"
"fmt"
"freetype-go.googlecode.com/hg/freetype"
"image"
"image/png"
"io/ioutil"
"log"
"os"
)
var (
dpi = flag.Int("dpi", 72, "screen resolution in Dots Per Inch")
fontfile = flag.String("fontfile", "../../luxi-fonts/luxisr.ttf", "filename of the ttf font")
size = flag.Float("size", 12, "font size in points")
spacing = flag.Float("spacing", 1.5, "line spacing (e.g. 2 means double spaced)")
wonb = flag.Bool("whiteonblack", false, "white text on a black background")
)
var text = []string{
"Twas brillig, and the slithy toves",
"Did gyre and gimble in the wabe;",
"All mimsy were the borogoves,",
"And the mome raths outgrabe.",
"",
"“Beware the Jabberwock, my son!",
"The jaws that bite, the claws that catch!",
"Beware the Jubjub bird, and shun",
"The frumious Bandersnatch!”",
"",
"He took his vorpal sword in hand:",
"Long time the manxome foe he sought—",
"So rested he by the Tumtum tree,",
"And stood awhile in thought.",
"",
"And as in uffish thought he stood,",
"The Jabberwock, with eyes of flame,",
"Came whiffling through the tulgey wood,",
"And burbled as it came!",
"",
"One, two! One, two! and through and through",
"The vorpal blade went snicker-snack!",
"He left it dead, and with its head",
"He went galumphing back.",
"",
"“And hast thou slain the Jabberwock?",
"Come to my arms, my beamish boy!",
"O frabjous day! Callooh! Callay!”",
"He chortled in his joy.",
"",
"Twas brillig, and the slithy toves",
"Did gyre and gimble in the wabe;",
"All mimsy were the borogoves,",
"And the mome raths outgrabe.",
}
func main() {
flag.Parse()
// Read the font data.
fontBytes, err := ioutil.ReadFile(*fontfile)
if err != nil {
log.Stderr(err)
return
}
font, err := freetype.ParseFont(fontBytes)
if err != nil {
log.Stderr(err)
return
}
// Initialize the context.
fg, bg := image.Black, image.White
ruler := image.RGBAColor{0xdd, 0xdd, 0xdd, 0xff}
if *wonb {
fg, bg = image.White, image.Black
ruler = image.RGBAColor{0x22, 0x22, 0x22, 0xff}
}
rgba := image.NewRGBA(640, 480)
draw.Draw(rgba, draw.Rect(0, 0, rgba.Width(), rgba.Height()), bg, draw.ZP)
c := freetype.NewRGBAContext(rgba)
c.SetColor(fg)
c.SetDPI(*dpi)
c.SetFont(font)
c.SetFontSize(*size)
// Draw the guidelines.
for i := 0; i < 200; i++ {
rgba.Set(10, 10+i, ruler)
rgba.Set(10+i, 10, ruler)
}
// Draw the text.
pt := freetype.Pt(10, 10)
for _, s := range text {
err = c.DrawText(pt, s)
if err != nil {
log.Stderr(err)
return
}
pt.Y += c.PointToFixed(*size * *spacing)
}
// Save that RGBA image to disk.
f, err := os.Open("out.png", os.O_CREAT|os.O_WRONLY, 0600)
if err != nil {
log.Stderr(err)
os.Exit(1)
}
defer f.Close()
b := bufio.NewWriter(f)
err = png.Encode(b, rgba)
if err != nil {
log.Stderr(err)
os.Exit(1)
}
err = b.Flush()
if err != nil {
log.Stderr(err)
os.Exit(1)
}
fmt.Println("Wrote out.png OK.")
}

View file

@ -31,7 +31,7 @@ func clear(m *image.Alpha) {
func main() {
// Draw a rounded corner that is one pixel wide.
r := raster.New(50, 50)
r := raster.NewRasterizer(50, 50)
r.Start(p(5, 5))
r.Add1(p(5, 25))
r.Add2(p(5, 45), p(25, 45))
@ -51,11 +51,12 @@ func main() {
draw.Draw(rgba, draw.Rect(0, 0, w, h/2), image.Black, draw.ZP)
draw.Draw(rgba, draw.Rect(0, h/2, w, h), image.White, draw.ZP)
mask := image.NewAlpha(50, 50)
painter := raster.AlphaSrcPainter(mask)
painter := raster.NewAlphaPainter(mask)
painter.Op = draw.Src
gammas := []float64{1.0 / 10.0, 1.0 / 3.0, 1.0 / 2.0, 2.0 / 3.0, 4.0 / 5.0, 1.0, 5.0 / 4.0, 3.0 / 2.0, 2.0, 3.0, 10.0}
for i, g := range gammas {
clear(mask)
r.Rasterize(raster.GammaCorrectionPainter(painter, g))
r.Rasterize(raster.NewGammaCorrectionPainter(painter, g))
x, y := 50*i+25, 25
draw.DrawMask(rgba, draw.Rect(x, y, x+50, y+50), image.White, draw.ZP, mask, draw.ZP, draw.Over)
y += 100

View file

@ -137,11 +137,13 @@ func main() {
w = 400
h = 400
)
r := raster.New(w, h)
r := raster.NewRasterizer(w, h)
contour(r, outside)
contour(r, inside)
mask := image.NewAlpha(w, h)
r.Rasterize(raster.AlphaSrcPainter(mask))
p := raster.NewAlphaPainter(mask)
p.Op = draw.Src
r.Rasterize(p)
// Draw the mask image (in gray) onto an RGBA image.
rgba := image.NewRGBA(w, h)

View file

@ -20,7 +20,7 @@ func printBounds(b truetype.Bounds) {
fmt.Printf("XMin:%d YMin:%d XMax:%d YMax:%d\n", b.XMin, b.YMin, b.XMax, b.YMax)
}
func printGlyph(g *truetype.Glyph) {
func printGlyph(g *truetype.GlyphBuf) {
printBounds(g.B)
fmt.Print("Points:\n---\n")
e := 0
@ -58,7 +58,7 @@ func main() {
i0 := font.Index(c0)
hm := font.HMetric(i0)
g := truetype.NewGlyph()
g := truetype.NewGlyphBuf()
err = g.Load(font, i0)
if err != nil {
log.Stderr(err)

13
freetype/Makefile Normal file
View file

@ -0,0 +1,13 @@
# Copyright 2010 The Freetype-Go Authors. All rights reserved.
# Use of this source code is governed by your choice of either the
# FreeType License or the GNU General Public License version 2,
# both of which can be found in the LICENSE file.
include $(GOROOT)/src/Make.$(GOARCH)
TARG=freetype-go.googlecode.com/hg/freetype
GOFILES=\
freetype.go\
include $(GOROOT)/src/Make.pkg

248
freetype/freetype.go Normal file
View file

@ -0,0 +1,248 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2,
// both of which can be found in the LICENSE file.
// The freetype package provides a convenient API to draw text onto an image.
// Use the freetype/raster and freetype/truetype packages for lower level
// control over rasterization and TrueType parsing.
package freetype
import (
"freetype-go.googlecode.com/hg/freetype/raster"
"freetype-go.googlecode.com/hg/freetype/truetype"
"image"
"os"
)
// ParseFont just calls the Parse function from the freetype/truetype package.
// It is provided here so that code that imports this package doesn't need
// to also include the freetype/truetype package.
func ParseFont(b []byte) (*truetype.Font, os.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.Fixed units.
func Pt(x, y int) raster.Point {
return raster.Point{raster.Fixed(x << 8), raster.Fixed(y << 8)}
}
// An RGBAContext holds the state for drawing text from a given font at a
// given size.
type RGBAContext struct {
r *raster.Rasterizer
p *raster.RGBAPainter
font *truetype.Font
glyphBuf *truetype.GlyphBuf
fontSize float
dpi int
upe int
// 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.
xmin, ymin int
// scale is a multiplication factor to convert 256 FUnits (which is truetype's
// native unit) to 24.8 fixed point units (which is the rasterizer's native unit).
// At the default values of 72 DPI and 2048 units-per-em, one em of a 12 point
// font is 12 pixels, which is 3072 fixed point units, and scale is
// (pointSize * resolution * 256 * 256) / (unitsPerEm * 72), or
// (12 * 72 * 256 * 256) / (2048 * 72),
// which equals 384 fixed point units per 256 FUnits.
// To check this, 1 em * 2048 FUnits per em * 384 fixed point units per 256 FUnits
// equals 3072 fixed point units.
scale int
}
// FUnitToFixed converts the given number of FUnits into fixed point units,
// rounding to nearest.
func (c *RGBAContext) FUnitToFixed(x int) raster.Fixed {
return raster.Fixed((x*c.scale + 128) >> 8)
}
// FUnitToPixelRD converts the given number of FUnits into pixel units,
// rounding down.
func (c *RGBAContext) FUnitToPixelRD(x int) int {
return x * c.scale >> 16
}
// FUnitToPixelRU converts the given number of FUnits into pixel units,
// rounding up.
func (c *RGBAContext) FUnitToPixelRU(x int) int {
return (x*c.scale + 0xffff) >> 16
}
// PointToFixed converts the given number of points (as in ``a 12 point font'')
// into fixed point units.
func (c *RGBAContext) PointToFixed(x float) raster.Fixed {
return raster.Fixed(x * float(c.dpi) * (256.0 / 72.0))
}
// drawContour draws the given closed contour with the given offset.
func (c *RGBAContext) drawContour(ps []truetype.Point, dx, dy raster.Fixed) {
if len(ps) == 0 {
return
}
// 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{
dx + c.FUnitToFixed(int(ps[0].X)),
dy + c.FUnitToFixed(c.upe-int(ps[0].Y)),
}
c.r.Start(start)
q0, on0 := start, true
for _, p := range ps[1:] {
q := raster.Point{
dx + c.FUnitToFixed(int(p.X)),
dy + c.FUnitToFixed(c.upe-int(p.Y)),
}
on := p.Flags&0x01 != 0
if on {
if on0 {
c.r.Add1(q)
} else {
c.r.Add2(q0, q)
}
} else {
if on0 {
// No-op.
} else {
mid := raster.Point{
(q0.X + q.X) / 2,
(q0.Y + q.Y) / 2,
}
c.r.Add2(q0, mid)
}
}
q0, on0 = q, on
}
// Close the curve.
if on0 {
c.r.Add1(start)
} else {
c.r.Add2(q0, start)
}
}
// DrawText draws s at pt. The text is placed so that the top left of the em
// square of the first character of s is equal to pt. The majority of the
// affected pixels will be below and to the right of pt, but some may be above
// or to the left. For example, drawing a string that starts with a 'J' in an
// italic font may affect pixels to the left of pt.
// pt is a raster.Point and can therefore represent sub-pixel positions.
func (c *RGBAContext) DrawText(pt raster.Point, s string) (err os.Error) {
if c.font == nil {
return os.NewError("freetype: DrawText called with a nil font")
}
// pt.X, pt.Y, x, y, dx, dy and x0 are measured in raster.Fixed units,
// c.p.Dx, c.p.Dy, c.xmin and c.ymin are measured in pixels, and
// advance is measured in FUnits.
var x, y raster.Fixed
advance, x0 := 0, pt.X
dx := raster.Fixed(-c.xmin << 8)
dy := raster.Fixed(-c.ymin << 8)
c.p.Dy, y = c.ymin+int(pt.Y>>8), pt.Y&0xff
y += dy
prev, hasPrev := truetype.Index(0), false
for _, ch := range s {
index := c.font.Index(ch)
// Load the next glyph (if it was different from the previous one)
// and add any kerning adjustment.
if hasPrev {
advance += int(c.font.Kerning(prev, index))
if prev != index {
err = c.glyphBuf.Load(c.font, index)
if err != nil {
return
}
}
} else {
err = c.glyphBuf.Load(c.font, index)
if err != nil {
return
}
}
// Convert the advance from FUnits to raster.Fixed units.
x = x0 + c.FUnitToFixed(advance)
// Break the co-ordinate down into an integer pixel part and a
// sub-pixel part, making sure that the latter is non-negative.
c.p.Dx, x = c.xmin+int(x>>8), x&0xff
x += dx
// Draw the contours.
c.r.Clear()
e0 := 0
for _, e := range c.glyphBuf.End {
c.drawContour(c.glyphBuf.Point[e0:e], x, y)
e0 = e
}
c.r.Rasterize(c.p)
// Advance the cursor.
advance += int(c.font.HMetric(index).AdvanceWidth)
prev, hasPrev = index, true
}
return
}
// recalc recalculates scale and bounds values from the font size, screen
// resolution and font metrics.
func (c *RGBAContext) recalc() {
c.scale = int((c.fontSize * float(c.dpi) * 256 * 256) / (float(c.upe) * 72))
if c.font == nil {
c.xmin, c.ymin = 0, 0
} else {
b := c.font.Bounds()
c.xmin = c.FUnitToPixelRD(int(b.XMin))
c.ymin = c.FUnitToPixelRD(c.upe - int(b.YMax))
xmax := c.FUnitToPixelRU(int(b.XMax))
ymax := c.FUnitToPixelRU(c.upe - int(b.YMin))
c.r.SetBounds(xmax-c.xmin, ymax-c.ymin)
}
}
// SetColor sets the color to draw text.
func (c *RGBAContext) SetColor(color image.Color) {
c.p.SetColor(color)
}
// SetDPI sets the screen resolution in dots per inch.
func (c *RGBAContext) SetDPI(dpi int) {
c.dpi = dpi
c.recalc()
}
// SetFont sets the font used to draw text.
func (c *RGBAContext) SetFont(font *truetype.Font) {
c.font = font
c.upe = font.UnitsPerEm()
if c.upe <= 0 {
c.upe = 1
}
c.recalc()
}
// SetFontSize sets the font size in points (as in ``a 12 point font'').
func (c *RGBAContext) SetFontSize(fontSize float) {
c.fontSize = fontSize
c.recalc()
}
// SetRGBA sets the image that the RGBAContext draws onto.
func (c *RGBAContext) SetRGBA(m *image.RGBA) {
c.p.Image = m
}
// NewRGBAContext creates a new RGBAContext.
func NewRGBAContext(m *image.RGBA) *RGBAContext {
return &RGBAContext{
r: raster.NewRasterizer(0, 0),
p: raster.NewRGBAPainter(m),
glyphBuf: truetype.NewGlyphBuf(),
fontSize: 12,
dpi: 72,
upe: 2048,
scale: (12 * 72 * 256 * 256) / (2048 * 72),
}
}

View file

@ -6,6 +6,7 @@
package raster
import (
"exp/draw"
"image"
"math"
)
@ -33,46 +34,138 @@ type PainterFunc func(ss []Span)
// Paint just delegates the call to f.
func (f PainterFunc) Paint(ss []Span) { f(ss) }
// AlphaOverPainter returns a Painter that paints onto the given Alpha image
// using the "src over dst" Porter-Duff composition operator.
func AlphaOverPainter(m *image.Alpha) Painter {
return PainterFunc(func(ss []Span) {
for _, s := range ss {
// An AlphaPainter is a Painter that paints Spans onto an image.Alpha.
type AlphaPainter struct {
// The image to compose onto.
Image *image.Alpha
// The Porter-Duff composition operator.
Op draw.Op
// An offset (in pixels) to the painted spans.
Dx, Dy int
}
// Paint satisfies the Painter interface by painting ss onto an image.Alpha.
func (r *AlphaPainter) Paint(ss []Span) {
for _, s := range ss {
y := r.Dy + s.Y
if y < 0 {
continue
}
if y >= len(r.Image.Pixel) {
return
}
p := r.Image.Pixel[y]
x0, x1 := r.Dx+s.X0, r.Dx+s.X1
if x0 < 0 {
x0 = 0
}
if x1 > len(p) {
x1 = len(p)
}
if r.Op == draw.Over {
a := int(s.A >> 24)
p := m.Pixel[s.Y]
for i := s.X0; i < s.X1; i++ {
ai := int(p[i].A)
ai = (ai*255 + (255-ai)*a) / 255
p[i] = image.AlphaColor{uint8(ai)}
for x := x0; x < x1; x++ {
ax := int(p[x].A)
ax = (ax*255 + (255-ax)*a) / 255
p[x] = image.AlphaColor{uint8(ax)}
}
}
})
}
// AlphaSrcPainter returns a Painter that paints onto the given Alpha image
// using the "src" Porter-Duff composition operator.
func AlphaSrcPainter(m *image.Alpha) Painter {
return PainterFunc(func(ss []Span) {
for _, s := range ss {
} else {
color := image.AlphaColor{uint8(s.A >> 24)}
p := m.Pixel[s.Y]
for i := s.X0; i < s.X1; i++ {
p[i] = color
for x := x0; x < x1; x++ {
p[x] = color
}
}
})
}
}
// A monochromePainter has a wrapped painter and an accumulator for merging
// adjacent opaque Spans.
type monochromePainter struct {
p Painter
// NewAlphaPainter creates a new AlphaPainter for the given image.
func NewAlphaPainter(m *image.Alpha) *AlphaPainter {
return &AlphaPainter{Image: m}
}
type RGBAPainter struct {
// The image to compose onto.
Image *image.RGBA
// The Porter-Duff composition operator.
Op draw.Op
// An offset (in pixels) to the painted spans.
Dx, Dy int
// The 16-bit color to paint the spans.
cr, cg, cb, ca uint32
}
// Paint satisfies the Painter interface by painting ss onto an image.RGBA.
func (r *RGBAPainter) Paint(ss []Span) {
for _, s := range ss {
y := r.Dy + s.Y
if y < 0 {
continue
}
if y >= len(r.Image.Pixel) {
return
}
p := r.Image.Pixel[y]
x0, x1 := r.Dx+s.X0, r.Dx+s.X1
if x0 < 0 {
x0 = 0
}
if x1 > len(p) {
x1 = len(p)
}
for x := x0; x < x1; x++ {
// This code is duplicated from drawGlyphOver in $GOROOT/src/pkg/exp/draw/draw.go.
// TODO(nigeltao): Factor out common code into a utility function, once the compiler
// can inline such function calls.
ma := s.A >> 16
const M = 1<<16 - 1
if r.Op == draw.Over {
rgba := p[x]
dr := uint32(rgba.R)
dg := uint32(rgba.G)
db := uint32(rgba.B)
da := uint32(rgba.A)
a := M - (r.ca * ma / M)
a *= 0x101
dr = (dr*a + r.cr*ma) / M
dg = (dg*a + r.cg*ma) / M
db = (db*a + r.cb*ma) / M
da = (da*a + r.ca*ma) / M
p[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
} else {
dr := r.cr * ma / M
dg := r.cg * ma / M
db := r.cb * ma / M
da := r.ca * ma / M
p[x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
}
}
}
}
// SetColor sets the color to paint the spans.
func (r *RGBAPainter) SetColor(c image.Color) {
r.cr, r.cg, r.cb, r.ca = c.RGBA()
r.cr >>= 16
r.cg >>= 16
r.cb >>= 16
r.ca >>= 16
}
// NewRGBAPainter creates a new RGBAPainter for the given image.
func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
return &RGBAPainter{Image: m}
}
// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
// be either fully opaque or fully transparent.
type MonochromePainter struct {
Painter Painter
y, x0, x1 int
}
// Paint delegates to the wrapped Painter after quantizing each Span's alpha
// values and merging adjacent fully opaque Spans.
func (m *monochromePainter) Paint(ss []Span) {
// value and merging adjacent fully opaque Spans.
func (m *MonochromePainter) Paint(ss []Span) {
// We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
j := 0
for _, s := range ss {
@ -93,11 +186,11 @@ func (m *monochromePainter) Paint(ss []Span) {
if j < len(ss) {
ss[j] = Span{}
j++
m.p.Paint(ss[0:j])
m.Painter.Paint(ss[0:j])
} else if j == len(ss) {
m.p.Paint(ss)
m.Painter.Paint(ss)
ss[0] = Span{}
m.p.Paint(ss[0:1])
m.Painter.Paint(ss[0:1])
} else {
panic("unreachable")
}
@ -106,25 +199,27 @@ func (m *monochromePainter) Paint(ss []Span) {
return
}
}
m.p.Paint(ss[0:j])
m.Painter.Paint(ss[0:j])
}
// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
// be either fully opaque or fully transparent.
func MonochromePainter(p Painter) Painter {
return &monochromePainter{p: p}
// NewMonochromePainter creates a new MonochromePainter that wraps the given
// Painter.
func NewMonochromePainter(p Painter) *MonochromePainter {
return &MonochromePainter{Painter: p}
}
// A gammaCorrectionPainter has a wrapped painter and a precomputed linear
// interpolation of the exponential gamma-correction curve.
type gammaCorrectionPainter struct {
p Painter
a [256]uint16 // Alpha values, with fully opaque == 1<<16-1.
// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
// on each Span's alpha value.
type GammaCorrectionPainter struct {
// The wrapped Painter.
Painter Painter
// Precomputed alpha values for linear interpolation, with fully opaque == 1<<16-1.
a [256]uint16
}
// Paint delegates to the wrapped Painter after performing gamma-correction
// on each Span.
func (g *gammaCorrectionPainter) Paint(ss []Span) {
func (g *GammaCorrectionPainter) Paint(ss []Span) {
const (
M = 0x1010101 // 255*M == 1<<32-1
N = 0x8080 // N = M>>9, and N < 1<<16-1
@ -145,17 +240,22 @@ func (g *gammaCorrectionPainter) Paint(ss []Span) {
}
ss[i].A = a
}
g.p.Paint(ss)
g.Painter.Paint(ss)
}
// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
// on the alpha values of each Span.
func GammaCorrectionPainter(p Painter, gamma float64) Painter {
g := &gammaCorrectionPainter{p: p}
// SetGamma sets the gamma value.
func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
for i := 0; i < 256; i++ {
a := float64(i) / 0xff
a = math.Pow(a, gamma)
g.a[i] = uint16(0xffff * a)
}
}
// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
// the given Painter.
func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
g := &GammaCorrectionPainter{Painter: p}
g.SetGamma(gamma)
return g
}

View file

@ -52,7 +52,7 @@ type Point struct {
X, Y Fixed
}
// A cell is part of a linked list (for a given yi coordinate) of accumulated
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
// area/coverage for the pixel at (xi, yi).
type cell struct {
xi int
@ -546,15 +546,15 @@ func (r *Rasterizer) Clear() {
}
}
// Creates a new Rasterizer with the given bounds.
func New(width, height int) *Rasterizer {
// SetBounds sets the maximum width and height of the rasterized image and
// calls Clear. The width and height are in pixels, not Fixed units.
func (r *Rasterizer) SetBounds(width, height int) {
if width < 0 {
width = 0
}
if height < 0 {
height = 0
}
// Use the same ssN heuristic as the C Freetype 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.
@ -565,8 +565,6 @@ func New(width, height int) *Rasterizer {
ss2, ss3 = 2*ss2, 2*ss3
}
}
r := new(Rasterizer)
r.width = width
r.splitScale2 = ss2
r.splitScale3 = ss3
@ -576,8 +574,12 @@ func New(width, height int) *Rasterizer {
} else {
r.cellIndex = r.cellIndexBuf[0:height]
}
for i := 0; i < len(r.cellIndex); i++ {
r.cellIndex[i] = -1
}
r.Clear()
}
// NewRasterizer creates a new Rasterizer with the given bounds.
func NewRasterizer(width, height int) *Rasterizer {
r := new(Rasterizer)
r.SetBounds(width, height)
return r
}

View file

@ -418,9 +418,9 @@ type Point struct {
Flags uint8
}
// A Glyph holds a glyph's contours. A Glyph can be re-used to load a series
// of glyphs from a Font.
type Glyph struct {
// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
// series of glyphs from a Font.
type GlyphBuf struct {
// The glyph's bounding box.
B Bounds
// Point contains all Points from all contours of the glyph.
@ -433,7 +433,7 @@ type Glyph struct {
// decodeFlags decodes a glyph's run-length encoded flags,
// and returns the remaining data.
func (g *Glyph) decodeFlags(d data) data {
func (g *GlyphBuf) decodeFlags(d data) data {
for i := 0; i < len(g.Point); {
c := d.u8()
g.Point[i].Flags = c
@ -450,7 +450,7 @@ func (g *Glyph) decodeFlags(d data) data {
}
// decodeCoords decodes a glyph's delta encoded co-ordinates.
func (g *Glyph) decodeCoords(d data) {
func (g *GlyphBuf) decodeCoords(d data) {
var x int16
for i := 0; i < len(g.Point); i++ {
f := g.Point[i].Flags
@ -484,9 +484,9 @@ func (g *Glyph) decodeCoords(d data) {
}
// Load loads a glyph's contours from a Font, overwriting any previously
// loaded contours for this Glyph.
func (g *Glyph) Load(f *Font, i Index) os.Error {
// Reset the Glyph.
// loaded contours for this GlyphBuf.
func (g *GlyphBuf) Load(f *Font, i Index) os.Error {
// Reset the GlyphBuf.
g.B = Bounds{}
g.Point = g.Point[0:0]
g.End = g.End[0:0]
@ -537,9 +537,9 @@ func (g *Glyph) Load(f *Font, i Index) os.Error {
return nil
}
// NewGlyph returns a newly allocated Glyph.
func NewGlyph() *Glyph {
g := new(Glyph)
// NewGlyphBuf returns a newly allocated GlyphBuf.
func NewGlyphBuf() *GlyphBuf {
g := new(GlyphBuf)
g.Point = make([]Point, 0, 256)
g.End = make([]int, 0, 32)
return g