paint.go 7.4 KB
Newer Older
1 2
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
3 4
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
5 6 7 8 9

package raster

import (
	"image"
N
Nigel Tao 已提交
10
	"image/color"
11
	"image/draw"
12 13 14 15
	"math"
)

// A Span is a horizontal segment of pixels with constant alpha. X0 is an
16 17
// inclusive bound and X1 is exclusive, the same as for slices. A fully opaque
// Span has Alpha == 0xffff.
18 19
type Span struct {
	Y, X0, X1 int
20
	Alpha     uint32
21 22
}

23
// A Painter knows how to paint a batch of Spans. Rasterization may involve
24 25 26
// 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.
27
type Painter interface {
28
	Paint(ss []Span, done bool)
29 30 31
}

// The PainterFunc type adapts an ordinary function to the Painter interface.
32
type PainterFunc func(ss []Span, done bool)
33 34

// Paint just delegates the call to f.
35
func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
36

37 38
// An AlphaOverPainter is a Painter that paints Spans onto a *image.Alpha using
// the Over Porter-Duff composition operator.
39
type AlphaOverPainter struct {
40 41 42
	Image *image.Alpha
}

43
// Paint satisfies the Painter interface.
44
func (r AlphaOverPainter) Paint(ss []Span, done bool) {
45
	b := r.Image.Bounds()
46
	for _, s := range ss {
47
		if s.Y < b.Min.Y {
48 49
			continue
		}
50
		if s.Y >= b.Max.Y {
51 52
			return
		}
53 54
		if s.X0 < b.Min.X {
			s.X0 = b.Min.X
55
		}
56 57
		if s.X1 > b.Max.X {
			s.X1 = b.Max.X
58
		}
59 60 61
		if s.X0 >= s.X1 {
			continue
		}
62
		base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
63
		p := r.Image.Pix[base+s.X0 : base+s.X1]
64
		a := int(s.Alpha >> 8)
65
		for i, c := range p {
66 67
			v := int(c)
			p[i] = uint8((v*255 + (255-v)*a) / 255)
68
		}
69
	}
70 71
}

72 73 74 75 76
// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
	return AlphaOverPainter{m}
}

77 78
// An AlphaSrcPainter is a Painter that paints Spans onto a *image.Alpha using
// the Src Porter-Duff composition operator.
79 80 81 82
type AlphaSrcPainter struct {
	Image *image.Alpha
}

83
// Paint satisfies the Painter interface.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
	b := r.Image.Bounds()
	for _, s := range ss {
		if s.Y < b.Min.Y {
			continue
		}
		if s.Y >= b.Max.Y {
			return
		}
		if s.X0 < b.Min.X {
			s.X0 = b.Min.X
		}
		if s.X1 > b.Max.X {
			s.X1 = b.Max.X
		}
99 100 101
		if s.X0 >= s.X1 {
			continue
		}
102
		base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
103
		p := r.Image.Pix[base+s.X0 : base+s.X1]
104
		color := uint8(s.Alpha >> 8)
105 106 107 108
		for i := range p {
			p[i] = color
		}
	}
109 110
}

111 112 113 114 115
// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
	return AlphaSrcPainter{m}
}

116
// An RGBAPainter is a Painter that paints Spans onto a *image.RGBA.
117
type RGBAPainter struct {
118
	// Image is the image to compose onto.
119
	Image *image.RGBA
120
	// Op is the Porter-Duff composition operator.
121
	Op draw.Op
122
	// cr, cg, cb and ca are the 16-bit color to paint the spans.
123 124 125
	cr, cg, cb, ca uint32
}

