golang-freetype/freetype/raster/paint.go
Nigel Tao 2af394dab9 freetype: clarify that the GPL licence is "version 2 or later", not
just version 2. The FTL (Freetype Licence) is still available. This
matches the original Freetype library (written in C).

This only expands and does not further restrict the licensing options
available to users of the freetype-go library.

R=adg
CC=golang-dev
http://codereview.appspot.com/1731049
2010-08-03 11:07:23 +10:00

258 lines
6.7 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 (or
// any later version), 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. 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.
type Painter interface {
Paint(ss []Span, done bool)
}
// The PainterFunc type adapts an ordinary function to the Painter interface.
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 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
}
// Paint satisfies the Painter interface by painting ss onto an image.Alpha.
func (r *AlphaPainter) Paint(ss []Span, done bool) {
for _, s := range ss {
if s.Y < 0 {
continue
}
if s.Y >= len(r.Image.Pixel) {
return
}
p := r.Image.Pixel[s.Y]
if s.X0 < 0 {
s.X0 = 0
}
if s.X1 > len(p) {
s.X1 = len(p)
}
if r.Op == draw.Over {
a := int(s.A >> 24)
for x := s.X0; x < s.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 := s.X0; x < s.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
// 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, done bool) {
for _, s := range ss {
if s.Y < 0 {
continue
}
if s.Y >= len(r.Image.Pixel) {
return
}
p := r.Image.Pixel[s.Y]
if s.X0 < 0 {
s.X0 = 0
}
if s.X1 > len(p) {
s.X1 = len(p)
}
for x := s.X0; x < s.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()
}
// 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, 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 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
}
}
}
if done {
// Flush the accumulated Span.
finalSpan := Span{m.y, m.x0, m.x1, 1<<32 - 1}
if j < len(ss) {
ss[j] = finalSpan
j++
m.Painter.Paint(ss[0:j], true)
} else if j == len(ss) {
m.Painter.Paint(ss, false)
if cap(ss) > 0 {
ss = ss[0:1]
} else {
ss = make([]Span, 1)
}
ss[0] = finalSpan
m.Painter.Paint(ss, true)
} else {
panic("unreachable")
}
// Reset the accumulator, so that this Painter can be re-used.
m.y, m.x0, m.x1 = 0, 0, 0
} else {
m.Painter.Paint(ss[0:j], false)
}
}
// 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
// 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, done bool) {
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
ss[i].A = a
}
}
g.Painter.Paint(ss, done)
}
// SetGamma sets the gamma value.
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, gamma64)
g.a[i] = uint16(0xffff * a)
}
}
// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
// the given Painter.
func NewGammaCorrectionPainter(p Painter, gamma float) *GammaCorrectionPainter {
g := &GammaCorrectionPainter{Painter: p}
g.SetGamma(gamma)
return g
}