From 8a4428169df5c9ec333fbda61dca6b830cb47d66 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Sun, 30 Aug 2015 23:48:43 +1000 Subject: [PATCH] Make raster.Painter's max alpha 1<<16-1, not 1<<32-1. This is more consistent with the standard library's image/color package. The benchmarks didn't seem to change significantly. --- raster/paint.go | 75 ++++++++++++++++++++++-------------------------- raster/raster.go | 9 +++--- truetype/face.go | 2 +- 3 files changed, 41 insertions(+), 45 deletions(-) diff --git a/raster/paint.go b/raster/paint.go index 13cccc1..185d36a 100644 --- a/raster/paint.go +++ b/raster/paint.go @@ -13,17 +13,17 @@ import ( ) // A Span is a horizontal segment of pixels with constant alpha. X0 is an -// inclusive bound and X1 is exclusive, the same as for slices. A fully -// opaque Span has A == 1<<32 - 1. +// inclusive bound and X1 is exclusive, the same as for slices. A fully opaque +// Span has Alpha == 0xffff. type Span struct { Y, X0, X1 int - A uint32 + Alpha uint32 } // A Painter knows how to paint a batch of Spans. Rasterization may involve -// Painting multiple batches, and done will be true for the final batch. -// The Spans' Y values are monotonically increasing during a rasterization. -// Paint may use all of ss as scratch space during the call. +// Painting multiple batches, and done will be true for the final batch. The +// Spans' Y values are monotonically increasing during a rasterization. Paint +// may use all of ss as scratch space during the call. type Painter interface { Paint(ss []Span, done bool) } @@ -34,13 +34,13 @@ type PainterFunc func(ss []Span, done bool) // Paint just delegates the call to f. func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) } -// An AlphaOverPainter is a Painter that paints Spans onto an image.Alpha -// using the Over Porter-Duff composition operator. +// An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using +// the Over Porter-Duff composition operator. type AlphaOverPainter struct { Image *image.Alpha } -// Paint satisfies the Painter interface by painting ss onto an image.Alpha. +// Paint satisfies the Painter interface. func (r AlphaOverPainter) Paint(ss []Span, done bool) { b := r.Image.Bounds() for _, s := range ss { @@ -61,7 +61,7 @@ func (r AlphaOverPainter) Paint(ss []Span, done bool) { } base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X p := r.Image.Pix[base+s.X0 : base+s.X1] - a := int(s.A >> 24) + a := int(s.Alpha >> 8) for i, c := range p { v := int(c) p[i] = uint8((v*255 + (255-v)*a) / 255) @@ -74,13 +74,13 @@ func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter { return AlphaOverPainter{m} } -// An AlphaSrcPainter is a Painter that paints Spans onto an image.Alpha -// using the Src Porter-Duff composition operator. +// An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using +// the Src Porter-Duff composition operator. type AlphaSrcPainter struct { Image *image.Alpha } -// Paint satisfies the Painter interface by painting ss onto an image.Alpha. +// Paint satisfies the Painter interface. func (r AlphaSrcPainter) Paint(ss []Span, done bool) { b := r.Image.Bounds() for _, s := range ss { @@ -101,7 +101,7 @@ func (r AlphaSrcPainter) Paint(ss []Span, done bool) { } base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X p := r.Image.Pix[base+s.X0 : base+s.X1] - color := uint8(s.A >> 24) + color := uint8(s.Alpha >> 8) for i := range p { p[i] = color } @@ -113,16 +113,17 @@ func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter { return AlphaSrcPainter{m} } +// An RGBAPainter is a Painter that paints Spans onto a *image.RGBA. type RGBAPainter struct { - // The image to compose onto. + // Image is the image to compose onto. Image *image.RGBA - // The Porter-Duff composition operator. + // Op is the Porter-Duff composition operator. Op draw.Op - // The 16-bit color to paint the spans. + // cr, cg, cb and ca are 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. +// Paint satisfies the Painter interface. func (r *RGBAPainter) Paint(ss []Span, done bool) { b := r.Image.Bounds() for _, s := range ss { @@ -141,8 +142,8 @@ func (r *RGBAPainter) Paint(ss []Span, done bool) { if s.X0 >= s.X1 { continue } - // This code is similar to drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go. - ma := s.A >> 16 + // This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go. + ma := s.Alpha const m = 1<<16 - 1 i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4 i1 := i0 + (s.X1-s.X0)*4 @@ -192,7 +193,7 @@ func (m *MonochromePainter) Paint(ss []Span, done bool) { // We compact the ss slice, discarding any Spans whose alpha quantizes to zero. j := 0 for _, s := range ss { - if s.A >= 1<<31 { + if s.Alpha >= 0x8000 { if m.y == s.Y && m.x1 == s.X0 { m.x1 = s.X1 } else { @@ -237,33 +238,28 @@ func NewMonochromePainter(p Painter) *MonochromePainter { // A GammaCorrectionPainter wraps another Painter, performing gamma-correction // on each Span's alpha value. type GammaCorrectionPainter struct { - // The wrapped Painter. + // Painter is the wrapped Painter. Painter Painter - // Precomputed alpha values for linear interpolation, with fully opaque == 1<<16-1. + // a is the precomputed alpha values for linear interpolation, with fully + // opaque == 0xffff. a [256]uint16 - // Whether gamma correction is a no-op. + // gammaIsOne is whether gamma correction is a no-op. gammaIsOne bool } -// Paint delegates to the wrapped Painter after performing gamma-correction -// on each Span. +// Paint delegates to the wrapped Painter after performing gamma-correction on +// each Span. func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) { if !g.gammaIsOne { - const ( - M = 0x1010101 // 255*M == 1<<32-1 - N = 0x8080 // N = M>>9, and N < 1<<16-1 - ) + const n = 0x101 for i, s := range ss { - if s.A == 0 || s.A == 1<<32-1 { + if s.Alpha == 0 || s.Alpha == 0xffff { continue } - p, q := s.A/M, (s.A%M)>>9 + p, q := s.Alpha/n, s.Alpha%n // 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 - ss[i].A = a + a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q + ss[i].Alpha = (a + n/2) / n } } g.Painter.Paint(ss, done) @@ -271,11 +267,10 @@ func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) { // SetGamma sets the gamma value. func (g *GammaCorrectionPainter) SetGamma(gamma float64) { - if gamma == 1.0 { - g.gammaIsOne = true + g.gammaIsOne = gamma == 1 + if g.gammaIsOne { return } - g.gammaIsOne = false for i := 0; i < 256; i++ { a := float64(i) / 0xff a = math.Pow(a, gamma) diff --git a/raster/raster.go b/raster/raster.go index e7ee464..7e6cd4e 100644 --- a/raster/raster.go +++ b/raster/raster.go @@ -469,13 +469,13 @@ func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner } // areaToAlpha converts an area value to a uint32 alpha value. A completely -// filled pixel corresponds to an area of 64*64*2, and an alpha of 1<<32-1. The +// filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The // conversion of area values greater than this depends on the winding rule: // even-odd or non-zero. func (r *Rasterizer) areaToAlpha(area int) uint32 { // The C Freetype implementation (version 2.3.12) does "alpha := area>>1" // without the +1. Round-to-nearest gives a more symmetric result than - // round-down. The C implementation also returns 8-bit alpha, not 32-bit + // round-down. The C implementation also returns 8-bit alpha, not 16-bit // alpha. a := (area + 1) >> 1 if a < 0 { @@ -494,8 +494,9 @@ func (r *Rasterizer) areaToAlpha(area int) uint32 { alpha = 0x0fff } } - alpha >>= 4 - return alpha * 0x01010101 + // alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to + // 16-bit alpha. + return alpha<<4 | alpha>>8 } // Rasterize converts r's accumulated curves into Spans for p. The Spans passed diff --git a/truetype/face.go b/truetype/face.go index 3f2ce2f..7730b9b 100644 --- a/truetype/face.go +++ b/truetype/face.go @@ -468,7 +468,7 @@ func (p facePainter) Paint(ss []raster.Span, done bool) { } base := (s.Y-m.Rect.Min.Y)*m.Stride - m.Rect.Min.X p := m.Pix[base+s.X0 : base+s.X1] - color := uint8(s.A >> 24) + color := uint8(s.Alpha >> 8) for i := range p { p[i] = color }