// 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 provides an anti-aliasing 2-D rasterizer. // // 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. // // 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 // http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm package raster // import "git.fromouter.space/crunchy-rocks/freetype/raster" import ( "strconv" "golang.org/x/image/math/fixed" ) // A cell is part of a linked list (for a given yi co-ordinate) of accumulated // 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 // An offset (in pixels) to the painted spans. Dx, Dy int // The width of the Rasterizer. The height is implicit in len(cellIndex). width int // 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 // The current pen position. a fixed.Point26_6 // 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 spanBuf [64]Span } // 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 // x0 to x1 in the horizontal direction (in 26.6 fixed point co-ordinates) // and from y0f to y1f fractional vertical units within that scanline. 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) // 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, // or 64 units in 26.6 fixed point format. var ( p, q, edge0, edge1 fixed.Int26_6 xiDelta int ) if dx > 0 { p, q = (64-x0f)*dy, dx edge0, edge1, xiDelta = 0, 64, 1 } else { p, q = x0f*dy, -dx edge0, edge1, xiDelta = 64, 0, -1 } 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. p = 64 * (y1f - y + yDelta) 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 } r.area += int(64 * yDelta) 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. func (r *Rasterizer) Start(a fixed.Point26_6) { r.setCell(int(a.X/64), int(a.Y/64)) r.a = a } // Add1 adds a linear segment to the current curve. func (r *Rasterizer) Add1(b fixed.Point26_6) { x0, y0 := r.a.X, r.a.Y x1, y1 := b.X, b.Y dx, dy := x1-x0, y1-y0 // 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) if y0i == y1i { // There is only one scanline. r.scan(y0i, x0, y0f, x1, y1f) } 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 ( edge0, edge1 fixed.Int26_6 yiDelta int ) if dy > 0 { edge0, edge1, yiDelta = 0, 64, 1 } else { edge0, edge1, yiDelta = 64, 0, -1 } x0i, yi := int(x0)/64, y0i x0fTimes2 := (int(x0) - (64 * x0i)) * 2 // 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 } else { // 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. var ( p, q, edge0, edge1 fixed.Int26_6 yiDelta int ) if dy > 0 { p, q = (64-y0f)*dx, dy edge0, edge1, yiDelta = 0, 64, 1 } else { p, q = y0f*dx, -dy edge0, edge1, yiDelta = 64, 0, -1 } 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 r.setCell(int(x)/64, yi) if yi != y1i { // Do all the intermediate scanlines. p = 64 * dx 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 r.setCell(int(x)/64, yi) } } // Do the last scanline. r.scan(yi, x, edge0, x1, y1f) } // The next lineTo starts from b. r.a = b } // Add2 adds a quadratic segment to the current curve. func (r *Rasterizer) Add2(b, c fixed.Point26_6) { // Calculate nSplit (the number of recursive decompositions) based on how // 'curvy' it is. Specifically, how much the middle point b deviates from // (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) nsplit := 0 for dev > 0 { dev /= 4 nsplit++ } // dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit // is 16. const maxNsplit = 16 if nsplit > maxNsplit { panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit)) } // Recursively decompose the curve nSplit levels deep. var ( pStack [2*maxNsplit + 3]fixed.Point26_6 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] p := pStack[2*i:] if s > 0 { // 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. 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 // 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 quadratic with a two-linear-piece // approximation. midx := (p[0].X + 2*p[1].X + p[2].X) / 4 midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4 r.Add1(fixed.Point26_6{midx, midy}) r.Add1(p[0]) i-- } } } // Add3 adds a cubic segment to the current curve. func (r *Rasterizer) Add3(b, c, d fixed.Point26_6) { // Calculate nSplit (the number of recursive decompositions) based on how // 'curvy' it is. 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) nsplit := 0 for dev2 > 0 || dev3 > 0 { dev2 /= 8 dev3 /= 4 nsplit++ } // devN is 32-bit, and nsplit++ every time we shift off 2 bits, so // maxNsplit is 16. const maxNsplit = 16 if nsplit > maxNsplit { panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit)) } // Recursively decompose the curve nSplit levels deep. var ( pStack [3*maxNsplit + 4]fixed.Point26_6 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 { // 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. 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 r.Add1(fixed.Point26_6{midx, midy}) r.Add1(p[0]) i-- } } } // AddPath adds the given Path. func (r *Rasterizer) AddPath(p Path) { for i := 0; i < len(p); { switch p[i] { case 0: r.Start( fixed.Point26_6{p[i+1], p[i+2]}, ) i += 4 case 1: r.Add1( fixed.Point26_6{p[i+1], p[i+2]}, ) i += 4 case 2: r.Add2( fixed.Point26_6{p[i+1], p[i+2]}, fixed.Point26_6{p[i+3], p[i+4]}, ) i += 6 case 3: 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]}, ) i += 8 default: panic("freetype/raster: bad path") } } } // AddStroke adds a stroked Path. func (r *Rasterizer) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) { Stroke(r, q, width, cr, jr) } // areaToAlpha converts an area value to a uint32 alpha value. A completely // filled pixel corresponds to an area of 64*64*2, and an alpha of 0xffff. The // conversion of area values greater than this depends on the winding rule: // even-odd or non-zero. func (r *Rasterizer) areaToAlpha(area int) uint32 { // The C Freetype implementation (version 2.3.12) does "alpha := area>>1" // without the +1. Round-to-nearest gives a more symmetric result than // round-down. The C implementation also returns 8-bit alpha, not 16-bit // alpha. a := (area + 1) >> 1 if a < 0 { a = -a } alpha := uint32(a) if r.UseNonZeroWinding { if alpha > 0x0fff { alpha = 0x0fff } } else { alpha &= 0x1fff if alpha > 0x1000 { alpha = 0x2000 - alpha } else if alpha == 0x1000 { alpha = 0x0fff } } // alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to // 16-bit alpha. return alpha<<4 | alpha>>8 } // 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. func (r *Rasterizer) Rasterize(p Painter) { r.saveCell() s := 0 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 { alpha := r.areaToAlpha(cover * 64 * 2) 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 { r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha} s++ } } } cover += r.cell[c].cover alpha := r.areaToAlpha(cover*64*2 - r.cell[c].area) 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 { r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha} s++ } } if s > len(r.spanBuf)-2 { p.Paint(r.spanBuf[:s], false) s = 0 } } } p.Paint(r.spanBuf[:s], true) } // Clear cancels any previous calls to r.Start or r.AddXxx. func (r *Rasterizer) Clear() { r.a = fixed.Point26_6{} r.xi = 0 r.yi = 0 r.area = 0 r.cover = 0 r.cell = r.cell[:0] for i := 0; i < len(r.cellIndex); i++ { r.cellIndex[i] = -1 } } // SetBounds sets the maximum width and height of the rasterized image and // calls Clear. The width and height are in pixels, not fixed.Int26_6 units. func (r *Rasterizer) SetBounds(width, height int) { if width < 0 { width = 0 } if height < 0 { height = 0 } // Use the same ssN heuristic as the C Freetype (version 2.4.0) // implementation. ss2, ss3 := 32, 16 if width > 24 || height > 24 { ss2, ss3 = 2*ss2, 2*ss3 if width > 120 || height > 120 { ss2, ss3 = 2*ss2, 2*ss3 } } r.width = width r.splitScale2 = ss2 r.splitScale3 = ss3 r.cell = r.cellBuf[:0] if height > len(r.cellIndexBuf) { r.cellIndex = make([]int, height) } else { r.cellIndex = r.cellIndexBuf[:height] } r.Clear() } // NewRasterizer creates a new Rasterizer with the given bounds. func NewRasterizer(width, height int) *Rasterizer { r := new(Rasterizer) r.SetBounds(width, height) return r }