raster.go 15.0 KB
Newer Older
N
Nigel Tao 已提交
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.
N
Nigel Tao 已提交
5

6
// Package raster provides an anti-aliasing 2-D rasterizer.
N
Nigel Tao 已提交
7
//
8 9 10
// It is part of the larger Freetype suite of font-related packages, but the
// raster package is not specific to font rasterization, and can be used
// standalone without any other Freetype package.
N
Nigel Tao 已提交
11
//
12 13 14
// Rasterization is done by the same area/coverage accumulation algorithm as
// the Freetype "smooth" module, and the Anti-Grain Geometry library. A
// description of the area/coverage algorithm is at
N
Nigel Tao 已提交
15
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
N
Nigel Tao 已提交
16
package raster // import "github.com/golang/freetype/raster"
N
Nigel Tao 已提交
17 18 19

import (
	"strconv"
20 21

	"golang.org/x/image/math/fixed"
N
Nigel Tao 已提交
22 23
)

24
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
N
Nigel Tao 已提交
25 26 27 28 29 30 31 32 33 34 35
// area/coverage for the pixel at (xi, yi).
type cell struct {
	xi          int
	area, cover int
	next        int
}

type Rasterizer struct {
	// If false, the default behavior is to use the even-odd winding fill
	// rule during Rasterize.
	UseNonZeroWinding bool
36 37
	// An offset (in pixels) to the painted spans.
	Dx, Dy int
N
Nigel Tao 已提交
38 39 40

	// The width of the Rasterizer. The height is implicit in len(cellIndex).
	width int
41 42 43
	// splitScaleN is the scaling factor used to determine how many times
	// to decompose a quadratic or cubic segment into a linear approximation.
	splitScale2, splitScale3 int
N
Nigel Tao 已提交
44 45

	// The current pen position.
46
	a fixed.Point26_6
N
Nigel Tao 已提交
47 48 49 50 51 52 53 54 55 56 57
	// The current cell and its area/coverage being accumulated.
	xi, yi      int
	area, cover int

	// Saved cells.
	cell []cell
	// Linked list of cells, one per row.
	cellIndex []int
	// Buffers.
	cellBuf      [256]cell
	cellIndexBuf [64]int
58
	spanBuf      [64]Span
N
Nigel Tao 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
}

// findCell returns the index in r.cell for the cell corresponding to
// (r.xi, r.yi). The cell is created if necessary.
func (r *Rasterizer) findCell() int {
	if r.yi < 0 || r.yi >= len(r.cellIndex) {
		return -1
	}
	xi := r.xi
	if xi < 0 {
		xi = -1
	} else if xi > r.width {
		xi = r.width
	}
	i, prev := r.cellIndex[r.yi], -1
	for i != -1 && r.cell[i].xi <= xi {
		if r.cell[i].xi == xi {
			return i
		}
		i, prev = r.cell[i].next, i
	}
	c := len(r.cell)
	if c == cap(r.cell) {
		buf := make([]cell, c, 4*c)
		copy(buf, r.cell)
		r.cell = buf[0 : c+1]
	} else {
		r.cell = r.cell[0 : c+1]
	}
	r.cell[c] = cell{xi, 0, 0, i}
	if prev == -1 {
		r.cellIndex[r.yi] = c
	} else {
		r.cell[prev].next = c
	}
	return c
}

// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
func (r *Rasterizer) saveCell() {
	if r.area != 0 || r.cover != 0 {
		i := r.findCell()
		if i != -1 {
			r.cell[i].area += r.area
			r.cell[i].cover += r.cover
		}
		r.area = 0
		r.cover = 0
	}
}

// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
func (r *Rasterizer) setCell(xi, yi int) {
	if r.xi != xi || r.yi != yi {
		r.saveCell()
		r.xi, r.yi = xi, yi
	}
}

