diff --git a/example/freetype/main.go b/example/freetype/main.go index aabef20..cd7f60c 100644 --- a/example/freetype/main.go +++ b/example/freetype/main.go @@ -16,6 +16,7 @@ import ( 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") + gamma = flag.Float("gamma", 1.0, "gamma correction") 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") @@ -87,6 +88,7 @@ func main() { c.SetDPI(*dpi) c.SetFont(font) c.SetFontSize(*size) + c.SetGamma(*gamma) // Draw the guidelines. for i := 0; i < 200; i++ { diff --git a/example/gamma/main.go b/example/gamma/main.go index 99c97af..2d5e14d 100644 --- a/example/gamma/main.go +++ b/example/gamma/main.go @@ -53,7 +53,7 @@ func main() { mask := image.NewAlpha(50, 50) 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} + gammas := []float{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.NewGammaCorrectionPainter(painter, g)) diff --git a/freetype/freetype.go b/freetype/freetype.go index 5a2d45b..192e39b 100644 --- a/freetype/freetype.go +++ b/freetype/freetype.go @@ -32,7 +32,8 @@ func Pt(x, y int) raster.Point { // given size. type RGBAContext struct { r *raster.Rasterizer - p *raster.RGBAPainter + rp *raster.RGBAPainter + gp *raster.GammaCorrectionPainter font *truetype.Font glyphBuf *truetype.GlyphBuf fontSize float @@ -144,7 +145,7 @@ func (c *RGBAContext) DrawText(pt raster.Point, s string) (err os.Error) { 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 + c.rp.Dy, y = c.ymin+int(pt.Y>>8), pt.Y&0xff y += dy prev, hasPrev := truetype.Index(0), false for _, ch := range s { @@ -169,7 +170,7 @@ func (c *RGBAContext) DrawText(pt raster.Point, s string) (err os.Error) { 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 + c.rp.Dx, x = c.xmin+int(x>>8), x&0xff x += dx // Draw the contours. c.r.Clear() @@ -178,7 +179,7 @@ func (c *RGBAContext) DrawText(pt raster.Point, s string) (err os.Error) { c.drawContour(c.glyphBuf.Point[e0:e], x, y) e0 = e } - c.r.Rasterize(c.p) + c.r.Rasterize(c.gp) // Advance the cursor. advance += int(c.font.HMetric(index).AdvanceWidth) prev, hasPrev = index, true @@ -204,7 +205,7 @@ func (c *RGBAContext) recalc() { // SetColor sets the color to draw text. func (c *RGBAContext) SetColor(color image.Color) { - c.p.SetColor(color) + c.rp.SetColor(color) } // SetDPI sets the screen resolution in dots per inch. @@ -229,20 +230,27 @@ func (c *RGBAContext) SetFontSize(fontSize float) { c.recalc() } +// SetGamma sets the gamma correction parameter. +func (c *RGBAContext) SetGamma(g float) { + c.gp.SetGamma(g) +} + // SetRGBA sets the image that the RGBAContext draws onto. func (c *RGBAContext) SetRGBA(m *image.RGBA) { - c.p.Image = m + c.rp.Image = m } // NewRGBAContext creates a new RGBAContext. func NewRGBAContext(m *image.RGBA) *RGBAContext { - return &RGBAContext{ + c := &RGBAContext{ r: raster.NewRasterizer(0, 0), - p: raster.NewRGBAPainter(m), + rp: raster.NewRGBAPainter(m), glyphBuf: truetype.NewGlyphBuf(), fontSize: 12, dpi: 72, upe: 2048, scale: (12 * 72 * 256 * 256) / (2048 * 72), } + c.gp = raster.NewGammaCorrectionPainter(c.rp, 1.0) + return c } diff --git a/freetype/raster/paint.go b/freetype/raster/paint.go index 5639a0b..5d417c7 100644 --- a/freetype/raster/paint.go +++ b/freetype/raster/paint.go @@ -215,46 +215,56 @@ type GammaCorrectionPainter struct { Painter Painter // Precomputed alpha values for linear interpolation, with fully opaque == 1<<16-1. a [256]uint16 + // Whether gamma correction is a no-op. + gammaIsOne bool } // Paint delegates to the wrapped Painter after performing gamma-correction // on each 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 - ) - for i, _ := range ss { - if ss[i].A == 0 || ss[i].A == 1<<32-1 { - continue + if !g.gammaIsOne { + const ( + M = 0x1010101 // 255*M == 1<<32-1 + N = 0x8080 // N = M>>9, and N < 1<<16-1 + ) + for i, _ := range ss { + if ss[i].A == 0 || ss[i].A == 1<<32-1 { + continue + } + p, q := ss[i].A/M, (ss[i].A%M)>>9 + // The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1]. + a := uint32(g.a[p])*(N-q) + uint32(g.a[p+1])*q + a = (a + N/2) / N + // Convert the alpha from 16-bit (which is g.a's range) to 32-bit. + a |= a << 16 + // A non-final Span can't have zero alpha. + if a == 0 { + a = 1 + } + ss[i].A = a } - p, q := ss[i].A/M, (ss[i].A%M)>>9 - // The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1]. - a := uint32(g.a[p])*(N-q) + uint32(g.a[p+1])*q - a = (a + N/2) / N - // Convert the alpha from 16-bit (which is g.a's range) to 32-bit. - a |= a << 16 - // A non-final Span can't have zero alpha. - if a == 0 { - a = 1 - } - ss[i].A = a } g.Painter.Paint(ss) } // SetGamma sets the gamma value. -func (g *GammaCorrectionPainter) SetGamma(gamma float64) { +func (g *GammaCorrectionPainter) SetGamma(gamma float) { + if gamma == 1.0 { + g.gammaIsOne = true + return + } + g.gammaIsOne = false + gamma64 := float64(gamma) for i := 0; i < 256; i++ { a := float64(i) / 0xff - a = math.Pow(a, gamma) + a = math.Pow(a, gamma64) g.a[i] = uint16(0xffff * a) } } // NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps // the given Painter. -func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter { +func NewGammaCorrectionPainter(p Painter, gamma float) *GammaCorrectionPainter { g := &GammaCorrectionPainter{Painter: p} g.SetGamma(gamma) return g