fill with winding rule

This commit is contained in:
Laurent Le Goff 2011-05-27 18:19:42 +02:00
parent 25f7be3323
commit eaa2454bb3
5 changed files with 606 additions and 449 deletions

View file

@ -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])

View file

@ -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)
img := image.NewRGBA(200, 200) //PolylineBresenham(img, image.Black, poly...)
rasterizer := NewRasterizer8BitsSample(200, 200)
rasterizer.RenderEvenOdd(img, &color, &poly, tr)
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) { 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)
}
}