// scan accumulates area/coverage for the yi'th scanline, going from
N
Nigel Tao 已提交
119
// x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates)
N
Nigel Tao 已提交
120
// and from y0f to y1f fractional vertical units within that scanline.
121 122 123 124 125 126
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f fixed.Int26_6) {
	// Break the 26.6 fixed point X co-ordinates into integral and fractional parts.
	x0i := int(x0) / 64
	x0f := x0 - fixed.Int26_6(64*x0i)
	x1i := int(x1) / 64
	x1f := x1 - fixed.Int26_6(64*x1i)
N
Nigel Tao 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

	// A perfectly horizontal scan.
	if y0f == y1f {
		r.setCell(x1i, yi)
		return
	}
	dx, dy := x1-x0, y1f-y0f
	// A single cell scan.
	if x0i == x1i {
		r.area += int((x0f + x1f) * dy)
		r.cover += int(dy)
		return
	}
	// There are at least two cells. Apart from the first and last cells,
	// all intermediate cells go through the full width of the cell,
142
	// or 64 units in 26.6 fixed point format.
N
Nigel Tao 已提交
143
	var (
144
		p, q, edge0, edge1 fixed.Int26_6
N
Nigel Tao 已提交
145 146 147
		xiDelta            int
	)
	if dx > 0 {
148 149
		p, q = (64-x0f)*dy, dx
		edge0, edge1, xiDelta = 0, 64, 1
N
Nigel Tao 已提交
150 151
	} else {
		p, q = x0f*dy, -dx
152
		edge0, edge1, xiDelta = 64, 0, -1
N
Nigel Tao 已提交
153 154 155 156 157 158 159 160 161 162 163 164 165 166
	}
	yDelta, yRem := p/q, p%q
	if yRem < 0 {
		yDelta -= 1
		yRem += q
	}
	// Do the first cell.
	xi, y := x0i, y0f
	r.area += int((x0f + edge1) * yDelta)
	r.cover += int(yDelta)
	xi, y = xi+xiDelta, y+yDelta
	r.setCell(xi, yi)
	if xi != x1i {
		// Do all the intermediate cells.
167
		p = 64 * (y1f - y + yDelta)
N
Nigel Tao 已提交
168 169 170 171 172 173 174 175 176 177 178 179 180
		fullDelta, fullRem := p/q, p%q
		if fullRem < 0 {
			fullDelta -= 1
			fullRem += q
		}
		yRem -= q
		for xi != x1i {
			yDelta = fullDelta
			yRem += fullRem
			if yRem >= 0 {
				yDelta += 1
				yRem -= q
			}
181
			r.area += int(64 * yDelta)
N
Nigel Tao 已提交
182 183 184 185 186 187 188 189 190 191 192 193
			r.cover += int(yDelta)
			xi, y = xi+xiDelta, y+yDelta
			r.setCell(xi, yi)
		}
	}
	// Do the last cell.
	yDelta = y1f - y
	r.area += int((edge0 + x1f) * yDelta)
	r.cover += int(yDelta)
}

// Start starts a new curve at the given point.
194 195
func (r *Rasterizer) Start(a fixed.Point26_6) {
	r.setCell(int(a.X/64), int(a.Y/64))
N
Nigel Tao 已提交
196 197 198
	r.a = a
}

199
// Add1 adds a linear segment to the current curve.
200
func (r *Rasterizer) Add1(b fixed.Point26_6) {
N
Nigel Tao 已提交
201 202 203
	x0, y0 := r.a.X, r.a.Y
	x1, y1 := b.X, b.Y
	dx, dy := x1-x0, y1-y0
204 205 206 207 208 209
	// Break the 26.6 fixed point Y co-ordinates into integral and fractional
	// parts.
	y0i := int(y0) / 64
	y0f := y0 - fixed.Int26_6(64*y0i)
	y1i := int(y1) / 64
	y1f := y1 - fixed.Int26_6(64*y1i)
N
Nigel Tao 已提交
210 211 212 213

	if y0i == y1i {
		// There is only one scanline.
		r.scan(y0i, x0, y0f, x1, y1f)
214 215 216 217 218

	} else if dx == 0 {
		// This is a vertical line segment. We avoid calling r.scan and instead
		// manipulate r.area and r.cover directly.
		var (
219
			edge0, edge1 fixed.Int26_6
220 221 222
			yiDelta      int
		)
		if dy > 0 {
223
			edge0, edge1, yiDelta = 0, 64, 1
224
		} else {
225
			edge0, edge1, yiDelta = 64, 0, -1
226
		}
227 228
		x0i, yi := int(x0)/64, y0i
		x0fTimes2 := (int(x0) - (64 * x0i)) * 2
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
		// Do the first pixel.
		dcover := int(edge1 - y0f)
		darea := int(x0fTimes2 * dcover)
		r.area += darea
		r.cover += dcover
		yi += yiDelta
		r.setCell(x0i, yi)
		// Do all the intermediate pixels.
		dcover = int(edge1 - edge0)
		darea = int(x0fTimes2 * dcover)
		for yi != y1i {
			r.area += darea
			r.cover += dcover
			yi += yiDelta
			r.setCell(x0i, yi)
		}
		// Do the last pixel.
		dcover = int(y1f - edge0)
		darea = int(x0fTimes2 * dcover)
		r.area += darea
		r.cover += dcover

N
Nigel Tao 已提交
251
	} else {
252 253 254
		// There are at least two scanlines. Apart from the first and last
		// scanlines, all intermediate scanlines go through the full height of
		// the row, or 64 units in 26.6 fixed point format.
N
Nigel Tao 已提交
255
		var (
256
			p, q, edge0, edge1 fixed.Int26_6
N
Nigel Tao 已提交
257 258 259
			yiDelta            int
		)
		if dy > 0 {
260 261
			p, q = (64-y0f)*dx, dy
			edge0, edge1, yiDelta = 0, 64, 1
N
Nigel Tao 已提交
262 263
		} else {
			p, q = y0f*dx, -dy
264
			edge0, edge1, yiDelta = 64, 0, -1
N
Nigel Tao 已提交
265 266 267 268 269 270 271 272 273 274
		}
		xDelta, xRem := p/q, p%q
		if xRem < 0 {
			xDelta -= 1
			xRem += q
		}
		// Do the first scanline.
		x, yi := x0, y0i
		r.scan(yi, x, y0f, x+xDelta, edge1)
		x, yi = x+xDelta, yi+yiDelta
275
		r.setCell(int(x)/64, yi)
N
Nigel Tao 已提交
276 277
		if yi != y1i {
			// Do all the intermediate scanlines.
278
			p = 64 * dx
N
Nigel Tao 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
			fullDelta, fullRem := p/q, p%q
			if fullRem < 0 {
				fullDelta -= 1
				fullRem += q
			}
			xRem -= q
			for yi != y1i {
				xDelta = fullDelta
				xRem += fullRem
				if xRem >= 0 {
					xDelta += 1
					xRem -= q
				}
				r.scan(yi, x, edge0, x+xDelta, edge1)
				x, yi = x+xDelta, yi+yiDelta
294
				r.setCell(int(x)/64, yi)
N
Nigel Tao 已提交
295 296 297 298 299 300 301 302 303
			}
		}
		// Do the last scanline.
		r.scan(yi, x, edge0, x1, y1f)
	}
	// The next lineTo starts from b.
	r.a = b
}

304
// Add2 adds a quadratic segment to the current curve.
305 306
func (r *Rasterizer) Add2(b, c fixed.Point26_6) {
	// Calculate nSplit (the number of recursive decompositions) based on how
307
	// 'curvy' it is. Specifically, how much the middle point b deviates from
308 309
	// (a+c)/2.
	dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / fixed.Int26_6(r.splitScale2)
N
Nigel Tao 已提交
310
	nsplit := 0
311 312
	for dev > 0 {
		dev /= 4
N
Nigel Tao 已提交
313 314
		nsplit++
	}
315 316
	// dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit
	// is 16.
N
Nigel Tao 已提交
317 318
	const maxNsplit = 16
	if nsplit > maxNsplit {
319
		panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
N
Nigel Tao 已提交
320 321 322
	}
	// Recursively decompose the curve nSplit levels deep.
	var (
323
		pStack [2*maxNsplit + 3]fixed.Point26_6
N
Nigel Tao 已提交
324 325 326 327 328 329 330 331 332
		sStack [maxNsplit + 1]int
		i      int
	)
	sStack[0] = nsplit
	pStack[0] = c
	pStack[1] = b
	pStack[2] = r.a
	for i >= 0 {
		s := sStack[i]
333
		p := pStack[2*i:]
N
Nigel Tao 已提交
334
		if s > 0 {
335 336 337
			// Split the quadratic curve p[:3] into an equivalent set of two
			// shorter curves: p[:3] and p[2:5]. The new p[4] is the old p[2],
			// and p[0] is unchanged.
338 339 340 341 342 343 344 345 346 347
			mx := p[1].X
			p[4].X = p[2].X
			p[3].X = (p[4].X + mx) / 2
			p[1].X = (p[0].X + mx) / 2
			p[2].X = (p[1].X + p[3].X) / 2
			my := p[1].Y
			p[4].Y = p[2].Y
			p[3].Y = (p[4].Y + my) / 2
			p[1].Y = (p[0].Y + my) / 2
			p[2].Y = (p[1].Y + p[3].Y) / 2
N
Nigel Tao 已提交
348 349 350 351 352
			// The two shorter curves have one less split to do.
			sStack[i] = s - 1
			sStack[i+1] = s - 1
			i++
		} else {
353 354
			// Replace the level-0 quadratic with a two-linear-piece
			// approximation.
355 356
			midx := (p[0].X + 2*p[1].X + p[2].X) / 4
			midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
357
			r.Add1(fixed.Point26_6{midx, midy})
358
			r.Add1(p[0])
N
Nigel Tao 已提交
359 360 361 362 363
			i--
		}
	}
}

364
// Add3 adds a cubic segment to the current curve.
365
func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) {
366 367
	// Calculate nSplit (the number of recursive decompositions) based on how
	// 'curvy' it is.
368 369
	dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / fixed.Int26_6(r.splitScale2)
	dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / fixed.Int26_6(r.splitScale3)
370 371 372 373 374 375
	nsplit := 0
	for dev2 > 0 || dev3 > 0 {
		dev2 /= 8
		dev3 /= 4
		nsplit++
	}
376 377
	// devN is 32-bit, and nsplit++ every time we shift off 2 bits, so
	// maxNsplit is 16.
378 379 380 381 382 383
	const maxNsplit = 16
	if nsplit > maxNsplit {
		panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
	}
	// Recursively decompose the curve nSplit levels deep.
	var (
384
		pStack [3*maxNsplit + 4]fixed.Point26_6
385 386 387 388 389 390 391 392 393 394 395 396
		sStack [maxNsplit + 1]int
		i      int
	)
	sStack[0] = nsplit
	pStack[0] = d
	pStack[1] = c
	pStack[2] = b
	pStack[3] = r.a
	for i >= 0 {
		s := sStack[i]
		p := pStack[3*i:]
		if s > 0 {
397 398 399
			// Split the cubic curve p[:4] into an equivalent set of two
			// shorter curves: p[:4] and p[3:7]. The new p[6] is the old p[3],
			// and p[0] is unchanged.
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
			m01x := (p[0].X + p[1].X) / 2
			m12x := (p[1].X + p[2].X) / 2
			m23x := (p[2].X + p[3].X) / 2
			p[6].X = p[3].X
			p[5].X = m23x
			p[1].X = m01x
			p[2].X = (m01x + m12x) / 2
			p[4].X = (m12x + m23x) / 2
			p[3].X = (p[2].X + p[4].X) / 2
			m01y := (p[0].Y + p[1].Y) / 2
			m12y := (p[1].Y + p[2].Y) / 2
			m23y := (p[2].Y + p[3].Y) / 2
			p[6].Y = p[3].Y
			p[5].Y = m23y
			p[1].Y = m01y
			p[2].Y = (m01y + m12y) / 2
			p[4].Y = (m12y + m23y) / 2
			p[3].Y = (p[2].Y + p[4].Y) / 2
			// The two shorter curves have one less split to do.
			sStack[i] = s - 1
			sStack[i+1] = s - 1
			i++
		} else {
			// Replace the level-0 cubic with a two-linear-piece approximation.
			midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
			midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
426
			r.Add1(fixed.Point26_6{midx, midy})
427 428 429 430
			r.Add1(p[0])
			i--
		}
	}
N
Nigel Tao 已提交
431 432
}

