golang-freetype/freetype/raster/paint.go
Nigel Tao 25c38cfec1 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
2010-05-14 13:29:53 +10:00

262 lines
6.8 KiB
Go

// 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.
package raster
import (
"exp/draw"
"image"
"math"
)
// 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.
type Span struct {
Y, X0, X1 int
A uint32
}
// A Painter knows how to paint a batch of Spans. A Span's alpha is non-zero
// until the final Span of the rasterization, which is the zero value Span.
// A rasterization may involve Painting multiple batches, but the final zero
// value Span will occur only once per rasterization, not once per Paint call.
// Paint may use all of ss as scratch space during the call.
type Painter interface {
Paint(ss []Span)
}
// The PainterFunc type adapts an ordinary function to the Painter interface.
type PainterFunc func(ss []Span)
// Paint just delegates the call to f.
func (f PainterFunc) Paint(ss []Span) { f(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)
for x := x0; x < x1; x++ {
ax := int(p[x].A)
ax = (ax*255 + (255-ax)*a) / 255
p[x] = image.AlphaColor{uint8(ax)}
}
} else {
color := image.AlphaColor{uint8(s.A >> 24)}
for x := x0; x < x1; x++ {
p[x] = color
}
}
}
}
// 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
// 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 {
if s.A >= 1<<31 {
if m.y == s.Y && m.x1 == s.X0 {
m.x1 = s.X1
} else {
ss[j] = Span{m.y, m.x0, m.x1, 1<<32 - 1}
j++
m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
}
} else if s.A == 0 {
// The final Span of a rasterization is a zero value. We flush
// our accumulated Span and finish with a zero Span.
ss[j] = Span{m.y, m.x0, m.x1, 1<<32 - 1}
j++
if j < len(ss) {
ss[j] = Span{}
j++
m.Painter.Paint(ss[0:j])
} else if j == len(ss) {
m.Painter.Paint(ss)
ss[0] = Span{}
m.Painter.Paint(ss[0:1])
} else {
panic("unreachable")
}
// Reset the accumulator, so that this Painter can be re-used.
m.y, m.x0, m.x1 = 0, 0, 0
return
}
}
m.Painter.Paint(ss[0:j])
}
// NewMonochromePainter creates a new MonochromePainter that wraps the given
// Painter.
func NewMonochromePainter(p Painter) *MonochromePainter {
return &MonochromePainter{Painter: p}
}
// 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) {
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
}
g.Painter.Paint(ss)
}
// 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
}