fill with winding rule
This commit is contained in:
parent
25f7be3323
commit
eaa2454bb3
5 changed files with 606 additions and 449 deletions
|
@ -8,11 +8,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SUBPIXEL_SHIFT = 3
|
SUBPIXEL_SHIFT = 4
|
||||||
SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT
|
SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT
|
||||||
)
|
)
|
||||||
|
|
||||||
var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8
|
var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_16
|
||||||
|
|
||||||
type SUBPIXEL_DATA uint16
|
type SUBPIXEL_DATA uint16
|
||||||
type NON_ZERO_MASK_DATA_UNIT uint8
|
type NON_ZERO_MASK_DATA_UNIT uint8
|
||||||
|
@ -105,13 +105,13 @@ func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *image.RGBA
|
||||||
clipRect = intersect(clipRect, r.ClipBound)
|
clipRect = intersect(clipRect, r.ClipBound)
|
||||||
p := 0
|
p := 0
|
||||||
l := len(*polygon) / 2
|
l := len(*polygon) / 2
|
||||||
|
var edges [32]PolygonEdge
|
||||||
for p < l {
|
for p < l {
|
||||||
var edges [20]PolygonEdge
|
edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
|
||||||
edgeCount := polygon.getEdges(p, 10, edges[:], transform, clipRect)
|
|
||||||
for k := 0; k < edgeCount; k++ {
|
for k := 0; k < edgeCount; k++ {
|
||||||
r.addEvenOddEdge(&(edges[k]))
|
r.addEvenOddEdge(&(edges[k]))
|
||||||
}
|
}
|
||||||
p += 10
|
p += 16
|
||||||
}
|
}
|
||||||
|
|
||||||
r.fillEvenOdd(img, color, clipRect)
|
r.fillEvenOdd(img, color, clipRect)
|
||||||
|
@ -121,11 +121,13 @@ func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *image.RGBA
|
||||||
func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) {
|
func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) {
|
||||||
x := edge.X
|
x := edge.X
|
||||||
slope := edge.Slope
|
slope := edge.Slope
|
||||||
|
var ySub, mask SUBPIXEL_DATA
|
||||||
|
var xp, yLine int
|
||||||
for y := edge.FirstLine; y <= edge.LastLine; y++ {
|
for y := edge.FirstLine; y <= edge.LastLine; y++ {
|
||||||
ySub := SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1))
|
ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1))
|
||||||
xp := (int)(x + SUBPIXEL_OFFSETS[ySub])
|
xp = (int)(x + SUBPIXEL_OFFSETS[ySub])
|
||||||
mask := SUBPIXEL_DATA(1 << ySub)
|
mask = SUBPIXEL_DATA(1 << ySub)
|
||||||
yLine := y >> SUBPIXEL_SHIFT
|
yLine = y >> SUBPIXEL_SHIFT
|
||||||
r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask
|
r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask
|
||||||
x += slope
|
x += slope
|
||||||
}
|
}
|
||||||
|
@ -157,9 +159,131 @@ func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *image.RGBACo
|
||||||
p := (*uint32)(unsafe.Pointer(&tp[x]))
|
p := (*uint32)(unsafe.Pointer(&tp[x]))
|
||||||
mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x]
|
mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x]
|
||||||
// 8bits
|
// 8bits
|
||||||
alpha := uint32(coverageTable[mask])
|
//alpha := uint32(coverageTable[mask])
|
||||||
// 16bits
|
// 16bits
|
||||||
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
//! 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! 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
|
// 32bits
|
||||||
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
|
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
|
||||||
|
|
||||||
|
|
|
@ -49,23 +49,6 @@ func (p *Path) LineTo(x, y float64) {
|
||||||
p.points[len(p.points)-1] = y
|
p.points[len(p.points)-1] = y
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRasterizer8BitsSample(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("_testRasterizer8BitsSample.png", img)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFreetype(t *testing.T) {
|
func TestFreetype(t *testing.T) {
|
||||||
var p Path
|
var p Path
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
||||||
|
@ -87,7 +70,8 @@ func TestFreetype(t *testing.T) {
|
||||||
savepng("_testFreetype.png", img)
|
savepng("_testFreetype.png", img)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkRasterizer8BitsSample(b *testing.B) {
|
func TestRasterizer(t *testing.T) {
|
||||||
|
img := image.NewRGBA(200, 200)
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
||||||
|
@ -95,11 +79,29 @@ func BenchmarkRasterizer8BitsSample(b *testing.B) {
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := image.RGBAColor{0, 0, 0, 0xff}
|
color := image.RGBAColor{0, 0, 0, 0xff}
|
||||||
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
||||||
for i := 0; i < b.N; i++ {
|
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)
|
img := image.NewRGBA(200, 200)
|
||||||
rasterizer := NewRasterizer8BitsSample(200, 200)
|
var p Path
|
||||||
rasterizer.RenderEvenOdd(img, &color, &poly, tr)
|
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) {
|
func BenchmarkFreetype(b *testing.B) {
|
||||||
|
@ -122,3 +124,34 @@ func BenchmarkFreetype(b *testing.B) {
|
||||||
rasterizer.Rasterize(painter)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue