Implement Edge/Flag antialias filler
This commit is contained in:
parent
c763e1614f
commit
25f7be3323
11 changed files with 1709 additions and 994 deletions
|
@ -10,9 +10,8 @@ const (
|
||||||
CurveRecursionLimit = 32
|
CurveRecursionLimit = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
type CubicCurveFloat64 struct {
|
// X1, Y1, X2, Y2, X3, Y3, X4, Y4 float64
|
||||||
X1, Y1, X2, Y2, X3, Y3, X4, Y4 float64
|
type CubicCurveFloat64 [8]float64
|
||||||
}
|
|
||||||
|
|
||||||
type LineTracer interface {
|
type LineTracer interface {
|
||||||
LineTo(x, y float64)
|
LineTo(x, y float64)
|
||||||
|
@ -21,21 +20,21 @@ type LineTracer interface {
|
||||||
func (c *CubicCurveFloat64) Subdivide(c1, c2 *CubicCurveFloat64) (x23, y23 float64) {
|
func (c *CubicCurveFloat64) Subdivide(c1, c2 *CubicCurveFloat64) (x23, y23 float64) {
|
||||||
// Calculate all the mid-points of the line segments
|
// Calculate all the mid-points of the line segments
|
||||||
//----------------------
|
//----------------------
|
||||||
c1.X1, c1.Y1 = c.X1, c.Y1
|
c1[0], c1[1] = c[0], c[1]
|
||||||
c2.X4, c2.Y4 = c.X4, c.Y4
|
c2[6], c2[7] = c[6], c[7]
|
||||||
c1.X2 = (c.X1 + c.X2) / 2
|
c1[2] = (c[0] + c[2]) / 2
|
||||||
c1.Y2 = (c.Y1 + c.Y2) / 2
|
c1[3] = (c[1] + c[3]) / 2
|
||||||
x23 = (c.X2 + c.X3) / 2
|
x23 = (c[2] + c[4]) / 2
|
||||||
y23 = (c.Y2 + c.Y3) / 2
|
y23 = (c[3] + c[5]) / 2
|
||||||
c2.X3 = (c.X3 + c.X4) / 2
|
c2[4] = (c[4] + c[6]) / 2
|
||||||
c2.Y3 = (c.Y3 + c.Y4) / 2
|
c2[5] = (c[5] + c[7]) / 2
|
||||||
c1.X3 = (c1.X2 + x23) / 2
|
c1[4] = (c1[2] + x23) / 2
|
||||||
c1.Y3 = (c1.Y2 + y23) / 2
|
c1[5] = (c1[3] + y23) / 2
|
||||||
c2.X2 = (x23 + c2.X3) / 2
|
c2[2] = (x23 + c2[4]) / 2
|
||||||
c2.Y2 = (y23 + c2.Y3) / 2
|
c2[3] = (y23 + c2[5]) / 2
|
||||||
c1.X4 = (c1.X3 + c2.X2) / 2
|
c1[6] = (c1[4] + c2[2]) / 2
|
||||||
c1.Y4 = (c1.Y3 + c2.Y2) / 2
|
c1[7] = (c1[5] + c2[3]) / 2
|
||||||
c2.X1, c2.Y1 = c1.X4, c1.Y4
|
c2[0], c2[1] = c1[6], c1[7]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,14 +49,14 @@ func (curve *CubicCurveFloat64) Segment(t LineTracer, flattening_threshold float
|
||||||
|
|
||||||
for i >= 0 {
|
for i >= 0 {
|
||||||
c = &curves[i]
|
c = &curves[i]
|
||||||
dx = c.X4 - c.X1
|
dx = c[6] - c[0]
|
||||||
dy = c.Y4 - c.Y1
|
dy = c[7] - c[1]
|
||||||
|
|
||||||
d2 = math.Fabs(((c.X2-c.X4)*dy - (c.Y2-c.Y4)*dx))
|
d2 = math.Fabs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
|
||||||
d3 = math.Fabs(((c.X3-c.X4)*dy - (c.Y3-c.Y4)*dx))
|
d3 = math.Fabs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
|
||||||
|
|
||||||
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
||||||
t.LineTo(c.X4, c.Y4)
|
t.LineTo(c[6], c[7])
|
||||||
i--
|
i--
|
||||||
} else {
|
} else {
|
||||||
// second half of bezier go lower onto the stack
|
// second half of bezier go lower onto the stack
|
||||||
|
|
|
@ -19,52 +19,52 @@ func (c *CubicCurveFloat64) ArbitraryPoint(mu float64) (x, y float64) {
|
||||||
mum13 := mum1 * mum1 * mum1
|
mum13 := mum1 * mum1 * mum1
|
||||||
mu3 := mu * mu * mu
|
mu3 := mu * mu * mu
|
||||||
|
|
||||||
x = mum13*c.X1 + 3*mu*mum1*mum1*c.X2 + 3*mu*mu*mum1*c.X3 + mu3*c.X4
|
x = mum13*c[0] + 3*mu*mum1*mum1*c[2] + 3*mu*mu*mum1*c[4] + mu3*c[6]
|
||||||
y = mum13*c.Y1 + 3*mu*mum1*mum1*c.Y2 + 3*mu*mu*mum1*c.Y3 + mu3*c.Y4
|
y = mum13*c[1] + 3*mu*mum1*mum1*c[3] + 3*mu*mu*mum1*c[5] + mu3*c[7]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CubicCurveFloat64) SubdivideAt(c1, c2 *CubicCurveFloat64, t float64) (x23, y23 float64) {
|
func (c *CubicCurveFloat64) SubdivideAt(c1, c2 *CubicCurveFloat64, t float64) (x23, y23 float64) {
|
||||||
inv_t := (1 - t)
|
inv_t := (1 - t)
|
||||||
c1.X1, c1.Y1 = c.X1, c.Y1
|
c1[0], c1[1] = c[0], c[1]
|
||||||
c2.X4, c2.Y4 = c.X4, c.Y4
|
c2[6], c2[7] = c[6], c[7]
|
||||||
|
|
||||||
c1.X2 = inv_t*c.X1 + t*c.X2
|
c1[2] = inv_t*c[0] + t*c[2]
|
||||||
c1.Y2 = inv_t*c.Y1 + t*c.Y2
|
c1[3] = inv_t*c[1] + t*c[3]
|
||||||
|
|
||||||
x23 = inv_t*c.X2 + t*c.X3
|
x23 = inv_t*c[2] + t*c[4]
|
||||||
y23 = inv_t*c.Y2 + t*c.Y3
|
y23 = inv_t*c[3] + t*c[5]
|
||||||
|
|
||||||
c2.X3 = inv_t*c.X3 + t*c.X4
|
c2[4] = inv_t*c[4] + t*c[6]
|
||||||
c2.Y3 = inv_t*c.Y3 + t*c.Y4
|
c2[5] = inv_t*c[5] + t*c[7]
|
||||||
|
|
||||||
c1.X3 = inv_t*c1.X2 + t*x23
|
c1[4] = inv_t*c1[2] + t*x23
|
||||||
c1.Y3 = inv_t*c1.Y2 + t*y23
|
c1[5] = inv_t*c1[3] + t*y23
|
||||||
|
|
||||||
c2.X2 = inv_t*x23 + t*c2.X3
|
c2[2] = inv_t*x23 + t*c2[4]
|
||||||
c2.Y2 = inv_t*y23 + t*c2.Y3
|
c2[3] = inv_t*y23 + t*c2[5]
|
||||||
|
|
||||||
c1.X4 = inv_t*c1.X3 + t*c2.X2
|
c1[6] = inv_t*c1[4] + t*c2[2]
|
||||||
c1.Y4 = inv_t*c1.Y3 + t*c2.Y2
|
c1[7] = inv_t*c1[5] + t*c2[3]
|
||||||
|
|
||||||
c2.X1, c2.Y1 = c1.X4, c1.Y4
|
c2[0], c2[1] = c1[6], c1[7]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CubicCurveFloat64) EstimateDistance() float64 {
|
func (c *CubicCurveFloat64) EstimateDistance() float64 {
|
||||||
dx1 := c.X2 - c.X1
|
dx1 := c[2] - c[0]
|
||||||
dy1 := c.Y2 - c.Y1
|
dy1 := c[3] - c[1]
|
||||||
dx2 := c.X3 - c.X2
|
dx2 := c[4] - c[2]
|
||||||
dy2 := c.Y3 - c.Y2
|
dy2 := c[5] - c[3]
|
||||||
dx3 := c.X4 - c.X3
|
dx3 := c[6] - c[4]
|
||||||
dy3 := c.Y4 - c.Y3
|
dy3 := c[7] - c[5]
|
||||||
return math.Sqrt(dx1*dx1+dy1*dy1) + math.Sqrt(dx2*dx2+dy2*dy2) + math.Sqrt(dx3*dx3+dy3*dy3)
|
return math.Sqrt(dx1*dx1+dy1*dy1) + math.Sqrt(dx2*dx2+dy2*dy2) + math.Sqrt(dx3*dx3+dy3*dy3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// subdivide the curve in straight lines using line approximation and Casteljau recursive subdivision
|
// subdivide the curve in straight lines using line approximation and Casteljau recursive subdivision
|
||||||
func (c *CubicCurveFloat64) SegmentRec(t LineTracer, flattening_threshold float64) {
|
func (c *CubicCurveFloat64) SegmentRec(t LineTracer, flattening_threshold float64) {
|
||||||
c.segmentRec(t, flattening_threshold)
|
c.segmentRec(t, flattening_threshold)
|
||||||
t.LineTo(c.X4, c.Y4)
|
t.LineTo(c[6], c[7])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CubicCurveFloat64) segmentRec(t LineTracer, flattening_threshold float64) {
|
func (c *CubicCurveFloat64) segmentRec(t LineTracer, flattening_threshold float64) {
|
||||||
|
@ -73,14 +73,14 @@ func (c *CubicCurveFloat64) segmentRec(t LineTracer, flattening_threshold float6
|
||||||
|
|
||||||
// Try to approximate the full cubic curve by a single straight line
|
// Try to approximate the full cubic curve by a single straight line
|
||||||
//------------------
|
//------------------
|
||||||
dx := c.X4 - c.X1
|
dx := c[6] - c[0]
|
||||||
dy := c.Y4 - c.Y1
|
dy := c[7] - c[1]
|
||||||
|
|
||||||
d2 := math.Fabs(((c.X2-c.X4)*dy - (c.Y2-c.Y4)*dx))
|
d2 := math.Fabs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
|
||||||
d3 := math.Fabs(((c.X3-c.X4)*dy - (c.Y3-c.Y4)*dx))
|
d3 := math.Fabs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
|
||||||
|
|
||||||
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) {
|
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) {
|
||||||
t.LineTo(c.X4, c.Y4)
|
t.LineTo(c[6], c[7])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Continue subdivision
|
// Continue subdivision
|
||||||
|
@ -114,7 +114,7 @@ func (c *CubicCurveFloat64) AdaptiveSegmentRec(t LineTracer, approximationScale,
|
||||||
distanceToleranceSquare := 0.5 / approximationScale
|
distanceToleranceSquare := 0.5 / approximationScale
|
||||||
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
|
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
|
||||||
c.adaptiveSegmentRec(t, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
|
c.adaptiveSegmentRec(t, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
|
||||||
t.LineTo(c.X4, c.Y4)
|
t.LineTo(c[6], c[7])
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeCuspLimit(v float64) (r float64) {
|
func computeCuspLimit(v float64) (r float64) {
|
||||||
|
@ -144,26 +144,26 @@ func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distance
|
||||||
|
|
||||||
// Try to approximate the full cubic curve by a single straight line
|
// Try to approximate the full cubic curve by a single straight line
|
||||||
//------------------
|
//------------------
|
||||||
dx := c.X4 - c.X1
|
dx := c[6] - c[0]
|
||||||
dy := c.Y4 - c.Y1
|
dy := c[7] - c[1]
|
||||||
|
|
||||||
d2 := math.Fabs(((c.X2-c.X4)*dy - (c.Y2-c.Y4)*dx))
|
d2 := math.Fabs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
|
||||||
d3 := math.Fabs(((c.X3-c.X4)*dy - (c.Y3-c.Y4)*dx))
|
d3 := math.Fabs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
|
||||||
switch {
|
switch {
|
||||||
case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
|
case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
|
||||||
// All collinear OR p1==p4
|
// All collinear OR p1==p4
|
||||||
//----------------------
|
//----------------------
|
||||||
k := dx*dx + dy*dy
|
k := dx*dx + dy*dy
|
||||||
if k == 0 {
|
if k == 0 {
|
||||||
d2 = squareDistance(c.X1, c.Y1, c.X2, c.Y2)
|
d2 = squareDistance(c[0], c[1], c[2], c[3])
|
||||||
d3 = squareDistance(c.X4, c.Y4, c.X3, c.Y3)
|
d3 = squareDistance(c[6], c[7], c[4], c[5])
|
||||||
} else {
|
} else {
|
||||||
k = 1 / k
|
k = 1 / k
|
||||||
da1 := c.X2 - c.X1
|
da1 := c[2] - c[0]
|
||||||
da2 := c.Y2 - c.Y1
|
da2 := c[3] - c[1]
|
||||||
d2 = k * (da1*dx + da2*dy)
|
d2 = k * (da1*dx + da2*dy)
|
||||||
da1 = c.X3 - c.X1
|
da1 = c[4] - c[0]
|
||||||
da2 = c.Y3 - c.Y1
|
da2 = c[5] - c[1]
|
||||||
d3 = k * (da1*dx + da2*dy)
|
d3 = k * (da1*dx + da2*dy)
|
||||||
if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
|
if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
|
||||||
// Simple collinear case, 1---2---3---4
|
// Simple collinear case, 1---2---3---4
|
||||||
|
@ -171,29 +171,29 @@ func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distance
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if d2 <= 0 {
|
if d2 <= 0 {
|
||||||
d2 = squareDistance(c.X2, c.Y2, c.X1, c.Y1)
|
d2 = squareDistance(c[2], c[3], c[0], c[1])
|
||||||
} else if d2 >= 1 {
|
} else if d2 >= 1 {
|
||||||
d2 = squareDistance(c.X2, c.Y2, c.X4, c.Y4)
|
d2 = squareDistance(c[2], c[3], c[6], c[7])
|
||||||
} else {
|
} else {
|
||||||
d2 = squareDistance(c.X2, c.Y2, c.X1+d2*dx, c.Y1+d2*dy)
|
d2 = squareDistance(c[2], c[3], c[0]+d2*dx, c[1]+d2*dy)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d3 <= 0 {
|
if d3 <= 0 {
|
||||||
d3 = squareDistance(c.X3, c.Y3, c.X1, c.Y1)
|
d3 = squareDistance(c[4], c[5], c[0], c[1])
|
||||||
} else if d3 >= 1 {
|
} else if d3 >= 1 {
|
||||||
d3 = squareDistance(c.X3, c.Y3, c.X4, c.Y4)
|
d3 = squareDistance(c[4], c[5], c[6], c[7])
|
||||||
} else {
|
} else {
|
||||||
d3 = squareDistance(c.X3, c.Y3, c.X1+d3*dx, c.Y1+d3*dy)
|
d3 = squareDistance(c[4], c[5], c[0]+d3*dx, c[1]+d3*dy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if d2 > d3 {
|
if d2 > d3 {
|
||||||
if d2 < distanceToleranceSquare {
|
if d2 < distanceToleranceSquare {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if d3 < distanceToleranceSquare {
|
if d3 < distanceToleranceSquare {
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,20 +209,20 @@ func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distance
|
||||||
|
|
||||||
// Angle Condition
|
// Angle Condition
|
||||||
//----------------------
|
//----------------------
|
||||||
da1 := math.Fabs(math.Atan2(c.Y4-c.Y3, c.X4-c.X3) - math.Atan2(c.Y3-c.Y2, c.X3-c.X2))
|
da1 := math.Fabs(math.Atan2(c[7]-c[5], c[6]-c[4]) - math.Atan2(c[5]-c[3], c[4]-c[2]))
|
||||||
if da1 >= math.Pi {
|
if da1 >= math.Pi {
|
||||||
da1 = 2*math.Pi - da1
|
da1 = 2*math.Pi - da1
|
||||||
}
|
}
|
||||||
|
|
||||||
if da1 < angleTolerance {
|
if da1 < angleTolerance {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,20 +239,20 @@ func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distance
|
||||||
|
|
||||||
// Angle Condition
|
// Angle Condition
|
||||||
//----------------------
|
//----------------------
|
||||||
da1 := math.Fabs(math.Atan2(c.Y3-c.Y2, c.X3-c.X2) - math.Atan2(c.Y2-c.Y1, c.X2-c.X1))
|
da1 := math.Fabs(math.Atan2(c[5]-c[3], c[4]-c[2]) - math.Atan2(c[3]-c[1], c[2]-c[0]))
|
||||||
if da1 >= math.Pi {
|
if da1 >= math.Pi {
|
||||||
da1 = 2*math.Pi - da1
|
da1 = 2*math.Pi - da1
|
||||||
}
|
}
|
||||||
|
|
||||||
if da1 < angleTolerance {
|
if da1 < angleTolerance {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,9 +272,9 @@ func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distance
|
||||||
|
|
||||||
// Angle & Cusp Condition
|
// Angle & Cusp Condition
|
||||||
//----------------------
|
//----------------------
|
||||||
k := math.Atan2(c.Y3-c.Y2, c.X3-c.X2)
|
k := math.Atan2(c[5]-c[3], c[4]-c[2])
|
||||||
da1 := math.Fabs(k - math.Atan2(c.Y2-c.Y1, c.X2-c.X1))
|
da1 := math.Fabs(k - math.Atan2(c[3]-c[1], c[2]-c[0]))
|
||||||
da2 := math.Fabs(math.Atan2(c.Y4-c.Y3, c.X4-c.X3) - k)
|
da2 := math.Fabs(math.Atan2(c[7]-c[5], c[6]-c[4]) - k)
|
||||||
if da1 >= math.Pi {
|
if da1 >= math.Pi {
|
||||||
da1 = 2*math.Pi - da1
|
da1 = 2*math.Pi - da1
|
||||||
}
|
}
|
||||||
|
@ -291,12 +291,12 @@ func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distance
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if da2 > cuspLimit {
|
if da2 > cuspLimit {
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,14 +328,14 @@ func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale
|
||||||
|
|
||||||
// Try to approximate the full cubic curve by a single straight line
|
// Try to approximate the full cubic curve by a single straight line
|
||||||
//------------------
|
//------------------
|
||||||
dx = c.X4 - c.X1
|
dx = c[6] - c[0]
|
||||||
dy = c.Y4 - c.Y1
|
dy = c[7] - c[1]
|
||||||
|
|
||||||
d2 = math.Fabs(((c.X2-c.X4)*dy - (c.Y2-c.Y4)*dx))
|
d2 = math.Fabs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
|
||||||
d3 = math.Fabs(((c.X3-c.X4)*dy - (c.Y3-c.Y4)*dx))
|
d3 = math.Fabs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
|
||||||
switch {
|
switch {
|
||||||
case i == len(curves)-1:
|
case i == len(curves)-1:
|
||||||
t.LineTo(c.X4, c.Y4)
|
t.LineTo(c[6], c[7])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
|
case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
|
||||||
|
@ -343,15 +343,15 @@ func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale
|
||||||
//----------------------
|
//----------------------
|
||||||
k = dx*dx + dy*dy
|
k = dx*dx + dy*dy
|
||||||
if k == 0 {
|
if k == 0 {
|
||||||
d2 = squareDistance(c.X1, c.Y1, c.X2, c.Y2)
|
d2 = squareDistance(c[0], c[1], c[2], c[3])
|
||||||
d3 = squareDistance(c.X4, c.Y4, c.X3, c.Y3)
|
d3 = squareDistance(c[6], c[7], c[4], c[5])
|
||||||
} else {
|
} else {
|
||||||
k = 1 / k
|
k = 1 / k
|
||||||
da1 := c.X2 - c.X1
|
da1 := c[2] - c[0]
|
||||||
da2 := c.Y2 - c.Y1
|
da2 := c[3] - c[1]
|
||||||
d2 = k * (da1*dx + da2*dy)
|
d2 = k * (da1*dx + da2*dy)
|
||||||
da1 = c.X3 - c.X1
|
da1 = c[4] - c[0]
|
||||||
da2 = c.Y3 - c.Y1
|
da2 = c[5] - c[1]
|
||||||
d3 = k * (da1*dx + da2*dy)
|
d3 = k * (da1*dx + da2*dy)
|
||||||
if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
|
if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
|
||||||
// Simple collinear case, 1---2---3---4
|
// Simple collinear case, 1---2---3---4
|
||||||
|
@ -360,30 +360,30 @@ func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if d2 <= 0 {
|
if d2 <= 0 {
|
||||||
d2 = squareDistance(c.X2, c.Y2, c.X1, c.Y1)
|
d2 = squareDistance(c[2], c[3], c[0], c[1])
|
||||||
} else if d2 >= 1 {
|
} else if d2 >= 1 {
|
||||||
d2 = squareDistance(c.X2, c.Y2, c.X4, c.Y4)
|
d2 = squareDistance(c[2], c[3], c[6], c[7])
|
||||||
} else {
|
} else {
|
||||||
d2 = squareDistance(c.X2, c.Y2, c.X1+d2*dx, c.Y1+d2*dy)
|
d2 = squareDistance(c[2], c[3], c[0]+d2*dx, c[1]+d2*dy)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d3 <= 0 {
|
if d3 <= 0 {
|
||||||
d3 = squareDistance(c.X3, c.Y3, c.X1, c.Y1)
|
d3 = squareDistance(c[4], c[5], c[0], c[1])
|
||||||
} else if d3 >= 1 {
|
} else if d3 >= 1 {
|
||||||
d3 = squareDistance(c.X3, c.Y3, c.X4, c.Y4)
|
d3 = squareDistance(c[4], c[5], c[6], c[7])
|
||||||
} else {
|
} else {
|
||||||
d3 = squareDistance(c.X3, c.Y3, c.X1+d3*dx, c.Y1+d3*dy)
|
d3 = squareDistance(c[4], c[5], c[0]+d3*dx, c[1]+d3*dy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if d2 > d3 {
|
if d2 > d3 {
|
||||||
if d2 < distanceToleranceSquare {
|
if d2 < distanceToleranceSquare {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if d3 < distanceToleranceSquare {
|
if d3 < distanceToleranceSquare {
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -401,21 +401,21 @@ func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale
|
||||||
|
|
||||||
// Angle Condition
|
// Angle Condition
|
||||||
//----------------------
|
//----------------------
|
||||||
da1 := math.Fabs(math.Atan2(c.Y4-c.Y3, c.X4-c.X3) - math.Atan2(c.Y3-c.Y2, c.X3-c.X2))
|
da1 := math.Fabs(math.Atan2(c[7]-c[5], c[6]-c[4]) - math.Atan2(c[5]-c[3], c[4]-c[2]))
|
||||||
if da1 >= math.Pi {
|
if da1 >= math.Pi {
|
||||||
da1 = 2*math.Pi - da1
|
da1 = 2*math.Pi - da1
|
||||||
}
|
}
|
||||||
|
|
||||||
if da1 < angleTolerance {
|
if da1 < angleTolerance {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -434,21 +434,21 @@ func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale
|
||||||
|
|
||||||
// Angle Condition
|
// Angle Condition
|
||||||
//----------------------
|
//----------------------
|
||||||
da1 := math.Fabs(math.Atan2(c.Y3-c.Y2, c.X3-c.X2) - math.Atan2(c.Y2-c.Y1, c.X2-c.X1))
|
da1 := math.Fabs(math.Atan2(c[5]-c[3], c[4]-c[2]) - math.Atan2(c[3]-c[1], c[2]-c[0]))
|
||||||
if da1 >= math.Pi {
|
if da1 >= math.Pi {
|
||||||
da1 = 2*math.Pi - da1
|
da1 = 2*math.Pi - da1
|
||||||
}
|
}
|
||||||
|
|
||||||
if da1 < angleTolerance {
|
if da1 < angleTolerance {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -470,9 +470,9 @@ func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale
|
||||||
|
|
||||||
// Angle & Cusp Condition
|
// Angle & Cusp Condition
|
||||||
//----------------------
|
//----------------------
|
||||||
k := math.Atan2(c.Y3-c.Y2, c.X3-c.X2)
|
k := math.Atan2(c[5]-c[3], c[4]-c[2])
|
||||||
da1 := math.Fabs(k - math.Atan2(c.Y2-c.Y1, c.X2-c.X1))
|
da1 := math.Fabs(k - math.Atan2(c[3]-c[1], c[2]-c[0]))
|
||||||
da2 := math.Fabs(math.Atan2(c.Y4-c.Y3, c.X4-c.X3) - k)
|
da2 := math.Fabs(math.Atan2(c[7]-c[5], c[6]-c[4]) - k)
|
||||||
if da1 >= math.Pi {
|
if da1 >= math.Pi {
|
||||||
da1 = 2*math.Pi - da1
|
da1 = 2*math.Pi - da1
|
||||||
}
|
}
|
||||||
|
@ -490,13 +490,13 @@ func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale
|
||||||
|
|
||||||
if cuspLimit != 0.0 {
|
if cuspLimit != 0.0 {
|
||||||
if da1 > cuspLimit {
|
if da1 > cuspLimit {
|
||||||
t.LineTo(c.X2, c.Y2)
|
t.LineTo(c[2], c[3])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if da2 > cuspLimit {
|
if da2 > cuspLimit {
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
i--
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -509,7 +509,7 @@ func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale
|
||||||
curves[i+1], curves[i] = c1, c2
|
curves[i+1], curves[i] = c1, c2
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
t.LineTo(curve.X4, curve.Y4)
|
t.LineTo(curve[6], curve[7])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -569,27 +569,27 @@ func (c *CubicCurveFloat64) ParabolicSegment(t LineTracer, flattening_threshold
|
||||||
|
|
||||||
// Find the third control point deviation form the axis
|
// Find the third control point deviation form the axis
|
||||||
func (c *CubicCurveFloat64) thirdControlPointDeviation() float64 {
|
func (c *CubicCurveFloat64) thirdControlPointDeviation() float64 {
|
||||||
dx := c.X2 - c.X1
|
dx := c[2] - c[0]
|
||||||
dy := c.Y2 - c.Y1
|
dy := c[3] - c[1]
|
||||||
l2 := dx*dx + dy*dy
|
l2 := dx*dx + dy*dy
|
||||||
if l2 == 0 {
|
if l2 == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
l := math.Sqrt(l2)
|
l := math.Sqrt(l2)
|
||||||
r := (c.Y2 - c.Y1) / l
|
r := (c[3] - c[1]) / l
|
||||||
s := (c.X1 - c.X2) / l
|
s := (c[0] - c[2]) / l
|
||||||
u := (c.X2*c.Y1 - c.X1*c.Y2) / l
|
u := (c[2]*c[1] - c[0]*c[3]) / l
|
||||||
return math.Fabs(r*c.X3 + s*c.Y3 + u)
|
return math.Fabs(r*c[4] + s*c[5] + u)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the number of inflection point
|
// Find the number of inflection point
|
||||||
func (c *CubicCurveFloat64) numberOfInflectionPoints() int {
|
func (c *CubicCurveFloat64) numberOfInflectionPoints() int {
|
||||||
dx21 := (c.X2 - c.X1)
|
dx21 := (c[2] - c[0])
|
||||||
dy21 := (c.Y2 - c.Y1)
|
dy21 := (c[3] - c[1])
|
||||||
dx32 := (c.X3 - c.X2)
|
dx32 := (c[4] - c[2])
|
||||||
dy32 := (c.Y3 - c.Y2)
|
dy32 := (c[5] - c[3])
|
||||||
dx43 := (c.X4 - c.X3)
|
dx43 := (c[6] - c[4])
|
||||||
dy43 := (c.Y4 - c.Y3)
|
dy43 := (c[7] - c[5])
|
||||||
if ((dx21*dy32 - dy21*dx32) * (dx32*dy43 - dy32*dx43)) < 0 {
|
if ((dx21*dy32 - dy21*dx32) * (dx32*dy43 - dy32*dx43)) < 0 {
|
||||||
return 1 // One inflection point
|
return 1 // One inflection point
|
||||||
} else if ((dx21*dy32 - dy21*dx32) * (dx21*dy43 - dy21*dx43)) > 0 {
|
} else if ((dx21*dy32 - dy21*dx32) * (dx21*dy43 - dy21*dx43)) > 0 {
|
||||||
|
@ -612,16 +612,16 @@ func (curve *CubicCurveFloat64) doParabolicApproximation(tracer LineTracer, flat
|
||||||
c = curve
|
c = curve
|
||||||
var d, t, dx, dy, d2, d3 float64
|
var d, t, dx, dy, d2, d3 float64
|
||||||
for {
|
for {
|
||||||
dx = c.X4 - c.X1
|
dx = c[6] - c[0]
|
||||||
dy = c.Y4 - c.Y1
|
dy = c[7] - c[1]
|
||||||
|
|
||||||
d2 = math.Fabs(((c.X2-c.X4)*dy - (c.Y2-c.Y4)*dx))
|
d2 = math.Fabs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
|
||||||
d3 = math.Fabs(((c.X3-c.X4)*dy - (c.Y3-c.Y4)*dx))
|
d3 = math.Fabs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
|
||||||
|
|
||||||
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) {
|
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) {
|
||||||
// If the subsegment deviation satisfy the flatness then store the last
|
// If the subsegment deviation satisfy the flatness then store the last
|
||||||
// point and stop
|
// point and stop
|
||||||
tracer.LineTo(c.X4, c.Y4)
|
tracer.LineTo(c[6], c[7])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Find the third control point deviation and the t values for subdivision
|
// Find the third control point deviation and the t values for subdivision
|
||||||
|
@ -636,17 +636,17 @@ func (curve *CubicCurveFloat64) doParabolicApproximation(tracer LineTracer, flat
|
||||||
var b1, b2 CubicCurveFloat64
|
var b1, b2 CubicCurveFloat64
|
||||||
c.SubdivideAt(&b1, &b2, t)
|
c.SubdivideAt(&b1, &b2, t)
|
||||||
// First subsegment should have its deviation equal to flatness
|
// First subsegment should have its deviation equal to flatness
|
||||||
dx = b1.X4 - b1.X1
|
dx = b1[6] - b1[0]
|
||||||
dy = b1.Y4 - b1.Y1
|
dy = b1[7] - b1[1]
|
||||||
|
|
||||||
d2 = math.Fabs(((b1.X2-b1.X4)*dy - (b1.Y2-b1.Y4)*dx))
|
d2 = math.Fabs(((b1[2]-b1[6])*dy - (b1[3]-b1[7])*dx))
|
||||||
d3 = math.Fabs(((b1.X3-b1.X4)*dy - (b1.Y3-b1.Y4)*dx))
|
d3 = math.Fabs(((b1[4]-b1[6])*dy - (b1[5]-b1[7])*dx))
|
||||||
|
|
||||||
if (d2+d3)*(d2+d3) > flattening_threshold*(dx*dx+dy*dy) {
|
if (d2+d3)*(d2+d3) > flattening_threshold*(dx*dx+dy*dy) {
|
||||||
// if not then use RS to handle any mathematical errors
|
// if not then use RS to handle any mathematical errors
|
||||||
b1.Segment(tracer, flattening_threshold)
|
b1.Segment(tracer, flattening_threshold)
|
||||||
} else {
|
} else {
|
||||||
tracer.LineTo(b1.X4, b1.Y4)
|
tracer.LineTo(b1[6], b1[7])
|
||||||
}
|
}
|
||||||
// repeat the process for the left over subsegment.
|
// repeat the process for the left over subsegment.
|
||||||
c = &b2
|
c = &b2
|
||||||
|
@ -661,12 +661,12 @@ func (curve *CubicCurveFloat64) findInflectionPoints() (int, firstIfp, secondIfp
|
||||||
// a = (float)(-bez.p1 + 3*bez.p2 - 3*bez.p3 + bez.p4);
|
// a = (float)(-bez.p1 + 3*bez.p2 - 3*bez.p3 + bez.p4);
|
||||||
// b = (float)(3*bez.p1 - 6*bez.p2 + 3*bez.p3);
|
// b = (float)(3*bez.p1 - 6*bez.p2 + 3*bez.p3);
|
||||||
// c = (float)(-3*bez.p1 + 3*bez.p2);
|
// c = (float)(-3*bez.p1 + 3*bez.p2);
|
||||||
ax := (-curve.X1 + 3*curve.X2 - 3*curve.X3 + curve.X4)
|
ax := (-curve[0] + 3*curve[2] - 3*curve[4] + curve[6])
|
||||||
bx := (3*curve.X1 - 6*curve.X2 + 3*curve.X3)
|
bx := (3*curve[0] - 6*curve[2] + 3*curve[4])
|
||||||
cx := (-3*curve.X1 + 3*curve.X2)
|
cx := (-3*curve[0] + 3*curve[2])
|
||||||
ay := (-curve.Y1 + 3*curve.Y2 - 3*curve.Y3 + curve.Y4)
|
ay := (-curve[1] + 3*curve[3] - 3*curve[5] + curve[7])
|
||||||
by := (3*curve.Y1 - 6*curve.Y2 + 3*curve.Y3)
|
by := (3*curve[1] - 6*curve[3] + 3*curve[5])
|
||||||
cy := (-3*curve.Y1 + 3*curve.Y2)
|
cy := (-3*curve[1] + 3*curve[3])
|
||||||
a := (3 * (ay*bx - ax*by))
|
a := (3 * (ay*bx - ax*by))
|
||||||
b := (3 * (ay*cx - ax*cy))
|
b := (3 * (ay*cx - ax*cy))
|
||||||
c := (by*cx - bx*cy)
|
c := (by*cx - bx*cy)
|
||||||
|
|
|
@ -108,12 +108,12 @@ func drawPoints(img draw.Image, c image.Color, s ...float64) image.Image {
|
||||||
func TestCubicCurveRec(t *testing.T) {
|
func TestCubicCurveRec(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.SegmentRec(&p, flattening_threshold)
|
curve.SegmentRec(&p, flattening_threshold)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve[:]...)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testRec%d.png", i), img)
|
savepng(fmt.Sprintf("_testRec%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
|
@ -124,12 +124,12 @@ func TestCubicCurveRec(t *testing.T) {
|
||||||
func TestCubicCurve(t *testing.T) {
|
func TestCubicCurve(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.Segment(&p, flattening_threshold)
|
curve.Segment(&p, flattening_threshold)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve[:]...)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_test%d.png", i), img)
|
savepng(fmt.Sprintf("_test%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
|
@ -140,12 +140,12 @@ func TestCubicCurve(t *testing.T) {
|
||||||
func TestCubicCurveAdaptiveRec(t *testing.T) {
|
func TestCubicCurveAdaptiveRec(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
|
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve[:]...)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testAdaptiveRec%d.png", i), img)
|
savepng(fmt.Sprintf("_testAdaptiveRec%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
|
@ -156,12 +156,12 @@ func TestCubicCurveAdaptiveRec(t *testing.T) {
|
||||||
func TestCubicCurveAdaptive(t *testing.T) {
|
func TestCubicCurveAdaptive(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.AdaptiveSegment(&p, 1, 0, 0)
|
curve.AdaptiveSegment(&p, 1, 0, 0)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve[:]...)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testAdaptive%d.png", i), img)
|
savepng(fmt.Sprintf("_testAdaptive%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
|
@ -172,12 +172,12 @@ func TestCubicCurveAdaptive(t *testing.T) {
|
||||||
func TestCubicCurveParabolic(t *testing.T) {
|
func TestCubicCurveParabolic(t *testing.T) {
|
||||||
for i, curve := range testsCubicFloat64 {
|
for i, curve := range testsCubicFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.ParabolicSegment(&p, flattening_threshold)
|
curve.ParabolicSegment(&p, flattening_threshold)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve[:]...)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3, curve.X4, curve.Y4)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testParabolic%d.png", i), img)
|
savepng(fmt.Sprintf("_testParabolic%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
|
@ -189,11 +189,12 @@ func TestCubicCurveParabolic(t *testing.T) {
|
||||||
func TestQuadCurve(t *testing.T) {
|
func TestQuadCurve(t *testing.T) {
|
||||||
for i, curve := range testsQuadFloat64 {
|
for i, curve := range testsQuadFloat64 {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.Segment(&p, flattening_threshold)
|
curve.Segment(&p, flattening_threshold)
|
||||||
img := image.NewNRGBA(300, 300)
|
img := image.NewNRGBA(300, 300)
|
||||||
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve.X1, curve.Y1, curve.X2, curve.Y2, curve.X3, curve.Y3)
|
raster.PolylineBresenham(img, image.NRGBAColor{0xff, 0, 0, 0xff}, curve[:]...)
|
||||||
raster.PolylineBresenham(img, image.Black, p.points...)
|
raster.PolylineBresenham(img, image.Black, p.points...)
|
||||||
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||||
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, p.points...)
|
||||||
savepng(fmt.Sprintf("_testQuad%d.png", i), img)
|
savepng(fmt.Sprintf("_testQuad%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.points))
|
log.Printf("Num of points: %d\n", len(p.points))
|
||||||
|
@ -205,7 +206,7 @@ func BenchmarkCubicCurveRec(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.SegmentRec(&p, flattening_threshold)
|
curve.SegmentRec(&p, flattening_threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,7 +216,7 @@ func BenchmarkCubicCurve(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.Segment(&p, flattening_threshold)
|
curve.Segment(&p, flattening_threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +226,7 @@ func BenchmarkCubicCurveAdaptiveRec(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
|
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,7 +236,7 @@ func BenchmarkCubicCurveAdaptive(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.AdaptiveSegment(&p, 1, 0, 0)
|
curve.AdaptiveSegment(&p, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +246,7 @@ func BenchmarkCubicCurveParabolic(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsCubicFloat64 {
|
for _, curve := range testsCubicFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.ParabolicSegment(&p, flattening_threshold)
|
curve.ParabolicSegment(&p, flattening_threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +256,7 @@ func BenchmarkQuadCurve(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, curve := range testsQuadFloat64 {
|
for _, curve := range testsQuadFloat64 {
|
||||||
p := Path{make([]float64, 0, 32)}
|
p := Path{make([]float64, 0, 32)}
|
||||||
p.LineTo(curve.X1, curve.Y1)
|
p.LineTo(curve[0], curve[1])
|
||||||
curve.Segment(&p, flattening_threshold)
|
curve.Segment(&p, flattening_threshold)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,24 +5,23 @@ package curve
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
//X1, Y1, X2, Y2, X3, Y3 float64
|
||||||
|
type QuadCurveFloat64 [6]float64
|
||||||
|
|
||||||
type QuadCurveFloat64 struct {
|
|
||||||
X1, Y1, X2, Y2, X3, Y3 float64
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func (c *QuadCurveFloat64) Subdivide(c1, c2 *QuadCurveFloat64) {
|
func (c *QuadCurveFloat64) Subdivide(c1, c2 *QuadCurveFloat64) {
|
||||||
// Calculate all the mid-points of the line segments
|
// Calculate all the mid-points of the line segments
|
||||||
//----------------------
|
//----------------------
|
||||||
c1.X1, c1.Y1 = c.X1, c.Y1
|
c1[0], c1[1] = c[0], c[1]
|
||||||
c2.X3, c2.Y3 = c.X3, c.Y3
|
c2[4], c2[5] = c[4], c[5]
|
||||||
c1.X2 = (c.X1 + c.X2) / 2
|
c1[2] = (c[0] + c[2]) / 2
|
||||||
c1.Y2 = (c.Y1 + c.Y2) / 2
|
c1[3] = (c[1] + c[3]) / 2
|
||||||
c2.X2 = (c.X2 + c.X3) / 2
|
c2[2] = (c[2] + c[4]) / 2
|
||||||
c2.Y2 = (c.Y2 + c.Y3) / 2
|
c2[3] = (c[3] + c[5]) / 2
|
||||||
c1.X3 = (c1.X2 + c2.X2) / 2
|
c1[4] = (c1[2] + c2[2]) / 2
|
||||||
c1.Y3 = (c1.Y2 + c2.Y2) / 2
|
c1[5] = (c1[3] + c2[3]) / 2
|
||||||
c2.X1, c2.Y1 = c1.X3, c1.Y3
|
c2[0], c2[1] = c1[4], c1[5]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,13 +36,13 @@ func (curve *QuadCurveFloat64) Segment(t LineTracer, flattening_threshold float6
|
||||||
|
|
||||||
for i >= 0 {
|
for i >= 0 {
|
||||||
c = &curves[i]
|
c = &curves[i]
|
||||||
dx = c.X3 - c.X1
|
dx = c[4] - c[0]
|
||||||
dy = c.Y3 - c.Y1
|
dy = c[5] - c[1]
|
||||||
|
|
||||||
d = math.Fabs(((c.X2-c.X3)*dy - (c.Y2-c.Y3)*dx))
|
d = math.Fabs(((c[2]-c[4])*dy - (c[3]-c[5])*dx))
|
||||||
|
|
||||||
if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
|
||||||
t.LineTo(c.X3, c.Y3)
|
t.LineTo(c[4], c[5])
|
||||||
i--
|
i--
|
||||||
} else {
|
} else {
|
||||||
// second half of bezier go lower onto the stack
|
// second half of bezier go lower onto the stack
|
||||||
|
|
|
@ -3,5 +3,9 @@ include $(GOROOT)/src/Make.inc
|
||||||
TARG=draw2d.googlecode.com/hg/draw2d/raster
|
TARG=draw2d.googlecode.com/hg/draw2d/raster
|
||||||
GOFILES=\
|
GOFILES=\
|
||||||
line.go\
|
line.go\
|
||||||
|
polygon.go\
|
||||||
|
coverage_table.go\
|
||||||
|
fillerAA.go\
|
||||||
|
|
||||||
|
|
||||||
include $(GOROOT)/src/Make.pkg
|
include $(GOROOT)/src/Make.pkg
|
||||||
|
|
135
draw2d/raster/coverage_table.go
Normal file
135
draw2d/raster/coverage_table.go
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// Copyright 2011 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 27/05/2011 by Laurent Le Goff
|
||||||
|
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_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),
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
var coverageTable = [256]uint8{
|
||||||
|
pixelCoverage(0x00), pixelCoverage(0x01), pixelCoverage(0x02), pixelCoverage(0x03),
|
||||||
|
pixelCoverage(0x04), pixelCoverage(0x05), pixelCoverage(0x06), pixelCoverage(0x07),
|
||||||
|
pixelCoverage(0x08), pixelCoverage(0x09), pixelCoverage(0x0a), pixelCoverage(0x0b),
|
||||||
|
pixelCoverage(0x0c), pixelCoverage(0x0d), pixelCoverage(0x0e), pixelCoverage(0x0f),
|
||||||
|
pixelCoverage(0x10), pixelCoverage(0x11), pixelCoverage(0x12), pixelCoverage(0x13),
|
||||||
|
pixelCoverage(0x14), pixelCoverage(0x15), pixelCoverage(0x16), pixelCoverage(0x17),
|
||||||
|
pixelCoverage(0x18), pixelCoverage(0x19), pixelCoverage(0x1a), pixelCoverage(0x1b),
|
||||||
|
pixelCoverage(0x1c), pixelCoverage(0x1d), pixelCoverage(0x1e), pixelCoverage(0x1f),
|
||||||
|
pixelCoverage(0x20), pixelCoverage(0x21), pixelCoverage(0x22), pixelCoverage(0x23),
|
||||||
|
pixelCoverage(0x24), pixelCoverage(0x25), pixelCoverage(0x26), pixelCoverage(0x27),
|
||||||
|
pixelCoverage(0x28), pixelCoverage(0x29), pixelCoverage(0x2a), pixelCoverage(0x2b),
|
||||||
|
pixelCoverage(0x2c), pixelCoverage(0x2d), pixelCoverage(0x2e), pixelCoverage(0x2f),
|
||||||
|
pixelCoverage(0x30), pixelCoverage(0x31), pixelCoverage(0x32), pixelCoverage(0x33),
|
||||||
|
pixelCoverage(0x34), pixelCoverage(0x35), pixelCoverage(0x36), pixelCoverage(0x37),
|
||||||
|
pixelCoverage(0x38), pixelCoverage(0x39), pixelCoverage(0x3a), pixelCoverage(0x3b),
|
||||||
|
pixelCoverage(0x3c), pixelCoverage(0x3d), pixelCoverage(0x3e), pixelCoverage(0x3f),
|
||||||
|
pixelCoverage(0x40), pixelCoverage(0x41), pixelCoverage(0x42), pixelCoverage(0x43),
|
||||||
|
pixelCoverage(0x44), pixelCoverage(0x45), pixelCoverage(0x46), pixelCoverage(0x47),
|
||||||
|
pixelCoverage(0x48), pixelCoverage(0x49), pixelCoverage(0x4a), pixelCoverage(0x4b),
|
||||||
|
pixelCoverage(0x4c), pixelCoverage(0x4d), pixelCoverage(0x4e), pixelCoverage(0x4f),
|
||||||
|
pixelCoverage(0x50), pixelCoverage(0x51), pixelCoverage(0x52), pixelCoverage(0x53),
|
||||||
|
pixelCoverage(0x54), pixelCoverage(0x55), pixelCoverage(0x56), pixelCoverage(0x57),
|
||||||
|
pixelCoverage(0x58), pixelCoverage(0x59), pixelCoverage(0x5a), pixelCoverage(0x5b),
|
||||||
|
pixelCoverage(0x5c), pixelCoverage(0x5d), pixelCoverage(0x5e), pixelCoverage(0x5f),
|
||||||
|
pixelCoverage(0x60), pixelCoverage(0x61), pixelCoverage(0x62), pixelCoverage(0x63),
|
||||||
|
pixelCoverage(0x64), pixelCoverage(0x65), pixelCoverage(0x66), pixelCoverage(0x67),
|
||||||
|
pixelCoverage(0x68), pixelCoverage(0x69), pixelCoverage(0x6a), pixelCoverage(0x6b),
|
||||||
|
pixelCoverage(0x6c), pixelCoverage(0x6d), pixelCoverage(0x6e), pixelCoverage(0x6f),
|
||||||
|
pixelCoverage(0x70), pixelCoverage(0x71), pixelCoverage(0x72), pixelCoverage(0x73),
|
||||||
|
pixelCoverage(0x74), pixelCoverage(0x75), pixelCoverage(0x76), pixelCoverage(0x77),
|
||||||
|
pixelCoverage(0x78), pixelCoverage(0x79), pixelCoverage(0x7a), pixelCoverage(0x7b),
|
||||||
|
pixelCoverage(0x7c), pixelCoverage(0x7d), pixelCoverage(0x7e), pixelCoverage(0x7f),
|
||||||
|
pixelCoverage(0x80), pixelCoverage(0x81), pixelCoverage(0x82), pixelCoverage(0x83),
|
||||||
|
pixelCoverage(0x84), pixelCoverage(0x85), pixelCoverage(0x86), pixelCoverage(0x87),
|
||||||
|
pixelCoverage(0x88), pixelCoverage(0x89), pixelCoverage(0x8a), pixelCoverage(0x8b),
|
||||||
|
pixelCoverage(0x8c), pixelCoverage(0x8d), pixelCoverage(0x8e), pixelCoverage(0x8f),
|
||||||
|
pixelCoverage(0x90), pixelCoverage(0x91), pixelCoverage(0x92), pixelCoverage(0x93),
|
||||||
|
pixelCoverage(0x94), pixelCoverage(0x95), pixelCoverage(0x96), pixelCoverage(0x97),
|
||||||
|
pixelCoverage(0x98), pixelCoverage(0x99), pixelCoverage(0x9a), pixelCoverage(0x9b),
|
||||||
|
pixelCoverage(0x9c), pixelCoverage(0x9d), pixelCoverage(0x9e), pixelCoverage(0x9f),
|
||||||
|
pixelCoverage(0xa0), pixelCoverage(0xa1), pixelCoverage(0xa2), pixelCoverage(0xa3),
|
||||||
|
pixelCoverage(0xa4), pixelCoverage(0xa5), pixelCoverage(0xa6), pixelCoverage(0xa7),
|
||||||
|
pixelCoverage(0xa8), pixelCoverage(0xa9), pixelCoverage(0xaa), pixelCoverage(0xab),
|
||||||
|
pixelCoverage(0xac), pixelCoverage(0xad), pixelCoverage(0xae), pixelCoverage(0xaf),
|
||||||
|
pixelCoverage(0xb0), pixelCoverage(0xb1), pixelCoverage(0xb2), pixelCoverage(0xb3),
|
||||||
|
pixelCoverage(0xb4), pixelCoverage(0xb5), pixelCoverage(0xb6), pixelCoverage(0xb7),
|
||||||
|
pixelCoverage(0xb8), pixelCoverage(0xb9), pixelCoverage(0xba), pixelCoverage(0xbb),
|
||||||
|
pixelCoverage(0xbc), pixelCoverage(0xbd), pixelCoverage(0xbe), pixelCoverage(0xbf),
|
||||||
|
pixelCoverage(0xc0), pixelCoverage(0xc1), pixelCoverage(0xc2), pixelCoverage(0xc3),
|
||||||
|
pixelCoverage(0xc4), pixelCoverage(0xc5), pixelCoverage(0xc6), pixelCoverage(0xc7),
|
||||||
|
pixelCoverage(0xc8), pixelCoverage(0xc9), pixelCoverage(0xca), pixelCoverage(0xcb),
|
||||||
|
pixelCoverage(0xcc), pixelCoverage(0xcd), pixelCoverage(0xce), pixelCoverage(0xcf),
|
||||||
|
pixelCoverage(0xd0), pixelCoverage(0xd1), pixelCoverage(0xd2), pixelCoverage(0xd3),
|
||||||
|
pixelCoverage(0xd4), pixelCoverage(0xd5), pixelCoverage(0xd6), pixelCoverage(0xd7),
|
||||||
|
pixelCoverage(0xd8), pixelCoverage(0xd9), pixelCoverage(0xda), pixelCoverage(0xdb),
|
||||||
|
pixelCoverage(0xdc), pixelCoverage(0xdd), pixelCoverage(0xde), pixelCoverage(0xdf),
|
||||||
|
pixelCoverage(0xe0), pixelCoverage(0xe1), pixelCoverage(0xe2), pixelCoverage(0xe3),
|
||||||
|
pixelCoverage(0xe4), pixelCoverage(0xe5), pixelCoverage(0xe6), pixelCoverage(0xe7),
|
||||||
|
pixelCoverage(0xe8), pixelCoverage(0xe9), pixelCoverage(0xea), pixelCoverage(0xeb),
|
||||||
|
pixelCoverage(0xec), pixelCoverage(0xed), pixelCoverage(0xee), pixelCoverage(0xef),
|
||||||
|
pixelCoverage(0xf0), pixelCoverage(0xf1), pixelCoverage(0xf2), pixelCoverage(0xf3),
|
||||||
|
pixelCoverage(0xf4), pixelCoverage(0xf5), pixelCoverage(0xf6), pixelCoverage(0xf7),
|
||||||
|
pixelCoverage(0xf8), pixelCoverage(0xf9), pixelCoverage(0xfa), pixelCoverage(0xfb),
|
||||||
|
pixelCoverage(0xfc), pixelCoverage(0xfd), pixelCoverage(0xfe), pixelCoverage(0xff),
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
178
draw2d/raster/fillerAA.go
Normal file
178
draw2d/raster/fillerAA.go
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
for p < l {
|
||||||
|
var edges [20]PolygonEdge
|
||||||
|
edgeCount := polygon.getEdges(p, 10, edges[:], transform, clipRect)
|
||||||
|
for k := 0; k < edgeCount; k++ {
|
||||||
|
r.addEvenOddEdge(&(edges[k]))
|
||||||
|
}
|
||||||
|
p += 10
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
//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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
// Copyright 2011 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 27/05/2011 by Laurent Le Goff
|
||||||
package raster
|
package raster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
273
draw2d/raster/polygon.go
Normal file
273
draw2d/raster/polygon.go
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
// Copyright 2011 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 27/05/2011 by Laurent Le Goff
|
||||||
|
package raster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
POLYGON_CLIP_NONE = iota
|
||||||
|
POLYGON_CLIP_LEFT
|
||||||
|
POLYGON_CLIP_RIGHT
|
||||||
|
POLYGON_CLIP_TOP
|
||||||
|
POLYGON_CLIP_BOTTOM
|
||||||
|
)
|
||||||
|
|
||||||
|
type Polygon []float64
|
||||||
|
|
||||||
|
|
||||||
|
type PolygonEdge struct {
|
||||||
|
X, Slope float64
|
||||||
|
FirstLine, LastLine int
|
||||||
|
Winding int16
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Calculates the edges of the polygon with transformation and clipping to edges array.
|
||||||
|
/*! \param startIndex the index for the first vertex.
|
||||||
|
* \param vertexCount the amount of vertices to convert.
|
||||||
|
* \param edges the array for result edges. This should be able to contain 2*aVertexCount edges.
|
||||||
|
* \param tr the transformation matrix for the polygon.
|
||||||
|
* \param aClipRectangle the clip rectangle.
|
||||||
|
* \return the amount of edges in the result.
|
||||||
|
*/
|
||||||
|
func (p Polygon) getEdges(startIndex, vertexCount int, edges []PolygonEdge, tr [6]float64, clipBound [4]float64) int {
|
||||||
|
startIndex = startIndex * 2
|
||||||
|
endIndex := startIndex + (vertexCount * 2)
|
||||||
|
if endIndex > len(p) {
|
||||||
|
endIndex = len(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
x := p[startIndex]
|
||||||
|
y := p[startIndex+1]
|
||||||
|
// inline transformation
|
||||||
|
prevX := x*tr[0] + y*tr[2] + tr[4]
|
||||||
|
prevY := x*tr[1] + y*tr[3] + tr[5]
|
||||||
|
|
||||||
|
//! Calculates the clip flags for a point.
|
||||||
|
prevClipFlags := POLYGON_CLIP_NONE
|
||||||
|
if prevX < clipBound[0] {
|
||||||
|
prevClipFlags |= POLYGON_CLIP_LEFT
|
||||||
|
} else if prevX >= clipBound[2] {
|
||||||
|
prevClipFlags |= POLYGON_CLIP_RIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevY < clipBound[1] {
|
||||||
|
prevClipFlags |= POLYGON_CLIP_TOP
|
||||||
|
} else if prevY >= clipBound[3] {
|
||||||
|
prevClipFlags |= POLYGON_CLIP_BOTTOM
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeCount := 0
|
||||||
|
var k, clipFlags, clipSum, clipUnion int
|
||||||
|
var xleft, yleft, xright, yright, oldY, maxX, minX float64
|
||||||
|
var swapWinding int16
|
||||||
|
for n := startIndex; n < endIndex; n = n + 2 {
|
||||||
|
k = (n + 2) % len(p)
|
||||||
|
x = p[k]*tr[0] + p[k+1]*tr[2] + tr[4]
|
||||||
|
y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5]
|
||||||
|
|
||||||
|
//! Calculates the clip flags for a point.
|
||||||
|
clipFlags = POLYGON_CLIP_NONE
|
||||||
|
if prevX < clipBound[0] {
|
||||||
|
clipFlags |= POLYGON_CLIP_LEFT
|
||||||
|
} else if prevX >= clipBound[2] {
|
||||||
|
clipFlags |= POLYGON_CLIP_RIGHT
|
||||||
|
}
|
||||||
|
if prevY < clipBound[1] {
|
||||||
|
clipFlags |= POLYGON_CLIP_TOP
|
||||||
|
} else if prevY >= clipBound[3] {
|
||||||
|
clipFlags |= POLYGON_CLIP_BOTTOM
|
||||||
|
}
|
||||||
|
|
||||||
|
clipSum = prevClipFlags | clipFlags
|
||||||
|
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 {
|
||||||
|
// Both clip to right, edge is a vertical line on the right side
|
||||||
|
if getVerticalEdge(prevY, y, clipBound[2], &(edges[edgeCount]), clipBound) {
|
||||||
|
edgeCount++
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
edgeCount++
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
edgeCount++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Clips to left or right or both.
|
||||||
|
|
||||||
|
if x < prevX {
|
||||||
|
xleft, yleft = x, y
|
||||||
|
xright, yright = prevX, prevY
|
||||||
|
swapWinding = -1
|
||||||
|
} else {
|
||||||
|
xleft, yleft = prevX, prevY
|
||||||
|
xright, yright = x, y
|
||||||
|
swapWinding = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
slope := (yright - yleft) / (xright - xleft)
|
||||||
|
|
||||||
|
if (clipSum & POLYGON_CLIP_RIGHT) != 0 {
|
||||||
|
// calculate new position for the right vertex
|
||||||
|
oldY = yright
|
||||||
|
maxX = clipBound[2]
|
||||||
|
|
||||||
|
yright = yleft + (maxX-xleft)*slope
|
||||||
|
xright = maxX
|
||||||
|
|
||||||
|
// add vertical edge for the overflowing part
|
||||||
|
if getVerticalEdge(yright, oldY, maxX, &(edges[edgeCount]), clipBound) {
|
||||||
|
edges[edgeCount].Winding *= swapWinding
|
||||||
|
edgeCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipSum & POLYGON_CLIP_LEFT) != 0 {
|
||||||
|
// calculate new position for the left vertex
|
||||||
|
oldY = yleft
|
||||||
|
minX = clipBound[0]
|
||||||
|
|
||||||
|
yleft = yleft + (minX-xleft)*slope
|
||||||
|
xleft = minX
|
||||||
|
|
||||||
|
// add vertical edge for the overflowing part
|
||||||
|
if getVerticalEdge(oldY, yleft, minX, &(edges[edgeCount]), clipBound) {
|
||||||
|
edges[edgeCount].Winding *= swapWinding
|
||||||
|
edgeCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if getEdge(xleft, yleft, xright, yright, &(edges[edgeCount]), clipBound) {
|
||||||
|
edges[edgeCount].Winding *= swapWinding
|
||||||
|
edgeCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevClipFlags = clipFlags
|
||||||
|
prevX = x
|
||||||
|
prevY = y
|
||||||
|
}
|
||||||
|
|
||||||
|
return edgeCount
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Creates a polygon edge between two vectors.
|
||||||
|
/*! Clips the edge vertically to the clip rectangle. Returns true for edges that
|
||||||
|
* should be rendered, false for others.
|
||||||
|
*/
|
||||||
|
func getEdge(x0, y0, x1, y1 float64, edge *PolygonEdge, clipBound [4]float64) bool {
|
||||||
|
var startX, startY, endX, endY float64
|
||||||
|
var winding int16
|
||||||
|
|
||||||
|
if y0 <= y1 {
|
||||||
|
startX = x0
|
||||||
|
startY = y0
|
||||||
|
endX = x1
|
||||||
|
endY = y1
|
||||||
|
winding = 1
|
||||||
|
} else {
|
||||||
|
startX = x1
|
||||||
|
startY = y1
|
||||||
|
endX = x0
|
||||||
|
endY = y0
|
||||||
|
winding = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Essentially, firstLine is floor(startY + 1) and lastLine is floor(endY).
|
||||||
|
// These are refactored to integer casts in order to avoid function
|
||||||
|
// calls. The difference with integer cast is that numbers are always
|
||||||
|
// rounded towards zero. Since values smaller than zero get clipped away,
|
||||||
|
// only coordinates between 0 and -1 require greater attention as they
|
||||||
|
// 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))
|
||||||
|
|
||||||
|
minClip := int(clipBound[1])
|
||||||
|
maxClip := int(clipBound[3])
|
||||||
|
|
||||||
|
// If start and end are on the same line, the edge doesn't cross
|
||||||
|
// any lines and thus can be ignored.
|
||||||
|
// If the end is smaller than the first line, edge is out.
|
||||||
|
// If the start is larger than the last line, edge is out.
|
||||||
|
if firstLine > lastLine || lastLine < minClip || firstLine >= maxClip {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust the start based on the target.
|
||||||
|
if firstLine < minClip {
|
||||||
|
firstLine = minClip
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastLine >= maxClip {
|
||||||
|
lastLine = maxClip - 1
|
||||||
|
}
|
||||||
|
edge.Slope = (endX - startX) / (endY - startY)
|
||||||
|
edge.X = startX + (float64(firstLine)-startY)*edge.Slope
|
||||||
|
edge.Winding = winding
|
||||||
|
edge.FirstLine = firstLine
|
||||||
|
edge.LastLine = lastLine
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Creates a vertical polygon edge between two y values.
|
||||||
|
/*! Clips the edge vertically to the clip rectangle. Returns true for edges that
|
||||||
|
* should be rendered, false for others.
|
||||||
|
*/
|
||||||
|
func getVerticalEdge(startY, endY, x float64, edge *PolygonEdge, clipBound [4]float64) bool {
|
||||||
|
var start, end float64
|
||||||
|
var winding int16
|
||||||
|
if startY < endY {
|
||||||
|
start = startY
|
||||||
|
end = endY
|
||||||
|
winding = 1
|
||||||
|
} else {
|
||||||
|
start = endY
|
||||||
|
end = startY
|
||||||
|
winding = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
firstLine := int(math.Floor(start)) + 1
|
||||||
|
lastLine := int(math.Floor(end))
|
||||||
|
|
||||||
|
minClip := int(clipBound[1])
|
||||||
|
maxClip := int(clipBound[3])
|
||||||
|
|
||||||
|
// If start and end are on the same line, the edge doesn't cross
|
||||||
|
// any lines and thus can be ignored.
|
||||||
|
// If the end is smaller than the first line, edge is out.
|
||||||
|
// If the start is larger than the last line, edge is out.
|
||||||
|
if firstLine > lastLine || lastLine < minClip || firstLine >= maxClip {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust the start based on the clip rect.
|
||||||
|
if firstLine < minClip {
|
||||||
|
firstLine = minClip
|
||||||
|
}
|
||||||
|
if lastLine >= maxClip {
|
||||||
|
lastLine = maxClip - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
edge.Slope = 0
|
||||||
|
edge.X = x
|
||||||
|
edge.Winding = winding
|
||||||
|
edge.FirstLine = firstLine
|
||||||
|
edge.LastLine = lastLine
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
124
draw2d/raster/raster_test.go
Normal file
124
draw2d/raster/raster_test.go
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
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 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) {
|
||||||
|
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 BenchmarkRasterizer8BitsSample(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue