4aa7375072
Introduce MonochromePainter and GammaCorrectionPainter. R=r, rsc CC=golang-dev http://codereview.appspot.com/904041
162 lines
4.5 KiB
Go
162 lines
4.5 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 (
|
|
"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) }
|
|
|
|
// 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 {
|
|
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)}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 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 {
|
|
color := image.AlphaColor{uint8(s.A >> 24)}
|
|
p := m.Pixel[s.Y]
|
|
for i := s.X0; i < s.X1; i++ {
|
|
p[i] = color
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// A monochromePainter has a wrapped painter and an accumulator for merging
|
|
// adjacent opaque Spans.
|
|
type monochromePainter struct {
|
|
p 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) {
|
|
// 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.p.Paint(ss[0:j])
|
|
} else if j == len(ss) {
|
|
m.p.Paint(ss)
|
|
ss[0] = Span{}
|
|
m.p.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.p.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}
|
|
}
|
|
|
|
// 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.
|
|
}
|
|
|
|
// 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.p.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}
|
|
for i := 0; i < 256; i++ {
|
|
a := float64(i) / 0xff
|
|
a = math.Pow(a, gamma)
|
|
g.a[i] = uint16(0xffff * a)
|
|
}
|
|
return g
|
|
}
|