diff --git a/draw2d/raster/coverage_table.go b/draw2d/raster/coverage_table.go index a9c0e3a..b7d4fdc 100644 --- a/draw2d/raster/coverage_table.go +++ b/draw2d/raster/coverage_table.go @@ -3,79 +3,132 @@ package raster var SUBPIXEL_OFFSETS_SAMPLE_8 = [8]float64{ - 5 / 8, - 0 / 8, - 3 / 8, - 6 / 8, - 1 / 8, - 4 / 8, - 7 / 8, - 2 / 8, -} - -var SUBPIXEL_OFFSETS_SAMPLE_8_FIXED = [8]Fix{ - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[0]*FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[1]*FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[2]*FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[3]*FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[4]*FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[5]*FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[6]*FIXED_FLOAT_COEF), - Fix(SUBPIXEL_OFFSETS_SAMPLE_8[7]*FIXED_FLOAT_COEF), + 5.0 / 8, + 0.0 / 8, + 3.0 / 8, + 6.0 / 8, + 1.0 / 8, + 4.0 / 8, + 7.0 / 8, + 2.0 / 8, +} + +var SUBPIXEL_OFFSETS_SAMPLE_8_FIXED = [8]Fix{ + Fix(SUBPIXEL_OFFSETS_SAMPLE_8[0] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_8[1] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_8[2] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_8[3] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_8[4] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_8[5] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_8[6] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_8[7] * FIXED_FLOAT_COEF), } var SUBPIXEL_OFFSETS_SAMPLE_16 = [16]float64{ - (1 / 16), - (8 / 16), - (4 / 16), - (15 / 16), - (11 / 16), - (2 / 16), - (6 / 16), - (14 / 16), - (10 / 16), - (3 / 16), - (7 / 16), - (12 / 16), - (0 / 16), - (9 / 16), - (5 / 16), - (13 / 16), + 1.0 / 16, + 8.0 / 16, + 4.0 / 16, + 15.0 / 16, + 11.0 / 16, + 2.0 / 16, + 6.0 / 16, + 14.0 / 16, + 10.0 / 16, + 3.0 / 16, + 7.0 / 16, + 12.0 / 16, + 0.0 / 16, + 9.0 / 16, + 5.0 / 16, + 13.0 / 16, +} + +var SUBPIXEL_OFFSETS_SAMPLE_16_FIXED = [16]Fix{ + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[0] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[1] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[2] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[3] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[4] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[5] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[6] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[7] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[8] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[9] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[10] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[11] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[12] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[13] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[14] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_16[15] * FIXED_FLOAT_COEF), } var SUBPIXEL_OFFSETS_SAMPLE_32 = [32]float64{ - 28 / 32, - 13 / 32, - 6 / 32, - 23 / 32, - 0 / 32, - 17 / 32, - 10 / 32, - 27 / 32, - 4 / 32, - 21 / 32, - 14 / 32, - 31 / 32, - 8 / 32, - 25 / 32, - 18 / 32, - 3 / 32, - 12 / 32, - 29 / 32, - 22 / 32, - 7 / 32, - 16 / 32, - 1 / 32, - 26 / 32, - 11 / 32, - 20 / 32, - 5 / 32, - 30 / 32, - 15 / 32, - 24 / 32, - 9 / 32, - 2 / 32, - 19 / 32, + 28.0 / 32, + 13.0 / 32, + 6.0 / 32, + 23.0 / 32, + 0.0 / 32, + 17.0 / 32, + 10.0 / 32, + 27.0 / 32, + 4.0 / 32, + 21.0 / 32, + 14.0 / 32, + 31.0 / 32, + 8.0 / 32, + 25.0 / 32, + 18.0 / 32, + 3.0 / 32, + 12.0 / 32, + 29.0 / 32, + 22.0 / 32, + 7.0 / 32, + 16.0 / 32, + 1.0 / 32, + 26.0 / 32, + 11.0 / 32, + 20.0 / 32, + 5.0 / 32, + 30.0 / 32, + 15.0 / 32, + 24.0 / 32, + 9.0 / 32, + 2.0 / 32, + 19.0 / 32, +} +var SUBPIXEL_OFFSETS_SAMPLE_32_FIXED = [32]Fix{ + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[0] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[1] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[2] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[3] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[4] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[5] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[6] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[7] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[8] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[9] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[10] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[11] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[12] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[13] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[14] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[15] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[16] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[17] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[18] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[19] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[20] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[21] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[22] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[23] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[24] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[25] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[26] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[27] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[28] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[29] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[30] * FIXED_FLOAT_COEF), + Fix(SUBPIXEL_OFFSETS_SAMPLE_32[31] * FIXED_FLOAT_COEF), } var coverageTable = [256]uint8{ @@ -146,5 +199,5 @@ var coverageTable = [256]uint8{ } func pixelCoverage(a uint8) uint8 { - return (((a) & 1) + (((a) >> 1) & 1) + (((a) >> 2) & 1) + (((a) >> 3) & 1) + (((a) >> 4) & 1) + (((a) >> 5) & 1) + (((a) >> 6) & 1) + (((a) >> 7) & 1)) + return a&1 + a>>1&1 + a>>2&1 + a>>3&1 + a>>4&1 + a>>5&1 + a>>6&1 + a>>7&1 } diff --git a/draw2d/raster/fillerAA.go b/draw2d/raster/fillerAA.go index 5bc4055..fa81348 100644 --- a/draw2d/raster/fillerAA.go +++ b/draw2d/raster/fillerAA.go @@ -1,320 +1,320 @@ // Copyright 2011 The draw2d Authors. All rights reserved. // created: 27/05/2011 by Laurent Le Goff -package raster - -import ( - "image" - "unsafe" -) - -const ( - SUBPIXEL_SHIFT = 3 - SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT -) - -var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8_FIXED - -type SUBPIXEL_DATA uint8 -type NON_ZERO_MASK_DATA_UNIT uint8 - -type Rasterizer8BitsSample struct { - MaskBuffer []SUBPIXEL_DATA - WindingBuffer []NON_ZERO_MASK_DATA_UNIT - - Width int - BufferWidth int - Height int - ClipBound [4]float64 - RemappingMatrix [6]float64 -} - +package raster + +import ( + "image" + "unsafe" +) + +const ( + SUBPIXEL_SHIFT = 3 + SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT +) + +var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8_FIXED + +type SUBPIXEL_DATA uint8 +type NON_ZERO_MASK_DATA_UNIT uint8 + +type Rasterizer8BitsSample struct { + MaskBuffer []SUBPIXEL_DATA + WindingBuffer []NON_ZERO_MASK_DATA_UNIT + + Width int + BufferWidth int + Height int + ClipBound [4]float64 + RemappingMatrix [6]float64 +} + /* width and height define the maximum output size for the filler. * The filler will output to larger bitmaps as well, but the output will * be cropped. - */ -func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample { - var r Rasterizer8BitsSample + */ +func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample { + var r Rasterizer8BitsSample // Scale the coordinates by SUBPIXEL_COUNT in vertical direction // The sampling point for the sub-pixel is at the top right corner. This // adjustment moves it to the pixel center. - r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT} - r.Width = width - r.Height = height + r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT} + r.Width = width + r.Height = height // The buffer used for filling needs to be one pixel wider than the bitmap. // This is because the end flag that turns the fill of is the first pixel // after the actually drawn edge. - r.BufferWidth = width + 1 - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT) - r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT) - return &r -} - -func clip(x, y, width, height, scale int) [4]float64 { - var clipBound [4]float64 - - offset := 0.99 / float64(scale) - - clipBound[0] = float64(x) + offset - clipBound[2] = float64(x+width) - offset - - clipBound[1] = float64(y * scale) - clipBound[3] = float64((y + height) * scale) - return clipBound -} - -func intersect(r1, r2 [4]float64) [4]float64 { - if r1[0] < r2[0] { - r1[0] = r2[0] - } - if r1[2] > r2[2] { - r1[2] = r2[2] - } - if r1[0] > r1[2] { - r1[0] = r1[2] - } - - if r1[1] < r2[1] { - r1[1] = r2[1] - } - if r1[3] > r2[3] { - r1[3] = r2[3] - } - if r1[1] > r1[3] { - r1[1] = r1[3] - } - return r1 -} - -func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { + r.BufferWidth = width + 1 + + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height) + r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT) + r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT) + return &r +} + +func clip(x, y, width, height, scale int) [4]float64 { + var clipBound [4]float64 + + offset := 0.99 / float64(scale) + + clipBound[0] = float64(x) + offset + clipBound[2] = float64(x+width) - offset + + clipBound[1] = float64(y * scale) + clipBound[3] = float64((y + height) * scale) + return clipBound +} + +func intersect(r1, r2 [4]float64) [4]float64 { + if r1[0] < r2[0] { + r1[0] = r2[0] + } + if r1[2] > r2[2] { + r1[2] = r2[2] + } + if r1[0] > r1[2] { + r1[0] = r1[2] + } + + if r1[1] < r2[1] { + r1[1] = r2[1] + } + if r1[3] > r2[3] { + r1[3] = r2[3] + } + if r1[1] > r1[3] { + r1[1] = r1[3] + } + return r1 +} + +func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { // memset 0 the mask buffer - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) + // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addEvenOddEdge(&(edges[k])) - } - p += 16 - } - - r.fillEvenOdd(img, color, clipRect) -} - + transform := [6]float64{ + tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], + tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], + tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], + tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], + tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], + tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], + } + + clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) + clipRect = intersect(clipRect, r.ClipBound) + p := 0 + l := len(*polygon) / 2 + var edges [32]PolygonEdge + for p < l { + edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) + for k := 0; k < edgeCount; k++ { + r.addEvenOddEdge(&edges[k]) + } + p += 16 + } + + r.fillEvenOdd(img, color, clipRect) +} + //! Adds an edge to be used with even-odd fill. -func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) { - x := Fix(edge.X * FIXED_FLOAT_COEF) - slope := Fix(edge.Slope * FIXED_FLOAT_COEF) - slopeFix := Fix(0) - if (edge.LastLine - edge.FirstLine >= SLOPE_FIX_STEP) { - slopeFix = Fix(edge.Slope * SLOPE_FIX_STEP * FIXED_FLOAT_COEF) - (slope << SLOPE_FIX_SHIFT); - } - - var mask SUBPIXEL_DATA - var ySub uint32 - var xp, yLine int - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = uint32(y & (SUBPIXEL_COUNT - 1)) - xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask - x += slope - if (y & SLOPE_FIX_MASK) == 0 { - x += slopeFix; - } - } -} - +func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) { + x := Fix(edge.X * FIXED_FLOAT_COEF) + slope := Fix(edge.Slope * FIXED_FLOAT_COEF) + slopeFix := Fix(0) + if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP { + slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<> FIXED_SHIFT) + mask = SUBPIXEL_DATA(1 << ySub) + yLine = y >> SUBPIXEL_SHIFT + r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask + x += slope + if y&SLOPE_FIX_MASK == 0 { + x += slopeFix + } + } +} + //! Adds an edge to be used with non-zero winding fill. -func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) { - x := Fix(edge.X * FIXED_FLOAT_COEF) - slope := Fix(edge.Slope * FIXED_FLOAT_COEF) - slopeFix := Fix(0) - if (edge.LastLine - edge.FirstLine >= SLOPE_FIX_STEP) { - slopeFix = Fix(edge.Slope * SLOPE_FIX_STEP * FIXED_FLOAT_COEF) - (slope << SLOPE_FIX_SHIFT); - } - var mask SUBPIXEL_DATA - var ySub uint32 - var xp, yLine int - winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding) - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = uint32(y & (SUBPIXEL_COUNT - 1)) - xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask - r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding - x += slope - if (y & SLOPE_FIX_MASK) == 0 { - x += slopeFix; - } - } -} - +func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) { + x := Fix(edge.X * FIXED_FLOAT_COEF) + slope := Fix(edge.Slope * FIXED_FLOAT_COEF) + slopeFix := Fix(0) + if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP { + slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<> FIXED_SHIFT) + mask = SUBPIXEL_DATA(1 << ySub) + yLine = y >> SUBPIXEL_SHIFT + r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask + r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding + x += slope + if y&SLOPE_FIX_MASK == 0 { + x += slopeFix + } + } +} + // Renders the mask to the canvas with even-odd fill. -func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - +func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { + var x, y uint32 + + minX := uint32(clipBound[0]) + maxX := uint32(clipBound[2]) + + minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT + maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT + //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := (*pixColor >> 8) & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x] + pixColor := (*uint32)(unsafe.Pointer(color)) + cs1 := *pixColor & 0xff00ff + cs2 := *pixColor >> 8 & 0xff00ff + + stride := uint32(img.Stride) + var mask SUBPIXEL_DATA + + for y = minY; y < maxY; y++ { + tp := img.Pix[y*stride:] + + mask = 0 + for x = minX; x <= maxX; x++ { + p := (*uint32)(unsafe.Pointer(&tp[x])) + mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x] // 8bits - alpha := uint32(coverageTable[mask]) + alpha := uint32(coverageTable[mask]) // 16bits //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) // 32bits //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff]) - + // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := (*p & 0xff00ff) * invAlpha - ct2 := ((*p >> 8) & 0xff00ff) * invAlpha - - ct1 = ((ct1 + cs1*alpha) >> SUBPIXEL_SHIFT) & 0xff00ff - ct2 = ((ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT)) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} - + invAlpha := SUBPIXEL_COUNT - alpha + + ct1 := *p & 0xff00ff * invAlpha + ct2 := *p >> 8 & 0xff00ff * invAlpha + + ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff + ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 + + *p = ct1 + ct2 + } + } +} + /* * Renders the polygon with non-zero winding fill. * param aTarget the target bitmap. * param aPolygon the polygon to render. * param aColor the color to be used for rendering. * param aTransformation the transformation matrix. - */ -func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT) - + */ +func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { + + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) + r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT) + // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addNonZeroEdge(&(edges[k])) - } - p += 16 - } - - r.fillNonZero(img, color, clipRect) -} - - + transform := [6]float64{ + tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], + tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], + tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], + tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], + tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], + tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], + } + + clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) + clipRect = intersect(clipRect, r.ClipBound) + + p := 0 + l := len(*polygon) / 2 + var edges [32]PolygonEdge + for p < l { + edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) + for k := 0; k < edgeCount; k++ { + r.addNonZeroEdge(&edges[k]) + } + p += 16 + } + + r.fillNonZero(img, color, clipRect) +} + + //! Renders the mask to the canvas with non-zero winding fill. -func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - +func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { + var x, y uint32 + + minX := uint32(clipBound[0]) + maxX := uint32(clipBound[2]) + + minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT + maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT + //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := (*pixColor >> 8) & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - var n uint32 - var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT - for n = 0; n < SUBPIXEL_COUNT; n++ { - values[n] = 0 - } - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x] - if temp != 0 { - var bit SUBPIXEL_DATA = 1 - for n = 0; n < SUBPIXEL_COUNT; n++ { - if (temp & bit) != 0 { - t := values[n] - values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n] - if (t == 0 || values[n] == 0 )&& t != values[n] { - mask ^= bit - } - } - bit <<= 1 - } - } - + pixColor := (*uint32)(unsafe.Pointer(color)) + cs1 := *pixColor & 0xff00ff + cs2 := *pixColor >> 8 & 0xff00ff + + stride := uint32(img.Stride) + var mask SUBPIXEL_DATA + var n uint32 + var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT + for n = 0; n < SUBPIXEL_COUNT; n++ { + values[n] = 0 + } + + for y = minY; y < maxY; y++ { + tp := img.Pix[y*stride:] + + mask = 0 + for x = minX; x <= maxX; x++ { + p := (*uint32)(unsafe.Pointer(&tp[x])) + temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x] + if temp != 0 { + var bit SUBPIXEL_DATA = 1 + for n = 0; n < SUBPIXEL_COUNT; n++ { + if temp&bit != 0 { + t := values[n] + values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n] + if (t == 0 || values[n] == 0) && t != values[n] { + mask ^= bit + } + } + bit <<= 1 + } + } + // 8bits - alpha := uint32(coverageTable[mask]) + alpha := uint32(coverageTable[mask]) // 16bits //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) // 32bits //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff]) - + // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := (*p & 0xff00ff) * invAlpha - ct2 := ((*p >> 8) & 0xff00ff) * invAlpha - - ct1 = ((ct1 + cs1*alpha) >> SUBPIXEL_SHIFT) & 0xff00ff - ct2 = ((ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT)) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} + invAlpha := uint32(SUBPIXEL_COUNT) - alpha + + ct1 := *p & 0xff00ff * invAlpha + ct2 := *p >> 8 & 0xff00ff * invAlpha + + ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff + ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 + + *p = ct1 + ct2 + } + } +} diff --git a/draw2d/raster/fillerV1/fillerAA.go b/draw2d/raster/fillerV1/fillerAA.go index 6a82549..9aace0a 100644 --- a/draw2d/raster/fillerV1/fillerAA.go +++ b/draw2d/raster/fillerV1/fillerAA.go @@ -1,302 +1,302 @@ // Copyright 2011 The draw2d Authors. All rights reserved. // created: 27/05/2011 by Laurent Le Goff -package raster - -import ( - "image" - "unsafe" -) - -const ( - SUBPIXEL_SHIFT = 3 - SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT -) - -var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8 - -type SUBPIXEL_DATA uint16 -type NON_ZERO_MASK_DATA_UNIT uint8 - -type Rasterizer8BitsSample struct { - MaskBuffer []SUBPIXEL_DATA - WindingBuffer []NON_ZERO_MASK_DATA_UNIT - - Width int - BufferWidth int - Height int - ClipBound [4]float64 - RemappingMatrix [6]float64 -} - +package raster + +import ( + "image" + "unsafe" +) + +const ( + SUBPIXEL_SHIFT = 3 + SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT +) + +var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8 + +type SUBPIXEL_DATA uint16 +type NON_ZERO_MASK_DATA_UNIT uint8 + +type Rasterizer8BitsSample struct { + MaskBuffer []SUBPIXEL_DATA + WindingBuffer []NON_ZERO_MASK_DATA_UNIT + + Width int + BufferWidth int + Height int + ClipBound [4]float64 + RemappingMatrix [6]float64 +} + /* width and height define the maximum output size for the filler. * The filler will output to larger bitmaps as well, but the output will * be cropped. - */ -func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample { - var r Rasterizer8BitsSample + */ +func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample { + var r Rasterizer8BitsSample // Scale the coordinates by SUBPIXEL_COUNT in vertical direction // The sampling point for the sub-pixel is at the top right corner. This // adjustment moves it to the pixel center. - r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT} - r.Width = width - r.Height = height + r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT} + r.Width = width + r.Height = height // The buffer used for filling needs to be one pixel wider than the bitmap. // This is because the end flag that turns the fill of is the first pixel // after the actually drawn edge. - r.BufferWidth = width + 1 - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT) - r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT) - return &r -} - -func clip(x, y, width, height, scale int) [4]float64 { - var clipBound [4]float64 - - offset := 0.99 / float64(scale) - - clipBound[0] = float64(x) + offset - clipBound[2] = float64(x+width) - offset - - clipBound[1] = float64(y * scale) - clipBound[3] = float64((y + height) * scale) - return clipBound -} - -func intersect(r1, r2 [4]float64) [4]float64 { - if r1[0] < r2[0] { - r1[0] = r2[0] - } - if r1[2] > r2[2] { - r1[2] = r2[2] - } - if r1[0] > r1[2] { - r1[0] = r1[2] - } - - if r1[1] < r2[1] { - r1[1] = r2[1] - } - if r1[3] > r2[3] { - r1[3] = r2[3] - } - if r1[1] > r1[3] { - r1[1] = r1[3] - } - return r1 -} - -func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { + r.BufferWidth = width + 1 + + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height) + r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT) + r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT) + return &r +} + +func clip(x, y, width, height, scale int) [4]float64 { + var clipBound [4]float64 + + offset := 0.99 / float64(scale) + + clipBound[0] = float64(x) + offset + clipBound[2] = float64(x+width) - offset + + clipBound[1] = float64(y * scale) + clipBound[3] = float64((y + height) * scale) + return clipBound +} + +func intersect(r1, r2 [4]float64) [4]float64 { + if r1[0] < r2[0] { + r1[0] = r2[0] + } + if r1[2] > r2[2] { + r1[2] = r2[2] + } + if r1[0] > r1[2] { + r1[0] = r1[2] + } + + if r1[1] < r2[1] { + r1[1] = r2[1] + } + if r1[3] > r2[3] { + r1[3] = r2[3] + } + if r1[1] > r1[3] { + r1[1] = r1[3] + } + return r1 +} + +func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { // memset 0 the mask buffer - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) + // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addEvenOddEdge(&(edges[k])) - } - p += 16 - } - - r.fillEvenOdd(img, color, clipRect) -} - + transform := [6]float64{ + tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], + tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], + tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], + tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], + tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], + tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], + } + + clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) + clipRect = intersect(clipRect, r.ClipBound) + p := 0 + l := len(*polygon) / 2 + var edges [32]PolygonEdge + for p < l { + edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) + for k := 0; k < edgeCount; k++ { + r.addEvenOddEdge(&edges[k]) + } + p += 16 + } + + r.fillEvenOdd(img, color, clipRect) +} + //! Adds an edge to be used with even-odd fill. -func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) { - x := edge.X - slope := edge.Slope - var ySub, mask SUBPIXEL_DATA - var xp, yLine int - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1)) - xp = (int)(x + SUBPIXEL_OFFSETS[ySub]) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask - x += slope - } -} - +func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) { + x := edge.X + slope := edge.Slope + var ySub, mask SUBPIXEL_DATA + var xp, yLine int + for y := edge.FirstLine; y <= edge.LastLine; y++ { + ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1)) + xp = int(x + SUBPIXEL_OFFSETS[ySub]) + mask = SUBPIXEL_DATA(1 << ySub) + yLine = y >> SUBPIXEL_SHIFT + r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask + x += slope + } +} + // Renders the mask to the canvas with even-odd fill. -func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - +func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { + var x, y uint32 + + minX := uint32(clipBound[0]) + maxX := uint32(clipBound[2]) + + minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT + maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT + //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := (*pixColor >> 8) & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x] + pixColor := (*uint32)(unsafe.Pointer(color)) + cs1 := *pixColor & 0xff00ff + cs2 := *pixColor >> 8 & 0xff00ff + + stride := uint32(img.Stride) + var mask SUBPIXEL_DATA + + for y = minY; y < maxY; y++ { + tp := img.Pix[y*stride:] + + mask = 0 + for x = minX; x <= maxX; x++ { + p := (*uint32)(unsafe.Pointer(&tp[x])) + mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x] // 8bits - alpha := uint32(coverageTable[mask]) + alpha := uint32(coverageTable[mask]) // 16bits //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) // 32bits //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff]) - + // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := (*p & 0xff00ff) * invAlpha - ct2 := ((*p >> 8) & 0xff00ff) * invAlpha - - ct1 = ((ct1 + cs1*alpha) >> SUBPIXEL_SHIFT) & 0xff00ff - ct2 = ((ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT)) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} - + invAlpha := uint32(SUBPIXEL_COUNT) - alpha + + ct1 := *p & 0xff00ff * invAlpha + ct2 := *p >> 8 & 0xff00ff * invAlpha + + ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff + ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 + + *p = ct1 + ct2 + } + } +} + /* * Renders the polygon with non-zero winding fill. * param aTarget the target bitmap. * param aPolygon the polygon to render. * param aColor the color to be used for rendering. * param aTransformation the transformation matrix. - */ -func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { - - r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) - r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT) - + */ +func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { + + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) + r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT) + // inline matrix multiplication - transform := [6]float64{ - tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], - tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], - tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], - tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], - tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], - tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], - } - - clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) - clipRect = intersect(clipRect, r.ClipBound) - - p := 0 - l := len(*polygon) / 2 - var edges [32]PolygonEdge - for p < l { - edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) - for k := 0; k < edgeCount; k++ { - r.addNonZeroEdge(&(edges[k])) - } - p += 16 - } - - r.fillNonZero(img, color, clipRect) -} - + transform := [6]float64{ + tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], + tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], + tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], + tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], + tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], + tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], + } + + clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) + clipRect = intersect(clipRect, r.ClipBound) + + p := 0 + l := len(*polygon) / 2 + var edges [32]PolygonEdge + for p < l { + edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) + for k := 0; k < edgeCount; k++ { + r.addNonZeroEdge(&edges[k]) + } + p += 16 + } + + r.fillNonZero(img, color, clipRect) +} + //! Adds an edge to be used with non-zero winding fill. -func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) { - x := edge.X - slope := edge.Slope - var ySub, mask SUBPIXEL_DATA - var xp, yLine int - winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding) - for y := edge.FirstLine; y <= edge.LastLine; y++ { - ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1)) - xp = (int)(x + SUBPIXEL_OFFSETS[ySub]) - mask = SUBPIXEL_DATA(1 << ySub) - yLine = y >> SUBPIXEL_SHIFT - r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask - r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding - x += slope - } -} - +func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) { + x := edge.X + slope := edge.Slope + var ySub, mask SUBPIXEL_DATA + var xp, yLine int + winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding) + for y := edge.FirstLine; y <= edge.LastLine; y++ { + ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1)) + xp = int(x + SUBPIXEL_OFFSETS[ySub]) + mask = SUBPIXEL_DATA(1 << ySub) + yLine = y >> SUBPIXEL_SHIFT + r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask + r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding + x += slope + } +} + //! Renders the mask to the canvas with non-zero winding fill. -func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { - var x, y uint32 - - minX := uint32(clipBound[0]) - maxX := uint32(clipBound[2]) - - minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT - maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT - +func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { + var x, y uint32 + + minX := uint32(clipBound[0]) + maxX := uint32(clipBound[2]) + + minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT + maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT + //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) - pixColor := (*uint32)(unsafe.Pointer(color)) - cs1 := *pixColor & 0xff00ff - cs2 := (*pixColor >> 8) & 0xff00ff - - stride := uint32(img.Stride) - var mask SUBPIXEL_DATA - var n uint32 - var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT - for n = 0; n < SUBPIXEL_COUNT; n++ { - values[n] = 0 - } - - for y = minY; y < maxY; y++ { - tp := img.Pix[y*stride:] - - mask = 0 - for x = minX; x <= maxX; x++ { - p := (*uint32)(unsafe.Pointer(&tp[x])) - temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x] - if temp != 0 { - var bit SUBPIXEL_DATA = 1 - for n = 0; n < SUBPIXEL_COUNT; n++ { - if (temp & bit) != 0 { - t := values[n] - values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n] - if (t == 0 || values[n] == 0 )&& t != values[n] { - mask ^= bit - } - } - bit <<= 1 - } - } - + pixColor := (*uint32)(unsafe.Pointer(color)) + cs1 := *pixColor & 0xff00ff + cs2 := *pixColor >> 8 & 0xff00ff + + stride := uint32(img.Stride) + var mask SUBPIXEL_DATA + var n uint32 + var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT + for n = 0; n < SUBPIXEL_COUNT; n++ { + values[n] = 0 + } + + for y = minY; y < maxY; y++ { + tp := img.Pix[y*stride:] + + mask = 0 + for x = minX; x <= maxX; x++ { + p := (*uint32)(unsafe.Pointer(&tp[x])) + temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x] + if temp != 0 { + var bit SUBPIXEL_DATA = 1 + for n = 0; n < SUBPIXEL_COUNT; n++ { + if temp&bit != 0 { + t := values[n] + values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n] + if (t == 0 || values[n] == 0) && t != values[n] { + mask ^= bit + } + } + bit <<= 1 + } + } + // 8bits - alpha := uint32(coverageTable[mask]) + alpha := uint32(coverageTable[mask]) // 16bits //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) // 32bits //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff]) - + // alpha is in range of 0 to SUBPIXEL_COUNT - invAlpha := uint32(SUBPIXEL_COUNT) - alpha - - ct1 := (*p & 0xff00ff) * invAlpha - ct2 := ((*p >> 8) & 0xff00ff) * invAlpha - - ct1 = ((ct1 + cs1*alpha) >> SUBPIXEL_SHIFT) & 0xff00ff - ct2 = ((ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT)) & 0xff00ff00 - - *p = ct1 + ct2 - } - } -} + invAlpha := uint32(SUBPIXEL_COUNT) - alpha + + ct1 := *p & 0xff00ff * invAlpha + ct2 := *p >> 8 & 0xff00ff * invAlpha + + ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff + ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 + + *p = ct1 + ct2 + } + } +} diff --git a/draw2d/raster/fillerV2/fillerAA.go b/draw2d/raster/fillerV2/fillerAA.go new file mode 100644 index 0000000..444e99b --- /dev/null +++ b/draw2d/raster/fillerV2/fillerAA.go @@ -0,0 +1,320 @@ +// Copyright 2011 The draw2d Authors. All rights reserved. +// created: 27/05/2011 by Laurent Le Goff +package raster + +import ( + "image" + "unsafe" +) + +const ( + SUBPIXEL_SHIFT = 5 + SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT +) + +var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_32_FIXED + +type SUBPIXEL_DATA uint32 +type NON_ZERO_MASK_DATA_UNIT uint8 + +type Rasterizer8BitsSample struct { + MaskBuffer []SUBPIXEL_DATA + WindingBuffer []NON_ZERO_MASK_DATA_UNIT + + Width int + BufferWidth int + Height int + ClipBound [4]float64 + RemappingMatrix [6]float64 +} + +/* width and height define the maximum output size for the filler. + * The filler will output to larger bitmaps as well, but the output will + * be cropped. + */ +func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample { + var r Rasterizer8BitsSample + // Scale the coordinates by SUBPIXEL_COUNT in vertical direction + // The sampling point for the sub-pixel is at the top right corner. This + // adjustment moves it to the pixel center. + r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT} + r.Width = width + r.Height = height + // The buffer used for filling needs to be one pixel wider than the bitmap. + // This is because the end flag that turns the fill of is the first pixel + // after the actually drawn edge. + r.BufferWidth = width + 1 + + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height) + r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT) + r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT) + return &r +} + +func clip(x, y, width, height, scale int) [4]float64 { + var clipBound [4]float64 + + offset := 0.99 / float64(scale) + + clipBound[0] = float64(x) + offset + clipBound[2] = float64(x+width) - offset + + clipBound[1] = float64(y * scale) + clipBound[3] = float64((y + height) * scale) + return clipBound +} + +func intersect(r1, r2 [4]float64) [4]float64 { + if r1[0] < r2[0] { + r1[0] = r2[0] + } + if r1[2] > r2[2] { + r1[2] = r2[2] + } + if r1[0] > r1[2] { + r1[0] = r1[2] + } + + if r1[1] < r2[1] { + r1[1] = r2[1] + } + if r1[3] > r2[3] { + r1[3] = r2[3] + } + if r1[1] > r1[3] { + r1[1] = r1[3] + } + return r1 +} + +func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { + // memset 0 the mask buffer + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) + + // inline matrix multiplication + transform := [6]float64{ + tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], + tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], + tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], + tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], + tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], + tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], + } + + clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) + clipRect = intersect(clipRect, r.ClipBound) + p := 0 + l := len(*polygon) / 2 + var edges [32]PolygonEdge + for p < l { + edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) + for k := 0; k < edgeCount; k++ { + r.addEvenOddEdge(&edges[k]) + } + p += 16 + } + + r.fillEvenOdd(img, color, clipRect) +} + +//! Adds an edge to be used with even-odd fill. +func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) { + x := Fix(edge.X * FIXED_FLOAT_COEF) + slope := Fix(edge.Slope * FIXED_FLOAT_COEF) + slopeFix := Fix(0) + if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP { + slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<> FIXED_SHIFT) + mask = SUBPIXEL_DATA(1 << ySub) + yLine = y >> SUBPIXEL_SHIFT + r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask + x += slope + if y&SLOPE_FIX_MASK == 0 { + x += slopeFix + } + } +} + +//! Adds an edge to be used with non-zero winding fill. +func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) { + x := Fix(edge.X * FIXED_FLOAT_COEF) + slope := Fix(edge.Slope * FIXED_FLOAT_COEF) + slopeFix := Fix(0) + if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP { + slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<> FIXED_SHIFT) + mask = SUBPIXEL_DATA(1 << ySub) + yLine = y >> SUBPIXEL_SHIFT + r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask + r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding + x += slope + if y&SLOPE_FIX_MASK == 0 { + x += slopeFix + } + } +} + +// Renders the mask to the canvas with even-odd fill. +func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { + var x, y uint32 + + minX := uint32(clipBound[0]) + maxX := uint32(clipBound[2]) + + minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT + maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT + + //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) + pixColor := (*uint32)(unsafe.Pointer(color)) + cs1 := *pixColor & 0xff00ff + cs2 := *pixColor >> 8 & 0xff00ff + + stride := uint32(img.Stride) + var mask SUBPIXEL_DATA + + for y = minY; y < maxY; y++ { + tp := img.Pix[y*stride:] + + mask = 0 + for x = minX; x <= maxX; x++ { + p := (*uint32)(unsafe.Pointer(&tp[x])) + mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x] + // 8bits + //alpha := uint32(coverageTable[mask]) + // 16bits + //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) + // 32bits + alpha := uint32(coverageTable[mask&0xff] + coverageTable[mask>>8&0xff] + coverageTable[mask>>16&0xff] + coverageTable[mask>>24&0xff]) + + // alpha is in range of 0 to SUBPIXEL_COUNT + invAlpha := uint32(SUBPIXEL_COUNT) - alpha + + ct1 := *p & 0xff00ff * invAlpha + ct2 := *p >> 8 & 0xff00ff * invAlpha + + ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff + ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 + + *p = ct1 + ct2 + } + } +} + +/* + * Renders the polygon with non-zero winding fill. + * param aTarget the target bitmap. + * param aPolygon the polygon to render. + * param aColor the color to be used for rendering. + * param aTransformation the transformation matrix. + */ +func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *image.RGBAColor, polygon *Polygon, tr [6]float64) { + + r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height) + r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT) + + // inline matrix multiplication + transform := [6]float64{ + tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2], + tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1], + tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2], + tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1], + tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4], + tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5], + } + + clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT) + clipRect = intersect(clipRect, r.ClipBound) + + p := 0 + l := len(*polygon) / 2 + var edges [32]PolygonEdge + for p < l { + edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect) + for k := 0; k < edgeCount; k++ { + r.addNonZeroEdge(&edges[k]) + } + p += 16 + } + + r.fillNonZero(img, color, clipRect) +} + + +//! Renders the mask to the canvas with non-zero winding fill. +func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *image.RGBAColor, clipBound [4]float64) { + var x, y uint32 + + minX := uint32(clipBound[0]) + maxX := uint32(clipBound[2]) + + minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT + maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT + + //pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A) + pixColor := (*uint32)(unsafe.Pointer(color)) + cs1 := *pixColor & 0xff00ff + cs2 := *pixColor >> 8 & 0xff00ff + + stride := uint32(img.Stride) + var mask SUBPIXEL_DATA + var n uint32 + var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT + for n = 0; n < SUBPIXEL_COUNT; n++ { + values[n] = 0 + } + + for y = minY; y < maxY; y++ { + tp := img.Pix[y*stride:] + + mask = 0 + for x = minX; x <= maxX; x++ { + p := (*uint32)(unsafe.Pointer(&tp[x])) + temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x] + if temp != 0 { + var bit SUBPIXEL_DATA = 1 + for n = 0; n < SUBPIXEL_COUNT; n++ { + if temp&bit != 0 { + t := values[n] + values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n] + if (t == 0 || values[n] == 0) && t != values[n] { + mask ^= bit + } + } + bit <<= 1 + } + } + + // 8bits + //alpha := uint32(coverageTable[mask]) + // 16bits + //alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff]) + // 32bits + alpha := uint32(coverageTable[mask&0xff] + coverageTable[mask>>8&0xff] + coverageTable[mask>>16&0xff] + coverageTable[mask>>24&0xff]) + + // alpha is in range of 0 to SUBPIXEL_COUNT + invAlpha := uint32(SUBPIXEL_COUNT) - alpha + + ct1 := *p & 0xff00ff * invAlpha + ct2 := *p >> 8 & 0xff00ff * invAlpha + + ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff + ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00 + + *p = ct1 + ct2 + } + } +} diff --git a/draw2d/raster/fixed_point.go b/draw2d/raster/fixed_point.go index 8ed8d38..14b8419 100644 --- a/draw2d/raster/fixed_point.go +++ b/draw2d/raster/fixed_point.go @@ -3,7 +3,7 @@ package raster type Fix int32 const ( - FIXED_SHIFT = 16 + FIXED_SHIFT = 16 FIXED_FLOAT_COEF = 1 << FIXED_SHIFT ) @@ -12,6 +12,6 @@ const ( */ const ( SLOPE_FIX_SHIFT = 8 - SLOPE_FIX_STEP = 1 << SLOPE_FIX_SHIFT - SLOPE_FIX_MASK = SLOPE_FIX_STEP - 1 -) \ No newline at end of file + SLOPE_FIX_STEP = 1 << SLOPE_FIX_SHIFT + SLOPE_FIX_MASK = SLOPE_FIX_STEP - 1 +) diff --git a/draw2d/raster/polygon.go b/draw2d/raster/polygon.go index 4044d56..0253d58 100644 --- a/draw2d/raster/polygon.go +++ b/draw2d/raster/polygon.go @@ -2,10 +2,6 @@ // created: 27/05/2011 by Laurent Le Goff package raster -import ( - "math" -) - const ( POLYGON_CLIP_NONE = iota POLYGON_CLIP_LEFT @@ -23,6 +19,15 @@ type PolygonEdge struct { Winding int16 } +//! A more optimized representation of a polygon edge. +type PolygonScanEdge struct { + FirstLine, LastLine int + Winding int16 + X Fix + Slope Fix + SlopeFix Fix + NextEdge *PolygonScanEdge +} //! Calculates the edges of the polygon with transformation and clipping to edges array. /*! \param startIndex the index for the first vertex. @@ -34,7 +39,7 @@ type PolygonEdge struct { */ func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [6]float64, clipBound [4]float64) int { startIndex = startIndex * 2 - endIndex := startIndex + (vertexCount * 2) + endIndex := startIndex + vertexCount*2 if endIndex > len(p) { endIndex = len(p) } @@ -85,20 +90,20 @@ func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [ clipUnion = prevClipFlags & clipFlags // Skip all edges that are either completely outside at the top or at the bottom. - if (clipUnion & (POLYGON_CLIP_TOP | POLYGON_CLIP_BOTTOM)) == 0 { - if (clipUnion & POLYGON_CLIP_RIGHT) != 0 { + if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 { + if clipUnion&POLYGON_CLIP_RIGHT != 0 { // Both clip to right, edge is a vertical line on the right side - if getVerticalEdge(prevY, y, clipBound[2], &(edges[edgeCount]), clipBound) { + if getVerticalEdge(prevY, y, clipBound[2], &edges[edgeCount], clipBound) { edgeCount++ } - } else if (clipUnion & POLYGON_CLIP_LEFT) != 0 { + } else if clipUnion&POLYGON_CLIP_LEFT != 0 { // Both clip to left, edge is a vertical line on the left side - if getVerticalEdge(prevY, y, clipBound[0], &(edges[edgeCount]), clipBound) { + if getVerticalEdge(prevY, y, clipBound[0], &edges[edgeCount], clipBound) { edgeCount++ } - } else if (clipSum & (POLYGON_CLIP_RIGHT | POLYGON_CLIP_LEFT)) == 0 { + } else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 { // No clipping in the horizontal direction - if getEdge(prevX, prevY, x, y, &(edges[edgeCount]), clipBound) { + if getEdge(prevX, prevY, x, y, &edges[edgeCount], clipBound) { edgeCount++ } } else { @@ -116,7 +121,7 @@ func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [ slope := (yright - yleft) / (xright - xleft) - if (clipSum & POLYGON_CLIP_RIGHT) != 0 { + if clipSum&POLYGON_CLIP_RIGHT != 0 { // calculate new position for the right vertex oldY = yright maxX = clipBound[2] @@ -125,13 +130,13 @@ func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [ xright = maxX // add vertical edge for the overflowing part - if getVerticalEdge(yright, oldY, maxX, &(edges[edgeCount]), clipBound) { + if getVerticalEdge(yright, oldY, maxX, &edges[edgeCount], clipBound) { edges[edgeCount].Winding *= swapWinding edgeCount++ } } - if (clipSum & POLYGON_CLIP_LEFT) != 0 { + if clipSum&POLYGON_CLIP_LEFT != 0 { // calculate new position for the left vertex oldY = yleft minX = clipBound[0] @@ -140,13 +145,13 @@ func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [ xleft = minX // add vertical edge for the overflowing part - if getVerticalEdge(oldY, yleft, minX, &(edges[edgeCount]), clipBound) { + if getVerticalEdge(oldY, yleft, minX, &edges[edgeCount], clipBound) { edges[edgeCount].Winding *= swapWinding edgeCount++ } } - if getEdge(xleft, yleft, xright, yright, &(edges[edgeCount]), clipBound) { + if getEdge(xleft, yleft, xright, yright, &edges[edgeCount], clipBound) { edges[edgeCount].Winding *= swapWinding edgeCount++ } @@ -192,8 +197,8 @@ func getEdge(x0, y0, x1, y1 float64, edge *PolygonEdge, clipBound [4]float64) bo // also round to zero. The problems in this range can be avoided by // adding one to the values before conversion and subtracting after it. - firstLine := int(math.Floor(startY)) + 1 - lastLine := int(math.Floor(endY)) + firstLine := int(startY + 1) + lastLine := int(endY+1) - 1 minClip := int(clipBound[1]) maxClip := int(clipBound[3]) @@ -241,8 +246,8 @@ func getVerticalEdge(startY, endY, x float64, edge *PolygonEdge, clipBound [4]fl winding = -1 } - firstLine := int(math.Floor(start)) + 1 - lastLine := int(math.Floor(end)) + firstLine := int(start + 1) + lastLine := int(end+1) - 1 minClip := int(clipBound[1]) maxClip := int(clipBound[3]) @@ -271,3 +276,309 @@ func getVerticalEdge(startY, endY, x float64, edge *PolygonEdge, clipBound [4]fl return true } + + +type VertexData struct { + X, Y float64 + ClipFlags int + Line int +} +//! Calculates the edges of the polygon with transformation and clipping to edges array. +/*! Note that this may return upto three times the amount of edges that the polygon has vertices, + * in the unlucky case where both left and right side get clipped for all edges. + * \param edges the array for result edges. This should be able to contain 2*aVertexCount edges. + * \param aTransformation the transformation matrix for the polygon. + * \param aClipRectangle the clip rectangle. + * \return the amount of edges in the result. + */ +func (p Polygon) getScanEdges(edges []PolygonScanEdge, tr [6]float64, clipBound [4]float64) int { + var n int + vertexData := make([]VertexData, len(p)/2+1) + for n = 0; n < len(vertexData)-1; n = n + 1 { + k := n * 2 + vertexData[n].X = p[k]*tr[0] + p[k+1]*tr[2] + tr[4] + vertexData[n].Y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5] + // Calculate clip flags for all vertices. + vertexData[n].ClipFlags = POLYGON_CLIP_NONE + if vertexData[n].X < clipBound[0] { + vertexData[n].ClipFlags |= POLYGON_CLIP_LEFT + } else if vertexData[n].X >= clipBound[2] { + vertexData[n].ClipFlags |= POLYGON_CLIP_RIGHT + } + if vertexData[n].Y < clipBound[1] { + vertexData[n].ClipFlags |= POLYGON_CLIP_TOP + } else if vertexData[n].Y >= clipBound[3] { + vertexData[n].ClipFlags |= POLYGON_CLIP_BOTTOM + } + + // Calculate line of the vertex. If the vertex is clipped by top or bottom, the line + // is determined by the clip rectangle. + if vertexData[n].ClipFlags&POLYGON_CLIP_TOP != 0 { + vertexData[n].Line = int(clipBound[1]) + } else if vertexData[n].ClipFlags&POLYGON_CLIP_BOTTOM != 0 { + vertexData[n].Line = int(clipBound[3] - 1) + } else { + vertexData[n].Line = int(vertexData[n].Y+1) - 1 + } + } + + // Copy the data from 0 to the last entry to make the data to loop. + vertexData[len(vertexData)-1] = vertexData[0] + + // Transform the first vertex; store. + // Process mVertexCount - 1 times, next is n+1 + // copy the first vertex to + // Process 1 time, next is n + + edgeCount := 0 + for n = 0; n < len(vertexData)-1; n++ { + clipSum := vertexData[n].ClipFlags | vertexData[n+1].ClipFlags + clipUnion := vertexData[n].ClipFlags & vertexData[n+1].ClipFlags + + if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 && + vertexData[n].Line != vertexData[n+1].Line { + var startIndex, endIndex int + var winding int16 + if vertexData[n].Y < vertexData[n+1].Y { + startIndex = n + endIndex = n + 1 + winding = 1 + } else { + startIndex = n + 1 + endIndex = n + winding = -1 + } + + firstLine := vertexData[startIndex].Line + 1 + lastLine := vertexData[endIndex].Line + + if clipUnion&POLYGON_CLIP_RIGHT != 0 { + // Both clip to right, edge is a vertical line on the right side + edges[edgeCount].FirstLine = firstLine + edges[edgeCount].LastLine = lastLine + edges[edgeCount].Winding = winding + edges[edgeCount].X = Fix(clipBound[2] * FIXED_FLOAT_COEF) + edges[edgeCount].Slope = 0 + edges[edgeCount].SlopeFix = 0 + + edgeCount++ + } else if clipUnion&POLYGON_CLIP_LEFT != 0 { + // Both clip to left, edge is a vertical line on the left side + edges[edgeCount].FirstLine = firstLine + edges[edgeCount].LastLine = lastLine + edges[edgeCount].Winding = winding + edges[edgeCount].X = Fix(clipBound[0] * FIXED_FLOAT_COEF) + edges[edgeCount].Slope = 0 + edges[edgeCount].SlopeFix = 0 + + edgeCount++ + } else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 { + // No clipping in the horizontal direction + slope := (vertexData[endIndex].X - + vertexData[startIndex].X) / + (vertexData[endIndex].Y - + vertexData[startIndex].Y) + + // If there is vertical clip (for the top) it will be processed here. The calculation + // should be done for all non-clipping edges as well to determine the accurate position + // where the edge crosses the first scanline. + startx := vertexData[startIndex].X + + (float64(firstLine)-vertexData[startIndex].Y)*slope + + edges[edgeCount].FirstLine = firstLine + edges[edgeCount].LastLine = lastLine + edges[edgeCount].Winding = winding + edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF) + edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF) + + if lastLine-firstLine >= SLOPE_FIX_STEP { + edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - + edges[edgeCount].Slope< clipBound[3] { + clipVertices[p].ClipFlags = POLYGON_CLIP_BOTTOM + clipVertices[p].Line = int(clipBound[3] - 1) + } else { + clipVertices[p].ClipFlags = 0 + clipVertices[p].Line = int(clipVertices[p].Y+1) - 1 + } + } else { + clipVertices[p].ClipFlags = 0 + clipVertices[p].Line = int(clipVertices[p].Y+1) - 1 + } + } + } + + // Now there are three or four vertices, in the top-to-bottom order of start, clip0, clip1, + // end. What kind of edges are required for connecting these can be determined from the + // clip flags. + // -if clip vertex has horizontal clip flags, it doesn't exist. No edge is generated. + // -if start vertex or end vertex has horizontal clip flag, the edge to/from the clip vertex is vertical + // -if the line of two vertices is the same, the edge is not generated, since the edge doesn't + // cross any scanlines. + + // The alternative patterns are: + // start - clip0 - clip1 - end + // start - clip0 - end + // start - clip1 - end + + var topClipIndex, bottomClipIndex int + if (clipVertices[0].ClipFlags|clipVertices[1].ClipFlags)& + (POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) == 0 { + // Both sides are clipped, the order is start-clip0-clip1-end + topClipIndex = 0 + bottomClipIndex = 1 + + // Add the edge from clip0 to clip1 + // Check that the line is different for the vertices. + if clipVertices[0].Line != clipVertices[1].Line { + firstClipLine := clipVertices[0].Line + 1 + + startx := vertexData[startIndex].X + + (float64(firstClipLine)-vertexData[startIndex].Y)*slope + + edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF) + edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF) + edges[edgeCount].FirstLine = firstClipLine + edges[edgeCount].LastLine = clipVertices[1].Line + edges[edgeCount].Winding = winding + + if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP { + edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - + edges[edgeCount].Slope<= SLOPE_FIX_STEP { + edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - + edges[edgeCount].Slope<= SLOPE_FIX_STEP { + edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - + edges[edgeCount].Slope< cap(p.points) { - points := make([]float64, len(p.points)+2, len(p.points)+32) - copy(points, p.points) - p.points = points - } else { - p.points = p.points[0 : len(p.points)+2] - } - p.points[len(p.points)-2] = x - p.points[len(p.points)-1] = y -} - -func TestFreetype(t *testing.T) { - var p Path - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := image.RGBAColor{0, 0, 0, 0xff} - - img := image.NewRGBA(200, 200) - rasterizer := raster.NewRasterizer(200, 200) - rasterizer.UseNonZeroWinding = false - rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) - for j := 0; j < len(poly); j = j + 2 { - rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) - } - painter := raster.NewRGBAPainter(img) - painter.SetColor(color) - rasterizer.Rasterize(painter) - - savepng("_testFreetype.png", img) -} - -func TestRasterizer(t *testing.T) { - img := image.NewRGBA(200, 200) - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := image.RGBAColor{0, 0, 0, 0xff} - tr := [6]float64{1, 0, 0, 1, 0, 0} - r := NewRasterizer8BitsSample(200, 200) +package raster + +import ( + "testing" + "log" + "image" + "os" + "bufio" + "image/png" + "draw2d.googlecode.com/hg/draw2d/curve" + "freetype-go.googlecode.com/hg/freetype/raster" +) + +var flattening_threshold float64 = 0.5 + +func savepng(filePath string, m image.Image) { + f, err := os.Create(filePath) + if err != nil { + log.Println(err) + os.Exit(1) + } + defer f.Close() + b := bufio.NewWriter(f) + err = png.Encode(b, m) + if err != nil { + log.Println(err) + os.Exit(1) + } + err = b.Flush() + if err != nil { + log.Println(err) + os.Exit(1) + } +} + +type Path struct { + points []float64 +} + +func (p *Path) LineTo(x, y float64) { + if len(p.points)+2 > cap(p.points) { + points := make([]float64, len(p.points)+2, len(p.points)+32) + copy(points, p.points) + p.points = points + } else { + p.points = p.points[0 : len(p.points)+2] + } + p.points[len(p.points)-2] = x + p.points[len(p.points)-1] = y +} + +func TestFreetype(t *testing.T) { + var p Path + p.LineTo(10, 190) + c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} + c.Segment(&p, flattening_threshold) + poly := Polygon(p.points) + color := image.RGBAColor{0, 0, 0, 0xff} + + img := image.NewRGBA(200, 200) + rasterizer := raster.NewRasterizer(200, 200) + rasterizer.UseNonZeroWinding = false + rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) + for j := 0; j < len(poly); j = j + 2 { + rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) + } + painter := raster.NewRGBAPainter(img) + painter.SetColor(color) + rasterizer.Rasterize(painter) + + savepng("_testFreetype.png", img) +} + +func TestFreetypeNonZeroWinding(t *testing.T) { + var p Path + p.LineTo(10, 190) + c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} + c.Segment(&p, flattening_threshold) + poly := Polygon(p.points) + color := image.RGBAColor{0, 0, 0, 0xff} + + img := image.NewRGBA(200, 200) + rasterizer := raster.NewRasterizer(200, 200) + rasterizer.UseNonZeroWinding = true + rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) + for j := 0; j < len(poly); j = j + 2 { + rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) + } + painter := raster.NewRGBAPainter(img) + painter.SetColor(color) + rasterizer.Rasterize(painter) + + savepng("_testFreetypeNonZeroWinding.png", img) +} + +func TestRasterizer(t *testing.T) { + img := image.NewRGBA(200, 200) + var p Path + p.LineTo(10, 190) + c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} + c.Segment(&p, flattening_threshold) + poly := Polygon(p.points) + color := image.RGBAColor{0, 0, 0, 0xff} + tr := [6]float64{1, 0, 0, 1, 0, 0} + r := NewRasterizer8BitsSample(200, 200) //PolylineBresenham(img, image.Black, poly...) - - - r.RenderEvenOdd(img, &color, &poly, tr) - savepng("_testRasterizer.png", img) -} - -func TestRasterizerNonZeroWinding(t *testing.T) { - img := image.NewRGBA(200, 200) - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := image.RGBAColor{0, 0, 0, 0xff} - tr := [6]float64{1, 0, 0, 1, 0, 0} - r := NewRasterizer8BitsSample(200, 200) + + + r.RenderEvenOdd(img, &color, &poly, tr) + savepng("_testRasterizer.png", img) +} + +func TestRasterizerNonZeroWinding(t *testing.T) { + img := image.NewRGBA(200, 200) + var p Path + p.LineTo(10, 190) + c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} + c.Segment(&p, flattening_threshold) + poly := Polygon(p.points) + color := image.RGBAColor{0, 0, 0, 0xff} + tr := [6]float64{1, 0, 0, 1, 0, 0} + r := NewRasterizer8BitsSample(200, 200) //PolylineBresenham(img, image.Black, poly...) - - - r.RenderNonZeroWinding(img, &color, &poly, tr) - savepng("_testRasterizerNonZeroWinding.png", img) -} - -func BenchmarkFreetype(b *testing.B) { - var p Path - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := image.RGBAColor{0, 0, 0, 0xff} - - for i := 0; i < b.N; i++ { - img := image.NewRGBA(200, 200) - rasterizer := raster.NewRasterizer(200, 200) - rasterizer.UseNonZeroWinding = false - rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) - for j := 0; j < len(poly); j = j + 2 { - rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) - } - painter := raster.NewRGBAPainter(img) - painter.SetColor(color) - rasterizer.Rasterize(painter) - } -} - -func BenchmarkRasterizerNonZeroWinding(b *testing.B) { - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := image.RGBAColor{0, 0, 0, 0xff} - tr := [6]float64{1, 0, 0, 1, 0, 0} - for i := 0; i < b.N; i++ { - img := image.NewRGBA(200, 200) - rasterizer := NewRasterizer8BitsSample(200, 200) - rasterizer.RenderNonZeroWinding(img, &color, &poly, tr) - } -} - -func BenchmarkRasterizer(b *testing.B) { - var p Path - p.LineTo(10, 190) - c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} - c.Segment(&p, flattening_threshold) - poly := Polygon(p.points) - color := image.RGBAColor{0, 0, 0, 0xff} - tr := [6]float64{1, 0, 0, 1, 0, 0} - for i := 0; i < b.N; i++ { - img := image.NewRGBA(200, 200) - rasterizer := NewRasterizer8BitsSample(200, 200) - rasterizer.RenderEvenOdd(img, &color, &poly, tr) - } -} - + + + r.RenderNonZeroWinding(img, &color, &poly, tr) + savepng("_testRasterizerNonZeroWinding.png", img) +} + +func BenchmarkFreetype(b *testing.B) { + var p Path + p.LineTo(10, 190) + c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} + c.Segment(&p, flattening_threshold) + poly := Polygon(p.points) + color := image.RGBAColor{0, 0, 0, 0xff} + + for i := 0; i < b.N; i++ { + img := image.NewRGBA(200, 200) + rasterizer := raster.NewRasterizer(200, 200) + rasterizer.UseNonZeroWinding = false + rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) + for j := 0; j < len(poly); j = j + 2 { + rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) + } + painter := raster.NewRGBAPainter(img) + painter.SetColor(color) + rasterizer.Rasterize(painter) + } +} +func BenchmarkFreetypeNonZeroWinding(b *testing.B) { + var p Path + p.LineTo(10, 190) + c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} + c.Segment(&p, flattening_threshold) + poly := Polygon(p.points) + color := image.RGBAColor{0, 0, 0, 0xff} + + for i := 0; i < b.N; i++ { + img := image.NewRGBA(200, 200) + rasterizer := raster.NewRasterizer(200, 200) + rasterizer.UseNonZeroWinding = true + rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)}) + for j := 0; j < len(poly); j = j + 2 { + rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)}) + } + painter := raster.NewRGBAPainter(img) + painter.SetColor(color) + rasterizer.Rasterize(painter) + } +} + +func BenchmarkRasterizerNonZeroWinding(b *testing.B) { + var p Path + p.LineTo(10, 190) + c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} + c.Segment(&p, flattening_threshold) + poly := Polygon(p.points) + color := image.RGBAColor{0, 0, 0, 0xff} + tr := [6]float64{1, 0, 0, 1, 0, 0} + for i := 0; i < b.N; i++ { + img := image.NewRGBA(200, 200) + rasterizer := NewRasterizer8BitsSample(200, 200) + rasterizer.RenderNonZeroWinding(img, &color, &poly, tr) + } +} + +func BenchmarkRasterizer(b *testing.B) { + var p Path + p.LineTo(10, 190) + c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190} + c.Segment(&p, flattening_threshold) + poly := Polygon(p.points) + color := image.RGBAColor{0, 0, 0, 0xff} + tr := [6]float64{1, 0, 0, 1, 0, 0} + for i := 0; i < b.N; i++ { + img := image.NewRGBA(200, 200) + rasterizer := NewRasterizer8BitsSample(200, 200) + rasterizer.RenderEvenOdd(img, &color, &poly, tr) + } +}