// 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 "github.com/golang/freetype/raster" import ( ) // 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 ( *Rasterizer) () int { if .yi < 0 || .yi >= len(.cellIndex) { return -1 } := .xi if < 0 { = -1 } else if > .width { = .width } , := .cellIndex[.yi], -1 for != -1 && .cell[].xi <= { if .cell[].xi == { return } , = .cell[].next, } := len(.cell) if == cap(.cell) { := make([]cell, , 4*) copy(, .cell) .cell = [0 : +1] } else { .cell = .cell[0 : +1] } .cell[] = cell{, 0, 0, } if == -1 { .cellIndex[.yi] = } else { .cell[].next = } return } // saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi). func ( *Rasterizer) () { if .area != 0 || .cover != 0 { := .findCell() if != -1 { .cell[].area += .area .cell[].cover += .cover } .area = 0 .cover = 0 } } // setCell sets the (xi, yi) cell that r is accumulating area/coverage for. func ( *Rasterizer) (, int) { if .xi != || .yi != { .saveCell() .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 ( *Rasterizer) ( int, , , , fixed.Int26_6) { // Break the 26.6 fixed point X co-ordinates into integral and fractional parts. := int() / 64 := - fixed.Int26_6(64*) := int() / 64 := - fixed.Int26_6(64*) // A perfectly horizontal scan. if == { .setCell(, ) return } , := -, - // A single cell scan. if == { .area += int(( + ) * ) .cover += int() 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 ( , , , fixed.Int26_6 int ) if > 0 { , = (64-)*, , , = 0, 64, 1 } else { , = *, - , , = 64, 0, -1 } , := /, % if < 0 { -= 1 += } // Do the first cell. , := , .area += int(( + ) * ) .cover += int() , = +, + .setCell(, ) if != { // Do all the intermediate cells. = 64 * ( - + ) , := /, % if < 0 { -= 1 += } -= for != { = += if >= 0 { += 1 -= } .area += int(64 * ) .cover += int() , = +, + .setCell(, ) } } // Do the last cell. = - .area += int(( + ) * ) .cover += int() } // Start starts a new curve at the given point. func ( *Rasterizer) ( fixed.Point26_6) { .setCell(int(.X/64), int(.Y/64)) .a = } // Add1 adds a linear segment to the current curve. func ( *Rasterizer) ( fixed.Point26_6) { , := .a.X, .a.Y , := .X, .Y , := -, - // Break the 26.6 fixed point Y co-ordinates into integral and fractional // parts. := int() / 64 := - fixed.Int26_6(64*) := int() / 64 := - fixed.Int26_6(64*) if == { // There is only one scanline. .scan(, , , , ) } else if == 0 { // This is a vertical line segment. We avoid calling r.scan and instead // manipulate r.area and r.cover directly. var ( , fixed.Int26_6 int ) if > 0 { , , = 0, 64, 1 } else { , , = 64, 0, -1 } , := int()/64, := (int() - (64 * )) * 2 // Do the first pixel. := int( - ) := int( * ) .area += .cover += += .setCell(, ) // Do all the intermediate pixels. = int( - ) = int( * ) for != { .area += .cover += += .setCell(, ) } // Do the last pixel. = int( - ) = int( * ) .area += .cover += } 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 ( , , , fixed.Int26_6 int ) if > 0 { , = (64-)*, , , = 0, 64, 1 } else { , = *, - , , = 64, 0, -1 } , := /, % if < 0 { -= 1 += } // Do the first scanline. , := , .scan(, , , +, ) , = +, + .setCell(int()/64, ) if != { // Do all the intermediate scanlines. = 64 * , := /, % if < 0 { -= 1 += } -= for != { = += if >= 0 { += 1 -= } .scan(, , , +, ) , = +, + .setCell(int()/64, ) } } // Do the last scanline. .scan(, , , , ) } // The next lineTo starts from b. .a = } // Add2 adds a quadratic segment to the current curve. func ( *Rasterizer) (, 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. := maxAbs(.a.X-2*.X+.X, .a.Y-2*.Y+.Y) / fixed.Int26_6(.splitScale2) := 0 for > 0 { /= 4 ++ } // dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit // is 16. const = 16 if > { panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa()) } // Recursively decompose the curve nSplit levels deep. var ( [2* + 3]fixed.Point26_6 [ + 1]int int ) [0] = [0] = [1] = [2] = .a for >= 0 { := [] := [2*:] if > 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. := [1].X [4].X = [2].X [3].X = ([4].X + ) / 2 [1].X = ([0].X + ) / 2 [2].X = ([1].X + [3].X) / 2 := [1].Y [4].Y = [2].Y [3].Y = ([4].Y + ) / 2 [1].Y = ([0].Y + ) / 2 [2].Y = ([1].Y + [3].Y) / 2 // The two shorter curves have one less split to do. [] = - 1 [+1] = - 1 ++ } else { // Replace the level-0 quadratic with a two-linear-piece // approximation. := ([0].X + 2*[1].X + [2].X) / 4 := ([0].Y + 2*[1].Y + [2].Y) / 4 .Add1(fixed.Point26_6{, }) .Add1([0]) -- } } } // Add3 adds a cubic segment to the current curve. func ( *Rasterizer) (, , fixed.Point26_6) { // Calculate nSplit (the number of recursive decompositions) based on how // 'curvy' it is. := maxAbs(.a.X-3*(.X+.X)+.X, .a.Y-3*(.Y+.Y)+.Y) / fixed.Int26_6(.splitScale2) := maxAbs(.a.X-2*.X+.X, .a.Y-2*.Y+.Y) / fixed.Int26_6(.splitScale3) := 0 for > 0 || > 0 { /= 8 /= 4 ++ } // devN is 32-bit, and nsplit++ every time we shift off 2 bits, so // maxNsplit is 16. const = 16 if > { panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa()) } // Recursively decompose the curve nSplit levels deep. var ( [3* + 4]fixed.Point26_6 [ + 1]int int ) [0] = [0] = [1] = [2] = [3] = .a for >= 0 { := [] := [3*:] if > 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. := ([0].X + [1].X) / 2 := ([1].X + [2].X) / 2 := ([2].X + [3].X) / 2 [6].X = [3].X [5].X = [1].X = [2].X = ( + ) / 2 [4].X = ( + ) / 2 [3].X = ([2].X + [4].X) / 2 := ([0].Y + [1].Y) / 2 := ([1].Y + [2].Y) / 2 := ([2].Y + [3].Y) / 2 [6].Y = [3].Y [5].Y = [1].Y = [2].Y = ( + ) / 2 [4].Y = ( + ) / 2 [3].Y = ([2].Y + [4].Y) / 2 // The two shorter curves have one less split to do. [] = - 1 [+1] = - 1 ++ } else { // Replace the level-0 cubic with a two-linear-piece approximation. := ([0].X + 3*([1].X+[2].X) + [3].X) / 8 := ([0].Y + 3*([1].Y+[2].Y) + [3].Y) / 8 .Add1(fixed.Point26_6{, }) .Add1([0]) -- } } } // AddPath adds the given Path. func ( *Rasterizer) ( Path) { for := 0; < len(); { switch [] { case 0: .Start( fixed.Point26_6{[+1], [+2]}, ) += 4 case 1: .Add1( fixed.Point26_6{[+1], [+2]}, ) += 4 case 2: .Add2( fixed.Point26_6{[+1], [+2]}, fixed.Point26_6{[+3], [+4]}, ) += 6 case 3: .Add3( fixed.Point26_6{[+1], [+2]}, fixed.Point26_6{[+3], [+4]}, fixed.Point26_6{[+5], [+6]}, ) += 8 default: panic("freetype/raster: bad path") } } } // AddStroke adds a stroked Path. func ( *Rasterizer) ( Path, fixed.Int26_6, Capper, Joiner) { Stroke(, , , , ) } // 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 ( *Rasterizer) ( 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. := ( + 1) >> 1 if < 0 { = - } := uint32() if .UseNonZeroWinding { if > 0x0fff { = 0x0fff } } else { &= 0x1fff if > 0x1000 { = 0x2000 - } else if == 0x1000 { = 0x0fff } } // alpha is now in the range [0x0000, 0x0fff]. Convert that 12-bit alpha to // 16-bit alpha. return <<4 | >>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 ( *Rasterizer) ( Painter) { .saveCell() := 0 for := 0; < len(.cellIndex); ++ { , := 0, 0 for := .cellIndex[]; != -1; = .cell[].next { if != 0 && .cell[].xi > { := .areaToAlpha( * 64 * 2) if != 0 { , := , .cell[].xi if < 0 { = 0 } if >= .width { = .width } if < { .spanBuf[] = Span{ + .Dy, + .Dx, + .Dx, } ++ } } } += .cell[].cover := .areaToAlpha(*64*2 - .cell[].area) = .cell[].xi + 1 if != 0 { , := .cell[].xi, if < 0 { = 0 } if >= .width { = .width } if < { .spanBuf[] = Span{ + .Dy, + .Dx, + .Dx, } ++ } } if > len(.spanBuf)-2 { .Paint(.spanBuf[:], false) = 0 } } } .Paint(.spanBuf[:], true) } // Clear cancels any previous calls to r.Start or r.AddXxx. func ( *Rasterizer) () { .a = fixed.Point26_6{} .xi = 0 .yi = 0 .area = 0 .cover = 0 .cell = .cell[:0] for := 0; < len(.cellIndex); ++ { .cellIndex[] = -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 ( *Rasterizer) (, int) { if < 0 { = 0 } if < 0 { = 0 } // Use the same ssN heuristic as the C Freetype (version 2.4.0) // implementation. , := 32, 16 if > 24 || > 24 { , = 2*, 2* if > 120 || > 120 { , = 2*, 2* } } .width = .splitScale2 = .splitScale3 = .cell = .cellBuf[:0] if > len(.cellIndexBuf) { .cellIndex = make([]int, ) } else { .cellIndex = .cellIndexBuf[:] } .Clear() } // NewRasterizer creates a new Rasterizer with the given bounds. func (, int) *Rasterizer { := new(Rasterizer) .SetBounds(, ) return }