126
// Paint satisfies the Painter interface.
127
func (r *RGBAPainter) Paint(ss []Span, done bool) {
128
	b := r.Image.Bounds()
129
	for _, s := range ss {
130
		if s.Y < b.Min.Y {
131 132
			continue
		}
133
		if s.Y >= b.Max.Y {
134 135
			return
		}
136 137
		if s.X0 < b.Min.X {
			s.X0 = b.Min.X
138
		}
139 140
		if s.X1 > b.Max.X {
			s.X1 = b.Max.X
141
		}
142 143 144
		if s.X0 >= s.X1 {
			continue
		}
145 146
		// This code mimics drawGlyphOver in $GOROOT/src/image/draw/draw.go.
		ma := s.Alpha
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
		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
		if r.Op == draw.Over {
			for i := i0; i < i1; i += 4 {
				dr := uint32(r.Image.Pix[i+0])
				dg := uint32(r.Image.Pix[i+1])
				db := uint32(r.Image.Pix[i+2])
				da := uint32(r.Image.Pix[i+3])
				a := (m - (r.ca * ma / m)) * 0x101
				r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
				r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
				r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
				r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
			}
		} else {
			for i := i0; i < i1; i += 4 {
				r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
				r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
				r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
				r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
168 169
			}
		}
170
	}
171 172
}

173
// SetColor sets the color to paint the spans.
N
Nigel Tao 已提交
174
func (r *RGBAPainter) SetColor(c color.Color) {
175 176 177 178 179 180 181 182 183 184 185 186
	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
187 188 189 190
	y, x0, x1 int
}

// Paint delegates to the wrapped Painter after quantizing each Span's alpha
191
// value and merging adjacent fully opaque Spans.
192
func (m *MonochromePainter) Paint(ss []Span, done bool) {
193 194 195
	// We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
	j := 0
	for _, s := range ss {
196
		if s.Alpha >= 0x8000 {
197 198 199 200 201 202 203
			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
			}
204 205 206 207 208 209 210
		}
	}
	if done {
		// Flush the accumulated Span.
		finalSpan := Span{m.y, m.x0, m.x1, 1<<32 - 1}
		if j < len(ss) {
			ss[j] = finalSpan
211
			j++
212
			m.Painter.Paint(ss[:j], true)
213 214 215
		} else if j == len(ss) {
			m.Painter.Paint(ss, false)
			if cap(ss) > 0 {
216
				ss = ss[:1]
217
			} else {
218
				ss = make([]Span, 1)
219
			}
220 221 222 223
			ss[0] = finalSpan
			m.Painter.Paint(ss, true)
		} else {
			panic("unreachable")
224
		}
225 226 227
		// Reset the accumulator, so that this Painter can be re-used.
		m.y, m.x0, m.x1 = 0, 0, 0
	} else {
228
		m.Painter.Paint(ss[:j], false)
229 230 231
	}
}

232 233 234 235
// NewMonochromePainter creates a new MonochromePainter that wraps the given
// Painter.
func NewMonochromePainter(p Painter) *MonochromePainter {
	return &MonochromePainter{Painter: p}
236 237
}

238 239 240
// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
// on each Span's alpha value.
type GammaCorrectionPainter struct {
241
	// Painter is the wrapped Painter.
242
	Painter Painter
243 244
	// a is the precomputed alpha values for linear interpolation, with fully
	// opaque == 0xffff.
245
	a [256]uint16
246
	// gammaIsOne is whether gamma correction is a no-op.
247
	gammaIsOne bool
248 249
}

250 251
// Paint delegates to the wrapped Painter after performing gamma-correction on
// each Span.
252
func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
253
	if !g.gammaIsOne {
254
		const n = 0x101
255
		for i, s := range ss {
256
			if s.Alpha == 0 || s.Alpha == 0xffff {
257 258
				continue
			}
259
			p, q := s.Alpha/n, s.Alpha%n
260
			// The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
261 262
			a := uint32(g.a[p])*(n-q) + uint32(g.a[p+1])*q
			ss[i].Alpha = (a + n/2) / n
263 264
		}
	}
265
	g.Painter.Paint(ss, done)
266 267
}

268
// SetGamma sets the gamma value.
N
Nigel Tao 已提交
269
func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
270 271
	g.gammaIsOne = gamma == 1
	if g.gammaIsOne {
272 273
		return
	}
274 275
	for i := 0; i < 256; i++ {
		a := float64(i) / 0xff
N
Nigel Tao 已提交
276
		a = math.Pow(a, gamma)
277 278
		g.a[i] = uint16(0xffff * a)
	}
279 280 281 282
}

// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
// the given Painter.
N
Nigel Tao 已提交
283
func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
284 285
	g := &GammaCorrectionPainter{Painter: p}
	g.SetGamma(gamma)
286 287
	return g
}