433 434 435 436 437
// AddPath adds the given Path.
func (r *Rasterizer) AddPath(p Path) {
	for i := 0; i < len(p); {
		switch p[i] {
		case 0:
438 439 440
			r.Start(
				fixed.Point26_6{p[i+1], p[i+2]},
			)
441
			i += 4
442
		case 1:
443 444 445
			r.Add1(
				fixed.Point26_6{p[i+1], p[i+2]},
			)
446
			i += 4
447
		case 2:
448 449 450 451
			r.Add2(
				fixed.Point26_6{p[i+1], p[i+2]},
				fixed.Point26_6{p[i+3], p[i+4]},
			)
452
			i += 6
453
		case 3:
454 455 456 457 458
			r.Add3(
				fixed.Point26_6{p[i+1], p[i+2]},
				fixed.Point26_6{p[i+3], p[i+4]},
				fixed.Point26_6{p[i+5], p[i+6]},
			)
459
			i += 8
460 461 462 463 464 465 466
		default:
			panic("freetype/raster: bad path")
		}
	}
}

// AddStroke adds a stroked Path.
467
func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
468
	Stroke(r, q, width, cr, jr)
469 470
}

471
// areaToAlpha converts an area value to a uint32 alpha value. A completely
472
// filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The
N
Nigel Tao 已提交
473 474 475
// conversion of area values greater than this depends on the winding rule:
// even-odd or non-zero.
func (r *Rasterizer) areaToAlpha(area int) uint32 {
476 477
	// The C Freetype implementation (version 2.3.12) does "alpha := area>>1"
	// without the +1. Round-to-nearest gives a more symmetric result than
478
	// round-down. The C implementation also returns 8-bit alpha, not 16-bit
479
	// alpha.
N
Nigel Tao 已提交
480 481 482 483 484 485
	a := (area + 1) >> 1
	if a < 0 {
		a = -a
	}
	alpha := uint32(a)
	if r.UseNonZeroWinding {
486 487
		if alpha > 0x0fff {
			alpha = 0x0fff
N
Nigel Tao 已提交
488 489
		}
	} else {
490 491 492 493 494
		alpha &= 0x1fff
		if alpha > 0x1000 {
			alpha = 0x2000 - alpha
		} else if alpha == 0x1000 {
			alpha = 0x0fff
N
Nigel Tao 已提交
495 496
		}
	}
497 498 499
	// alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to
	// 16-bit alpha.
	return alpha<<4 | alpha>>8
N
Nigel Tao 已提交
500 501
}

