From 25c38cfec1de05480d84e344c3005555c332e535 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Fri, 14 May 2010 13:29:53 +1000 Subject: [PATCH] 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 --- example/freetype/main.go | 127 +++++++++++++++++ example/gamma/main.go | 7 +- example/raster/main.go | 6 +- example/truetype/main.go | 4 +- freetype/Makefile | 13 ++ freetype/freetype.go | 248 ++++++++++++++++++++++++++++++++++ freetype/raster/paint.go | 196 ++++++++++++++++++++------- freetype/raster/raster.go | 20 +-- freetype/truetype/truetype.go | 22 +-- 9 files changed, 568 insertions(+), 75 deletions(-) create mode 100644 example/freetype/main.go create mode 100644 freetype/Makefile create mode 100644 freetype/freetype.go diff --git a/example/freetype/main.go b/example/freetype/main.go new file mode 100644 index 0000000..aabef20 --- /dev/null +++ b/example/freetype/main.go @@ -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.") +} diff --git a/example/gamma/main.go b/example/gamma/main.go index ba5c087..99c97af 100644 --- a/example/gamma/main.go +++ b/example/gamma/main.go @@ -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 diff --git a/example/raster/main.go b/example/raster/main.go index 6b73211..39344ac 100644 --- a/example/raster/main.go +++ b/example/raster/main.go @@ -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) diff --git a/example/truetype/main.go b/example/truetype/main.go index cd8ab56..53ddada 100644 --- a/example/truetype/main.go +++ b/example/truetype/main.go @@ -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) diff --git a/freetype/Makefile b/freetype/Makefile new file mode 100644 index 0000000..054cd3f --- /dev/null +++ b/freetype/Makefile @@ -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 + diff --git a/freetype/freetype.go b/freetype/freetype.go new file mode 100644 index 0000000..5a2d45b --- /dev/null +++ b/freetype/freetype.go @@ -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), + } +} diff --git a/freetype/raster/paint.go b/freetype/raster/paint.go index 4d31a10..5639a0b 100644 --- a/freetype/raster/paint.go +++ b/freetype/raster/paint.go @@ -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 } diff --git a/freetype/raster/raster.go b/freetype/raster/raster.go index 89f831c..f074652 100644 --- a/freetype/raster/raster.go +++ b/freetype/raster/raster.go @@ -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 } diff --git a/freetype/truetype/truetype.go b/freetype/truetype/truetype.go index 0d4d368..ecb3b30 100644 --- a/freetype/truetype/truetype.go +++ b/freetype/truetype/truetype.go @@ -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