502 503 504 505
// Rasterize converts r's accumulated curves into Spans for p. The Spans passed
// to p are non-overlapping, and sorted by Y and then X. They all have non-zero
// width (and 0 <= X0 < X1 <= r.width) and non-zero A, except for the final
// Span, which has Y, X0, X1 and A all equal to zero.
N
Nigel Tao 已提交
506 507
func (r *Rasterizer) Rasterize(p Painter) {
	r.saveCell()
508
	s := 0
N
Nigel Tao 已提交
509 510 511 512
	for yi := 0; yi < len(r.cellIndex); yi++ {
		xi, cover := 0, 0
		for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
			if cover != 0 && r.cell[c].xi > xi {
513
				alpha := r.areaToAlpha(cover * 64 * 2)
N
Nigel Tao 已提交
514 515 516 517 518 519 520 521 522
				if alpha != 0 {
					xi0, xi1 := xi, r.cell[c].xi
					if xi0 < 0 {
						xi0 = 0
					}
					if xi1 >= r.width {
						xi1 = r.width
					}
					if xi0 < xi1 {
523
						r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
524
						s++
N
Nigel Tao 已提交
525 526 527 528
					}
				}
			}
			cover += r.cell[c].cover
529
			alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area)
N
Nigel Tao 已提交
530 531 532 533 534 535 536 537 538 539
			xi = r.cell[c].xi + 1
			if alpha != 0 {
				xi0, xi1 := r.cell[c].xi, xi
				if xi0 < 0 {
					xi0 = 0
				}
				if xi1 >= r.width {
					xi1 = r.width
				}
				if xi0 < xi1 {
540
					r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
541
					s++
N
Nigel Tao 已提交
542 543
				}
			}
544
			if s > len(r.spanBuf)-2 {
545
				p.Paint(r.spanBuf[:s], false)
546 547
				s = 0
			}
N
Nigel Tao 已提交
548 549
		}
	}
550
	p.Paint(r.spanBuf[:s], true)
N
Nigel Tao 已提交
551 552
}

553
// Clear cancels any previous calls to r.Start or r.AddXxx.
N
Nigel Tao 已提交
554
func (r *Rasterizer) Clear() {
555
	r.a = fixed.Point26_6{}
N
Nigel Tao 已提交
556 557 558 559
	r.xi = 0
	r.yi = 0
	r.area = 0
	r.cover = 0
560
	r.cell = r.cell[:0]
N
Nigel Tao 已提交
561 562 563 564 565
	for i := 0; i < len(r.cellIndex); i++ {
		r.cellIndex[i] = -1
	}
}

566
// SetBounds sets the maximum width and height of the rasterized image and
567
// calls Clear. The width and height are in pixels, not fixed.Int26_6 units.
568
func (r *Rasterizer) SetBounds(width, height int) {
N
Nigel Tao 已提交
569 570 571 572 573 574
	if width < 0 {
		width = 0
	}
	if height < 0 {
		height = 0
	}
575 576 577
	// Use the same ssN heuristic as the C Freetype (version 2.4.0)
	// implementation.
	ss2, ss3 := 32, 16
N
Nigel Tao 已提交
578
	if width > 24 || height > 24 {
579
		ss2, ss3 = 2*ss2, 2*ss3
N
Nigel Tao 已提交
580
		if width > 120 || height > 120 {
581
			ss2, ss3 = 2*ss2, 2*ss3
N
Nigel Tao 已提交
582 583 584
		}
	}
	r.width = width
585 586
	r.splitScale2 = ss2
	r.splitScale3 = ss3
587
	r.cell = r.cellBuf[:0]
N
Nigel Tao 已提交
588 589 590
	if height > len(r.cellIndexBuf) {
		r.cellIndex = make([]int, height)
	} else {
591
		r.cellIndex = r.cellIndexBuf[:height]
N
Nigel Tao 已提交
592
	}
593 594 595 596 597 598 599
	r.Clear()
}

// NewRasterizer creates a new Rasterizer with the given bounds.
func NewRasterizer(width, height int) *Rasterizer {
	r := new(Rasterizer)
	r.SetBounds(width, height)
N
Nigel Tao 已提交
600 601
	return r
}