Merge pull request #50 from mattermost/mm-1320

HELIUM fixes mm-1320 removes freetype libs and only use solid color for gene…
This commit is contained in:
Corey Hulen
2015-06-22 11:42:54 -08:00
46 changed files with 5 additions and 11460 deletions

15
Godeps/Godeps.json generated
View File

@@ -2,21 +2,6 @@
"ImportPath": "github.com/mattermost/platform",
"GoVersion": "go1.4",
"Deps": [
{
"ImportPath": "code.google.com/p/draw2d/draw2d",
"Comment": "release-20",
"Rev": "eaeb833648eee1b7c20e77ffc8180646c0395298"
},
{
"ImportPath": "code.google.com/p/freetype-go/freetype/raster",
"Comment": "release-85",
"Rev": "46c3056cafbb4da11c4087a892c7d2bfa4224a8f"
},
{
"ImportPath": "code.google.com/p/freetype-go/freetype/truetype",
"Comment": "release-85",
"Rev": "46c3056cafbb4da11c4087a892c7d2bfa4224a8f"
},
{
"ImportPath": "code.google.com/p/go-uuid/uuid",
"Comment": "null-15",

View File

@@ -1,42 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff
package draw2d
import (
"math"
)
//high level path creation
func Rect(path Path, x1, y1, x2, y2 float64) {
path.MoveTo(x1, y1)
path.LineTo(x2, y1)
path.LineTo(x2, y2)
path.LineTo(x1, y2)
path.Close()
}
func RoundRect(path Path, x1, y1, x2, y2, arcWidth, arcHeight float64) {
arcWidth = arcWidth / 2
arcHeight = arcHeight / 2
path.MoveTo(x1, y1+arcHeight)
path.QuadCurveTo(x1, y1, x1+arcWidth, y1)
path.LineTo(x2-arcWidth, y1)
path.QuadCurveTo(x2, y1, x2, y1+arcHeight)
path.LineTo(x2, y2-arcHeight)
path.QuadCurveTo(x2, y2, x2-arcWidth, y2)
path.LineTo(x1+arcWidth, y2)
path.QuadCurveTo(x1, y2, x1, y2-arcHeight)
path.Close()
}
func Ellipse(path Path, cx, cy, rx, ry float64) {
path.ArcTo(cx, cy, rx, ry, 0, -math.Pi*2)
path.Close()
}
func Circle(path Path, cx, cy, radius float64) {
path.ArcTo(cx, cy, radius, radius, 0, -math.Pi*2)
path.Close()
}

View File

@@ -1,67 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"code.google.com/p/freetype-go/freetype/raster"
"math"
)
func arc(t VertexConverter, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) {
end := start + angle
clockWise := true
if angle < 0 {
clockWise = false
}
ra := (math.Abs(rx) + math.Abs(ry)) / 2
da := math.Acos(ra/(ra+0.125/scale)) * 2
//normalize
if !clockWise {
da = -da
}
angle = start + da
var curX, curY float64
for {
if (angle < end-da/4) != clockWise {
curX = x + math.Cos(end)*rx
curY = y + math.Sin(end)*ry
return curX, curY
}
curX = x + math.Cos(angle)*rx
curY = y + math.Sin(angle)*ry
angle += da
t.Vertex(curX, curY)
}
return curX, curY
}
func arcAdder(adder raster.Adder, x, y, rx, ry, start, angle, scale float64) raster.Point {
end := start + angle
clockWise := true
if angle < 0 {
clockWise = false
}
ra := (math.Abs(rx) + math.Abs(ry)) / 2
da := math.Acos(ra/(ra+0.125/scale)) * 2
//normalize
if !clockWise {
da = -da
}
angle = start + da
var curX, curY float64
for {
if (angle < end-da/4) != clockWise {
curX = x + math.Cos(end)*rx
curY = y + math.Sin(end)*ry
return raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)}
}
curX = x + math.Cos(angle)*rx
curY = y + math.Sin(angle)*ry
angle += da
adder.Add1(raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)})
}
return raster.Point{raster.Fix32(curX * 256), raster.Fix32(curY * 256)}
}

View File

@@ -1,11 +0,0 @@
include $(GOROOT)/src/Make.inc
TARG=draw2d.googlecode.com/hg/draw2d/curve
GOFILES=\
cubic_float64.go\
quad_float64.go\
cubic_float64_others.go\
include $(GOROOT)/src/Make.pkg

View File

@@ -1,36 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package curve
import (
"math"
)
func SegmentArc(t LineTracer, x, y, rx, ry, start, angle, scale float64) {
end := start + angle
clockWise := true
if angle < 0 {
clockWise = false
}
ra := (math.Abs(rx) + math.Abs(ry)) / 2
da := math.Acos(ra/(ra+0.125/scale)) * 2
//normalize
if !clockWise {
da = -da
}
angle = start + da
var curX, curY float64
for {
if (angle < end-da/4) != clockWise {
curX = x + math.Cos(end)*rx
curY = y + math.Sin(end)*ry
break;
}
curX = x + math.Cos(angle)*rx
curY = y + math.Sin(angle)*ry
angle += da
t.LineTo(curX, curY)
}
t.LineTo(curX, curY)
}

View File

@@ -1,67 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 17/05/2011 by Laurent Le Goff
package curve
import (
"math"
)
const (
CurveRecursionLimit = 32
)
// X1, Y1, X2, Y2, X3, Y3, X4, Y4 float64
type CubicCurveFloat64 [8]float64
type LineTracer interface {
LineTo(x, y float64)
}
func (c *CubicCurveFloat64) Subdivide(c1, c2 *CubicCurveFloat64) (x23, y23 float64) {
// Calculate all the mid-points of the line segments
//----------------------
c1[0], c1[1] = c[0], c[1]
c2[6], c2[7] = c[6], c[7]
c1[2] = (c[0] + c[2]) / 2
c1[3] = (c[1] + c[3]) / 2
x23 = (c[2] + c[4]) / 2
y23 = (c[3] + c[5]) / 2
c2[4] = (c[4] + c[6]) / 2
c2[5] = (c[5] + c[7]) / 2
c1[4] = (c1[2] + x23) / 2
c1[5] = (c1[3] + y23) / 2
c2[2] = (x23 + c2[4]) / 2
c2[3] = (y23 + c2[5]) / 2
c1[6] = (c1[4] + c2[2]) / 2
c1[7] = (c1[5] + c2[3]) / 2
c2[0], c2[1] = c1[6], c1[7]
return
}
func (curve *CubicCurveFloat64) Segment(t LineTracer, flattening_threshold float64) {
var curves [CurveRecursionLimit]CubicCurveFloat64
curves[0] = *curve
i := 0
// current curve
var c *CubicCurveFloat64
var dx, dy, d2, d3 float64
for i >= 0 {
c = &curves[i]
dx = c[6] - c[0]
dy = c[7] - c[1]
d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
d3 = math.Abs(((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 {
t.LineTo(c[6], c[7])
i--
} else {
// second half of bezier go lower onto the stack
c.Subdivide(&curves[i+1], &curves[i])
i++
}
}
}

View File

@@ -1,696 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 17/05/2011 by Laurent Le Goff
package curve
import (
"math"
)
const (
CurveCollinearityEpsilon = 1e-30
CurveAngleToleranceEpsilon = 0.01
)
//mu ranges from 0 to 1, start to end of curve
func (c *CubicCurveFloat64) ArbitraryPoint(mu float64) (x, y float64) {
mum1 := 1 - mu
mum13 := mum1 * mum1 * mum1
mu3 := mu * mu * mu
x = mum13*c[0] + 3*mu*mum1*mum1*c[2] + 3*mu*mu*mum1*c[4] + mu3*c[6]
y = mum13*c[1] + 3*mu*mum1*mum1*c[3] + 3*mu*mu*mum1*c[5] + mu3*c[7]
return
}
func (c *CubicCurveFloat64) SubdivideAt(c1, c2 *CubicCurveFloat64, t float64) (x23, y23 float64) {
inv_t := (1 - t)
c1[0], c1[1] = c[0], c[1]
c2[6], c2[7] = c[6], c[7]
c1[2] = inv_t*c[0] + t*c[2]
c1[3] = inv_t*c[1] + t*c[3]
x23 = inv_t*c[2] + t*c[4]
y23 = inv_t*c[3] + t*c[5]
c2[4] = inv_t*c[4] + t*c[6]
c2[5] = inv_t*c[5] + t*c[7]
c1[4] = inv_t*c1[2] + t*x23
c1[5] = inv_t*c1[3] + t*y23
c2[2] = inv_t*x23 + t*c2[4]
c2[3] = inv_t*y23 + t*c2[5]
c1[6] = inv_t*c1[4] + t*c2[2]
c1[7] = inv_t*c1[5] + t*c2[3]
c2[0], c2[1] = c1[6], c1[7]
return
}
func (c *CubicCurveFloat64) EstimateDistance() float64 {
dx1 := c[2] - c[0]
dy1 := c[3] - c[1]
dx2 := c[4] - c[2]
dy2 := c[5] - c[3]
dx3 := c[6] - c[4]
dy3 := c[7] - c[5]
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
func (c *CubicCurveFloat64) SegmentRec(t LineTracer, flattening_threshold float64) {
c.segmentRec(t, flattening_threshold)
t.LineTo(c[6], c[7])
}
func (c *CubicCurveFloat64) segmentRec(t LineTracer, flattening_threshold float64) {
var c1, c2 CubicCurveFloat64
c.Subdivide(&c1, &c2)
// Try to approximate the full cubic curve by a single straight line
//------------------
dx := c[6] - c[0]
dy := c[7] - c[1]
d2 := math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
d3 := math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) {
t.LineTo(c[6], c[7])
return
}
// Continue subdivision
//----------------------
c1.segmentRec(t, flattening_threshold)
c2.segmentRec(t, flattening_threshold)
}
/*
The function has the following parameters:
approximationScale :
Eventually determines the approximation accuracy. In practice we need to transform points from the World coordinate system to the Screen one.
It always has some scaling coefficient.
The curves are usually processed in the World coordinates, while the approximation accuracy should be eventually in pixels.
Usually it looks as follows:
curved.approximationScale(transform.scale());
where transform is the affine matrix that includes all the transformations, including viewport and zoom.
angleTolerance :
You set it in radians.
The less this value is the more accurate will be the approximation at sharp turns.
But 0 means that we don't consider angle conditions at all.
cuspLimit :
An angle in radians.
If 0, only the real cusps will have bevel cuts.
If more than 0, it will restrict the sharpness.
The more this value is the less sharp turns will be cut.
Typically it should not exceed 10-15 degrees.
*/
func (c *CubicCurveFloat64) AdaptiveSegmentRec(t LineTracer, approximationScale, angleTolerance, cuspLimit float64) {
cuspLimit = computeCuspLimit(cuspLimit)
distanceToleranceSquare := 0.5 / approximationScale
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
c.adaptiveSegmentRec(t, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
t.LineTo(c[6], c[7])
}
func computeCuspLimit(v float64) (r float64) {
if v == 0.0 {
r = 0.0
} else {
r = math.Pi - v
}
return
}
func squareDistance(x1, y1, x2, y2 float64) float64 {
dx := x2 - x1
dy := y2 - y1
return dx*dx + dy*dy
}
/**
* http://www.antigrain.com/research/adaptive_bezier/index.html
*/
func (c *CubicCurveFloat64) adaptiveSegmentRec(t LineTracer, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) {
if level > CurveRecursionLimit {
return
}
var c1, c2 CubicCurveFloat64
x23, y23 := c.Subdivide(&c1, &c2)
// Try to approximate the full cubic curve by a single straight line
//------------------
dx := c[6] - c[0]
dy := c[7] - c[1]
d2 := math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
d3 := math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
switch {
case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
// All collinear OR p1==p4
//----------------------
k := dx*dx + dy*dy
if k == 0 {
d2 = squareDistance(c[0], c[1], c[2], c[3])
d3 = squareDistance(c[6], c[7], c[4], c[5])
} else {
k = 1 / k
da1 := c[2] - c[0]
da2 := c[3] - c[1]
d2 = k * (da1*dx + da2*dy)
da1 = c[4] - c[0]
da2 = c[5] - c[1]
d3 = k * (da1*dx + da2*dy)
if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
// Simple collinear case, 1---2---3---4
// We can leave just two endpoints
return
}
if d2 <= 0 {
d2 = squareDistance(c[2], c[3], c[0], c[1])
} else if d2 >= 1 {
d2 = squareDistance(c[2], c[3], c[6], c[7])
} else {
d2 = squareDistance(c[2], c[3], c[0]+d2*dx, c[1]+d2*dy)
}
if d3 <= 0 {
d3 = squareDistance(c[4], c[5], c[0], c[1])
} else if d3 >= 1 {
d3 = squareDistance(c[4], c[5], c[6], c[7])
} else {
d3 = squareDistance(c[4], c[5], c[0]+d3*dx, c[1]+d3*dy)
}
}
if d2 > d3 {
if d2 < distanceToleranceSquare {
t.LineTo(c[2], c[3])
return
}
} else {
if d3 < distanceToleranceSquare {
t.LineTo(c[4], c[5])
return
}
}
case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
// p1,p2,p4 are collinear, p3 is significant
//----------------------
if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
if angleTolerance < CurveAngleToleranceEpsilon {
t.LineTo(x23, y23)
return
}
// Angle Condition
//----------------------
da1 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - math.Atan2(c[5]-c[3], c[4]-c[2]))
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da1 < angleTolerance {
t.LineTo(c[2], c[3])
t.LineTo(c[4], c[5])
return
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
t.LineTo(c[4], c[5])
return
}
}
}
case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
// p1,p3,p4 are collinear, p2 is significant
//----------------------
if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
if angleTolerance < CurveAngleToleranceEpsilon {
t.LineTo(x23, y23)
return
}
// Angle Condition
//----------------------
da1 := math.Abs(math.Atan2(c[5]-c[3], c[4]-c[2]) - math.Atan2(c[3]-c[1], c[2]-c[0]))
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da1 < angleTolerance {
t.LineTo(c[2], c[3])
t.LineTo(c[4], c[5])
return
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
t.LineTo(c[2], c[3])
return
}
}
}
case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
// Regular case
//-----------------
if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) {
// If the curvature doesn't exceed the distanceTolerance value
// we tend to finish subdivisions.
//----------------------
if angleTolerance < CurveAngleToleranceEpsilon {
t.LineTo(x23, y23)
return
}
// Angle & Cusp Condition
//----------------------
k := math.Atan2(c[5]-c[3], c[4]-c[2])
da1 := math.Abs(k - math.Atan2(c[3]-c[1], c[2]-c[0]))
da2 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - k)
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da2 >= math.Pi {
da2 = 2*math.Pi - da2
}
if da1+da2 < angleTolerance {
// Finally we can stop the recursion
//----------------------
t.LineTo(x23, y23)
return
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
t.LineTo(c[2], c[3])
return
}
if da2 > cuspLimit {
t.LineTo(c[4], c[5])
return
}
}
}
}
// Continue subdivision
//----------------------
c1.adaptiveSegmentRec(t, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
c2.adaptiveSegmentRec(t, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
}
func (curve *CubicCurveFloat64) AdaptiveSegment(t LineTracer, approximationScale, angleTolerance, cuspLimit float64) {
cuspLimit = computeCuspLimit(cuspLimit)
distanceToleranceSquare := 0.5 / approximationScale
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
var curves [CurveRecursionLimit]CubicCurveFloat64
curves[0] = *curve
i := 0
// current curve
var c *CubicCurveFloat64
var c1, c2 CubicCurveFloat64
var dx, dy, d2, d3, k, x23, y23 float64
for i >= 0 {
c = &curves[i]
x23, y23 = c.Subdivide(&c1, &c2)
// Try to approximate the full cubic curve by a single straight line
//------------------
dx = c[6] - c[0]
dy = c[7] - c[1]
d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
d3 = math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
switch {
case i == len(curves)-1:
t.LineTo(c[6], c[7])
i--
continue
case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
// All collinear OR p1==p4
//----------------------
k = dx*dx + dy*dy
if k == 0 {
d2 = squareDistance(c[0], c[1], c[2], c[3])
d3 = squareDistance(c[6], c[7], c[4], c[5])
} else {
k = 1 / k
da1 := c[2] - c[0]
da2 := c[3] - c[1]
d2 = k * (da1*dx + da2*dy)
da1 = c[4] - c[0]
da2 = c[5] - c[1]
d3 = k * (da1*dx + da2*dy)
if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
// Simple collinear case, 1---2---3---4
// We can leave just two endpoints
i--
continue
}
if d2 <= 0 {
d2 = squareDistance(c[2], c[3], c[0], c[1])
} else if d2 >= 1 {
d2 = squareDistance(c[2], c[3], c[6], c[7])
} else {
d2 = squareDistance(c[2], c[3], c[0]+d2*dx, c[1]+d2*dy)
}
if d3 <= 0 {
d3 = squareDistance(c[4], c[5], c[0], c[1])
} else if d3 >= 1 {
d3 = squareDistance(c[4], c[5], c[6], c[7])
} else {
d3 = squareDistance(c[4], c[5], c[0]+d3*dx, c[1]+d3*dy)
}
}
if d2 > d3 {
if d2 < distanceToleranceSquare {
t.LineTo(c[2], c[3])
i--
continue
}
} else {
if d3 < distanceToleranceSquare {
t.LineTo(c[4], c[5])
i--
continue
}
}
case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
// p1,p2,p4 are collinear, p3 is significant
//----------------------
if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
if angleTolerance < CurveAngleToleranceEpsilon {
t.LineTo(x23, y23)
i--
continue
}
// Angle Condition
//----------------------
da1 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - math.Atan2(c[5]-c[3], c[4]-c[2]))
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da1 < angleTolerance {
t.LineTo(c[2], c[3])
t.LineTo(c[4], c[5])
i--
continue
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
t.LineTo(c[4], c[5])
i--
continue
}
}
}
case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
// p1,p3,p4 are collinear, p2 is significant
//----------------------
if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
if angleTolerance < CurveAngleToleranceEpsilon {
t.LineTo(x23, y23)
i--
continue
}
// Angle Condition
//----------------------
da1 := math.Abs(math.Atan2(c[5]-c[3], c[4]-c[2]) - math.Atan2(c[3]-c[1], c[2]-c[0]))
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da1 < angleTolerance {
t.LineTo(c[2], c[3])
t.LineTo(c[4], c[5])
i--
continue
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
t.LineTo(c[2], c[3])
i--
continue
}
}
}
case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
// Regular case
//-----------------
if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) {
// If the curvature doesn't exceed the distanceTolerance value
// we tend to finish subdivisions.
//----------------------
if angleTolerance < CurveAngleToleranceEpsilon {
t.LineTo(x23, y23)
i--
continue
}
// Angle & Cusp Condition
//----------------------
k := math.Atan2(c[5]-c[3], c[4]-c[2])
da1 := math.Abs(k - math.Atan2(c[3]-c[1], c[2]-c[0]))
da2 := math.Abs(math.Atan2(c[7]-c[5], c[6]-c[4]) - k)
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da2 >= math.Pi {
da2 = 2*math.Pi - da2
}
if da1+da2 < angleTolerance {
// Finally we can stop the recursion
//----------------------
t.LineTo(x23, y23)
i--
continue
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
t.LineTo(c[2], c[3])
i--
continue
}
if da2 > cuspLimit {
t.LineTo(c[4], c[5])
i--
continue
}
}
}
}
// Continue subdivision
//----------------------
curves[i+1], curves[i] = c1, c2
i++
}
t.LineTo(curve[6], curve[7])
}
/********************** Ahmad thesis *******************/
/**************************************************************************************
* This code is the implementation of the Parabolic Approximation (PA). Although *
* it uses recursive subdivision as a safe net for the failing cases, this is an *
* iterative routine and reduces considerably the number of vertices (point) *
* generation. *
**************************************************************************************/
func (c *CubicCurveFloat64) ParabolicSegment(t LineTracer, flattening_threshold float64) {
estimatedIFP := c.numberOfInflectionPoints()
if estimatedIFP == 0 {
// If no inflection points then apply PA on the full Bezier segment.
c.doParabolicApproximation(t, flattening_threshold)
return
}
// If one or more inflection point then we will have to subdivide the curve
numOfIfP, t1, t2 := c.findInflectionPoints()
if numOfIfP == 2 {
// Case when 2 inflection points then divide at the smallest one first
var sub1, tmp1, sub2, sub3 CubicCurveFloat64
c.SubdivideAt(&sub1, &tmp1, t1)
// Now find the second inflection point in the second curve an subdivide
numOfIfP, t1, t2 = tmp1.findInflectionPoints()
if numOfIfP == 2 {
tmp1.SubdivideAt(&sub2, &sub3, t2)
} else if numOfIfP == 1 {
tmp1.SubdivideAt(&sub2, &sub3, t1)
} else {
return
}
// Use PA for first subsegment
sub1.doParabolicApproximation(t, flattening_threshold)
// Use RS for the second (middle) subsegment
sub2.Segment(t, flattening_threshold)
// Drop the last point in the array will be added by the PA in third subsegment
//noOfPoints--;
// Use PA for the third curve
sub3.doParabolicApproximation(t, flattening_threshold)
} else if numOfIfP == 1 {
// Case where there is one inflection point, subdivide once and use PA on
// both subsegments
var sub1, sub2 CubicCurveFloat64
c.SubdivideAt(&sub1, &sub2, t1)
sub1.doParabolicApproximation(t, flattening_threshold)
//noOfPoints--;
sub2.doParabolicApproximation(t, flattening_threshold)
} else {
// Case where there is no inflection USA PA directly
c.doParabolicApproximation(t, flattening_threshold)
}
}
// Find the third control point deviation form the axis
func (c *CubicCurveFloat64) thirdControlPointDeviation() float64 {
dx := c[2] - c[0]
dy := c[3] - c[1]
l2 := dx*dx + dy*dy
if l2 == 0 {
return 0
}
l := math.Sqrt(l2)
r := (c[3] - c[1]) / l
s := (c[0] - c[2]) / l
u := (c[2]*c[1] - c[0]*c[3]) / l
return math.Abs(r*c[4] + s*c[5] + u)
}
// Find the number of inflection point
func (c *CubicCurveFloat64) numberOfInflectionPoints() int {
dx21 := (c[2] - c[0])
dy21 := (c[3] - c[1])
dx32 := (c[4] - c[2])
dy32 := (c[5] - c[3])
dx43 := (c[6] - c[4])
dy43 := (c[7] - c[5])
if ((dx21*dy32 - dy21*dx32) * (dx32*dy43 - dy32*dx43)) < 0 {
return 1 // One inflection point
} else if ((dx21*dy32 - dy21*dx32) * (dx21*dy43 - dy21*dx43)) > 0 {
return 0 // No inflection point
} else {
// Most cases no inflection point
b1 := (dx21*dx32 + dy21*dy32) > 0
b2 := (dx32*dx43 + dy32*dy43) > 0
if b1 || b2 && !(b1 && b2) { // xor!!
return 0
}
}
return -1 // cases where there in zero or two inflection points
}
// This is the main function where all the work is done
func (curve *CubicCurveFloat64) doParabolicApproximation(tracer LineTracer, flattening_threshold float64) {
var c *CubicCurveFloat64
c = curve
var d, t, dx, dy, d2, d3 float64
for {
dx = c[6] - c[0]
dy = c[7] - c[1]
d2 = math.Abs(((c[2]-c[6])*dy - (c[3]-c[7])*dx))
d3 = math.Abs(((c[4]-c[6])*dy - (c[5]-c[7])*dx))
if (d2+d3)*(d2+d3) < flattening_threshold*(dx*dx+dy*dy) {
// If the subsegment deviation satisfy the flatness then store the last
// point and stop
tracer.LineTo(c[6], c[7])
break
}
// Find the third control point deviation and the t values for subdivision
d = c.thirdControlPointDeviation()
t = 2 * math.Sqrt(flattening_threshold/d/3)
if t > 1 {
// Case where the t value calculated is invalid so using RS
c.Segment(tracer, flattening_threshold)
break
}
// Valid t value to subdivide at that calculated value
var b1, b2 CubicCurveFloat64
c.SubdivideAt(&b1, &b2, t)
// First subsegment should have its deviation equal to flatness
dx = b1[6] - b1[0]
dy = b1[7] - b1[1]
d2 = math.Abs(((b1[2]-b1[6])*dy - (b1[3]-b1[7])*dx))
d3 = math.Abs(((b1[4]-b1[6])*dy - (b1[5]-b1[7])*dx))
if (d2+d3)*(d2+d3) > flattening_threshold*(dx*dx+dy*dy) {
// if not then use RS to handle any mathematical errors
b1.Segment(tracer, flattening_threshold)
} else {
tracer.LineTo(b1[6], b1[7])
}
// repeat the process for the left over subsegment.
c = &b2
}
}
// Find the actual inflection points and return the number of inflection points found
// if 2 inflection points found, the first one returned will be with smaller t value.
func (curve *CubicCurveFloat64) findInflectionPoints() (int, firstIfp, secondIfp float64) {
// For Cubic Bezier curve with equation P=a*t^3 + b*t^2 + c*t + d
// slope of the curve dP/dt = 3*a*t^2 + 2*b*t + c
// a = (float)(-bez.p1 + 3*bez.p2 - 3*bez.p3 + bez.p4);
// b = (float)(3*bez.p1 - 6*bez.p2 + 3*bez.p3);
// c = (float)(-3*bez.p1 + 3*bez.p2);
ax := (-curve[0] + 3*curve[2] - 3*curve[4] + curve[6])
bx := (3*curve[0] - 6*curve[2] + 3*curve[4])
cx := (-3*curve[0] + 3*curve[2])
ay := (-curve[1] + 3*curve[3] - 3*curve[5] + curve[7])
by := (3*curve[1] - 6*curve[3] + 3*curve[5])
cy := (-3*curve[1] + 3*curve[3])
a := (3 * (ay*bx - ax*by))
b := (3 * (ay*cx - ax*cy))
c := (by*cx - bx*cy)
r2 := (b*b - 4*a*c)
firstIfp = 0.0
secondIfp = 0.0
if r2 >= 0.0 && a != 0.0 {
r := math.Sqrt(r2)
firstIfp = ((-b + r) / (2 * a))
secondIfp = ((-b - r) / (2 * a))
if (firstIfp > 0.0 && firstIfp < 1.0) && (secondIfp > 0.0 && secondIfp < 1.0) {
if firstIfp > secondIfp {
tmp := firstIfp
firstIfp = secondIfp
secondIfp = tmp
}
if secondIfp-firstIfp > 0.00001 {
return 2, firstIfp, secondIfp
} else {
return 1, firstIfp, secondIfp
}
} else if firstIfp > 0.0 && firstIfp < 1.0 {
return 1, firstIfp, secondIfp
} else if secondIfp > 0.0 && secondIfp < 1.0 {
firstIfp = secondIfp
return 1, firstIfp, secondIfp
}
return 0, firstIfp, secondIfp
}
return 0, firstIfp, secondIfp
}

View File

@@ -1,262 +0,0 @@
package curve
import (
"bufio"
"code.google.com/p/draw2d/draw2d/raster"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"log"
"os"
"testing"
)
var (
flattening_threshold float64 = 0.5
testsCubicFloat64 = []CubicCurveFloat64{
CubicCurveFloat64{100, 100, 200, 100, 100, 200, 200, 200},
CubicCurveFloat64{100, 100, 300, 200, 200, 200, 300, 100},
CubicCurveFloat64{100, 100, 0, 300, 200, 0, 300, 300},
CubicCurveFloat64{150, 290, 10, 10, 290, 10, 150, 290},
CubicCurveFloat64{10, 290, 10, 10, 290, 10, 290, 290},
CubicCurveFloat64{100, 290, 290, 10, 10, 10, 200, 290},
}
testsQuadFloat64 = []QuadCurveFloat64{
QuadCurveFloat64{100, 100, 200, 100, 200, 200},
QuadCurveFloat64{100, 100, 290, 200, 290, 100},
QuadCurveFloat64{100, 100, 0, 290, 200, 290},
QuadCurveFloat64{150, 290, 10, 10, 290, 290},
QuadCurveFloat64{10, 290, 10, 10, 290, 290},
QuadCurveFloat64{100, 290, 290, 10, 120, 290},
}
)
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 init() {
f, err := os.Create("_test.html")
if err != nil {
log.Println(err)
os.Exit(1)
}
defer f.Close()
log.Printf("Create html viewer")
f.Write([]byte("<html><body>"))
for i := 0; i < len(testsCubicFloat64); i++ {
f.Write([]byte(fmt.Sprintf("<div><img src='_testRec%d.png'/>\n<img src='_test%d.png'/>\n<img src='_testAdaptiveRec%d.png'/>\n<img src='_testAdaptive%d.png'/>\n<img src='_testParabolic%d.png'/>\n</div>\n", i, i, i, i, i)))
}
for i := 0; i < len(testsQuadFloat64); i++ {
f.Write([]byte(fmt.Sprintf("<div><img src='_testQuad%d.png'/>\n</div>\n", i)))
}
f.Write([]byte("</body></html>"))
}
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)
}
}
func drawPoints(img draw.Image, c color.Color, s ...float64) image.Image {
/*for i := 0; i < len(s); i += 2 {
x, y := int(s[i]+0.5), int(s[i+1]+0.5)
img.Set(x, y, c)
img.Set(x, y+1, c)
img.Set(x, y-1, c)
img.Set(x+1, y, c)
img.Set(x+1, y+1, c)
img.Set(x+1, y-1, c)
img.Set(x-1, y, c)
img.Set(x-1, y+1, c)
img.Set(x-1, y-1, c)
}*/
return img
}
func TestCubicCurveRec(t *testing.T) {
for i, curve := range testsCubicFloat64 {
var p Path
p.LineTo(curve[0], curve[1])
curve.SegmentRec(&p, flattening_threshold)
img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
raster.PolylineBresenham(img, image.Black, p.points...)
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
savepng(fmt.Sprintf("_testRec%d.png", i), img)
log.Printf("Num of points: %d\n", len(p.points))
}
fmt.Println()
}
func TestCubicCurve(t *testing.T) {
for i, curve := range testsCubicFloat64 {
var p Path
p.LineTo(curve[0], curve[1])
curve.Segment(&p, flattening_threshold)
img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
raster.PolylineBresenham(img, image.Black, p.points...)
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
savepng(fmt.Sprintf("_test%d.png", i), img)
log.Printf("Num of points: %d\n", len(p.points))
}
fmt.Println()
}
func TestCubicCurveAdaptiveRec(t *testing.T) {
for i, curve := range testsCubicFloat64 {
var p Path
p.LineTo(curve[0], curve[1])
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
raster.PolylineBresenham(img, image.Black, p.points...)
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
savepng(fmt.Sprintf("_testAdaptiveRec%d.png", i), img)
log.Printf("Num of points: %d\n", len(p.points))
}
fmt.Println()
}
func TestCubicCurveAdaptive(t *testing.T) {
for i, curve := range testsCubicFloat64 {
var p Path
p.LineTo(curve[0], curve[1])
curve.AdaptiveSegment(&p, 1, 0, 0)
img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
raster.PolylineBresenham(img, image.Black, p.points...)
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
savepng(fmt.Sprintf("_testAdaptive%d.png", i), img)
log.Printf("Num of points: %d\n", len(p.points))
}
fmt.Println()
}
func TestCubicCurveParabolic(t *testing.T) {
for i, curve := range testsCubicFloat64 {
var p Path
p.LineTo(curve[0], curve[1])
curve.ParabolicSegment(&p, flattening_threshold)
img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
raster.PolylineBresenham(img, image.Black, p.points...)
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
savepng(fmt.Sprintf("_testParabolic%d.png", i), img)
log.Printf("Num of points: %d\n", len(p.points))
}
fmt.Println()
}
func TestQuadCurve(t *testing.T) {
for i, curve := range testsQuadFloat64 {
var p Path
p.LineTo(curve[0], curve[1])
curve.Segment(&p, flattening_threshold)
img := image.NewNRGBA(image.Rect(0, 0, 300, 300))
raster.PolylineBresenham(img, color.NRGBA{0xff, 0, 0, 0xff}, curve[:]...)
raster.PolylineBresenham(img, image.Black, p.points...)
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.points...)
savepng(fmt.Sprintf("_testQuad%d.png", i), img)
log.Printf("Num of points: %d\n", len(p.points))
}
fmt.Println()
}
func BenchmarkCubicCurveRec(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, curve := range testsCubicFloat64 {
p := Path{make([]float64, 0, 32)}
p.LineTo(curve[0], curve[1])
curve.SegmentRec(&p, flattening_threshold)
}
}
}
func BenchmarkCubicCurve(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, curve := range testsCubicFloat64 {
p := Path{make([]float64, 0, 32)}
p.LineTo(curve[0], curve[1])
curve.Segment(&p, flattening_threshold)
}
}
}
func BenchmarkCubicCurveAdaptiveRec(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, curve := range testsCubicFloat64 {
p := Path{make([]float64, 0, 32)}
p.LineTo(curve[0], curve[1])
curve.AdaptiveSegmentRec(&p, 1, 0, 0)
}
}
}
func BenchmarkCubicCurveAdaptive(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, curve := range testsCubicFloat64 {
p := Path{make([]float64, 0, 32)}
p.LineTo(curve[0], curve[1])
curve.AdaptiveSegment(&p, 1, 0, 0)
}
}
}
func BenchmarkCubicCurveParabolic(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, curve := range testsCubicFloat64 {
p := Path{make([]float64, 0, 32)}
p.LineTo(curve[0], curve[1])
curve.ParabolicSegment(&p, flattening_threshold)
}
}
}
func BenchmarkQuadCurve(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, curve := range testsQuadFloat64 {
p := Path{make([]float64, 0, 32)}
p.LineTo(curve[0], curve[1])
curve.Segment(&p, flattening_threshold)
}
}
}

View File

@@ -1,51 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 17/05/2011 by Laurent Le Goff
package curve
import (
"math"
)
//X1, Y1, X2, Y2, X3, Y3 float64
type QuadCurveFloat64 [6]float64
func (c *QuadCurveFloat64) Subdivide(c1, c2 *QuadCurveFloat64) {
// Calculate all the mid-points of the line segments
//----------------------
c1[0], c1[1] = c[0], c[1]
c2[4], c2[5] = c[4], c[5]
c1[2] = (c[0] + c[2]) / 2
c1[3] = (c[1] + c[3]) / 2
c2[2] = (c[2] + c[4]) / 2
c2[3] = (c[3] + c[5]) / 2
c1[4] = (c1[2] + c2[2]) / 2
c1[5] = (c1[3] + c2[3]) / 2
c2[0], c2[1] = c1[4], c1[5]
return
}
func (curve *QuadCurveFloat64) Segment(t LineTracer, flattening_threshold float64) {
var curves [CurveRecursionLimit]QuadCurveFloat64
curves[0] = *curve
i := 0
// current curve
var c *QuadCurveFloat64
var dx, dy, d float64
for i >= 0 {
c = &curves[i]
dx = c[4] - c[0]
dy = c[5] - c[1]
d = math.Abs(((c[2]-c[4])*dy - (c[3]-c[5])*dx))
if (d*d) < flattening_threshold*(dx*dx+dy*dy) || i == len(curves)-1 {
t.LineTo(c[4], c[5])
i--
} else {
// second half of bezier go lower onto the stack
c.Subdivide(&curves[i+1], &curves[i])
i++
}
}
}

View File

@@ -1,336 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"math"
)
var (
CurveRecursionLimit = 32
CurveCollinearityEpsilon = 1e-30
CurveAngleToleranceEpsilon = 0.01
)
/*
The function has the following parameters:
approximationScale :
Eventually determines the approximation accuracy. In practice we need to transform points from the World coordinate system to the Screen one.
It always has some scaling coefficient.
The curves are usually processed in the World coordinates, while the approximation accuracy should be eventually in pixels.
Usually it looks as follows:
curved.approximationScale(transform.scale());
where transform is the affine matrix that includes all the transformations, including viewport and zoom.
angleTolerance :
You set it in radians.
The less this value is the more accurate will be the approximation at sharp turns.
But 0 means that we don't consider angle conditions at all.
cuspLimit :
An angle in radians.
If 0, only the real cusps will have bevel cuts.
If more than 0, it will restrict the sharpness.
The more this value is the less sharp turns will be cut.
Typically it should not exceed 10-15 degrees.
*/
func cubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4, approximationScale, angleTolerance, cuspLimit float64) {
cuspLimit = computeCuspLimit(cuspLimit)
distanceToleranceSquare := 0.5 / approximationScale
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
recursiveCubicBezier(v, x1, y1, x2, y2, x3, y3, x4, y4, 0, distanceToleranceSquare, angleTolerance, cuspLimit)
}
/*
* see cubicBezier comments for approximationScale and angleTolerance definition
*/
func quadraticBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, approximationScale, angleTolerance float64) {
distanceToleranceSquare := 0.5 / approximationScale
distanceToleranceSquare = distanceToleranceSquare * distanceToleranceSquare
recursiveQuadraticBezierBezier(v, x1, y1, x2, y2, x3, y3, 0, distanceToleranceSquare, angleTolerance)
}
func computeCuspLimit(v float64) (r float64) {
if v == 0.0 {
r = 0.0
} else {
r = math.Pi - v
}
return
}
/**
* http://www.antigrain.com/research/adaptive_bezier/index.html
*/
func recursiveQuadraticBezierBezier(v VertexConverter, x1, y1, x2, y2, x3, y3 float64, level int, distanceToleranceSquare, angleTolerance float64) {
if level > CurveRecursionLimit {
return
}
// Calculate all the mid-points of the line segments
//----------------------
x12 := (x1 + x2) / 2
y12 := (y1 + y2) / 2
x23 := (x2 + x3) / 2
y23 := (y2 + y3) / 2
x123 := (x12 + x23) / 2
y123 := (y12 + y23) / 2
dx := x3 - x1
dy := y3 - y1
d := math.Abs(((x2-x3)*dy - (y2-y3)*dx))
if d > CurveCollinearityEpsilon {
// Regular case
//-----------------
if d*d <= distanceToleranceSquare*(dx*dx+dy*dy) {
// If the curvature doesn't exceed the distanceTolerance value
// we tend to finish subdivisions.
//----------------------
if angleTolerance < CurveAngleToleranceEpsilon {
v.Vertex(x123, y123)
return
}
// Angle & Cusp Condition
//----------------------
da := math.Abs(math.Atan2(y3-y2, x3-x2) - math.Atan2(y2-y1, x2-x1))
if da >= math.Pi {
da = 2*math.Pi - da
}
if da < angleTolerance {
// Finally we can stop the recursion
//----------------------
v.Vertex(x123, y123)
return
}
}
} else {
// Collinear case
//------------------
da := dx*dx + dy*dy
if da == 0 {
d = squareDistance(x1, y1, x2, y2)
} else {
d = ((x2-x1)*dx + (y2-y1)*dy) / da
if d > 0 && d < 1 {
// Simple collinear case, 1---2---3
// We can leave just two endpoints
return
}
if d <= 0 {
d = squareDistance(x2, y2, x1, y1)
} else if d >= 1 {
d = squareDistance(x2, y2, x3, y3)
} else {
d = squareDistance(x2, y2, x1+d*dx, y1+d*dy)
}
}
if d < distanceToleranceSquare {
v.Vertex(x2, y2)
return
}
}
// Continue subdivision
//----------------------
recursiveQuadraticBezierBezier(v, x1, y1, x12, y12, x123, y123, level+1, distanceToleranceSquare, angleTolerance)
recursiveQuadraticBezierBezier(v, x123, y123, x23, y23, x3, y3, level+1, distanceToleranceSquare, angleTolerance)
}
/**
* http://www.antigrain.com/research/adaptive_bezier/index.html
*/
func recursiveCubicBezier(v VertexConverter, x1, y1, x2, y2, x3, y3, x4, y4 float64, level int, distanceToleranceSquare, angleTolerance, cuspLimit float64) {
if level > CurveRecursionLimit {
return
}
// Calculate all the mid-points of the line segments
//----------------------
x12 := (x1 + x2) / 2
y12 := (y1 + y2) / 2
x23 := (x2 + x3) / 2
y23 := (y2 + y3) / 2
x34 := (x3 + x4) / 2
y34 := (y3 + y4) / 2
x123 := (x12 + x23) / 2
y123 := (y12 + y23) / 2
x234 := (x23 + x34) / 2
y234 := (y23 + y34) / 2
x1234 := (x123 + x234) / 2
y1234 := (y123 + y234) / 2
// Try to approximate the full cubic curve by a single straight line
//------------------
dx := x4 - x1
dy := y4 - y1
d2 := math.Abs(((x2-x4)*dy - (y2-y4)*dx))
d3 := math.Abs(((x3-x4)*dy - (y3-y4)*dx))
switch {
case d2 <= CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
// All collinear OR p1==p4
//----------------------
k := dx*dx + dy*dy
if k == 0 {
d2 = squareDistance(x1, y1, x2, y2)
d3 = squareDistance(x4, y4, x3, y3)
} else {
k = 1 / k
da1 := x2 - x1
da2 := y2 - y1
d2 = k * (da1*dx + da2*dy)
da1 = x3 - x1
da2 = y3 - y1
d3 = k * (da1*dx + da2*dy)
if d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 {
// Simple collinear case, 1---2---3---4
// We can leave just two endpoints
return
}
if d2 <= 0 {
d2 = squareDistance(x2, y2, x1, y1)
} else if d2 >= 1 {
d2 = squareDistance(x2, y2, x4, y4)
} else {
d2 = squareDistance(x2, y2, x1+d2*dx, y1+d2*dy)
}
if d3 <= 0 {
d3 = squareDistance(x3, y3, x1, y1)
} else if d3 >= 1 {
d3 = squareDistance(x3, y3, x4, y4)
} else {
d3 = squareDistance(x3, y3, x1+d3*dx, y1+d3*dy)
}
}
if d2 > d3 {
if d2 < distanceToleranceSquare {
v.Vertex(x2, y2)
return
}
} else {
if d3 < distanceToleranceSquare {
v.Vertex(x3, y3)
return
}
}
break
case d2 <= CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
// p1,p2,p4 are collinear, p3 is significant
//----------------------
if d3*d3 <= distanceToleranceSquare*(dx*dx+dy*dy) {
if angleTolerance < CurveAngleToleranceEpsilon {
v.Vertex(x23, y23)
return
}
// Angle Condition
//----------------------
da1 := math.Abs(math.Atan2(y4-y3, x4-x3) - math.Atan2(y3-y2, x3-x2))
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da1 < angleTolerance {
v.Vertex(x2, y2)
v.Vertex(x3, y3)
return
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
v.Vertex(x3, y3)
return
}
}
}
break
case d2 > CurveCollinearityEpsilon && d3 <= CurveCollinearityEpsilon:
// p1,p3,p4 are collinear, p2 is significant
//----------------------
if d2*d2 <= distanceToleranceSquare*(dx*dx+dy*dy) {
if angleTolerance < CurveAngleToleranceEpsilon {
v.Vertex(x23, y23)
return
}
// Angle Condition
//----------------------
da1 := math.Abs(math.Atan2(y3-y2, x3-x2) - math.Atan2(y2-y1, x2-x1))
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da1 < angleTolerance {
v.Vertex(x2, y2)
v.Vertex(x3, y3)
return
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
v.Vertex(x2, y2)
return
}
}
}
break
case d2 > CurveCollinearityEpsilon && d3 > CurveCollinearityEpsilon:
// Regular case
//-----------------
if (d2+d3)*(d2+d3) <= distanceToleranceSquare*(dx*dx+dy*dy) {
// If the curvature doesn't exceed the distanceTolerance value
// we tend to finish subdivisions.
//----------------------
if angleTolerance < CurveAngleToleranceEpsilon {
v.Vertex(x23, y23)
return
}
// Angle & Cusp Condition
//----------------------
k := math.Atan2(y3-y2, x3-x2)
da1 := math.Abs(k - math.Atan2(y2-y1, x2-x1))
da2 := math.Abs(math.Atan2(y4-y3, x4-x3) - k)
if da1 >= math.Pi {
da1 = 2*math.Pi - da1
}
if da2 >= math.Pi {
da2 = 2*math.Pi - da2
}
if da1+da2 < angleTolerance {
// Finally we can stop the recursion
//----------------------
v.Vertex(x23, y23)
return
}
if cuspLimit != 0.0 {
if da1 > cuspLimit {
v.Vertex(x2, y2)
return
}
if da2 > cuspLimit {
v.Vertex(x3, y3)
return
}
}
}
break
}
// Continue subdivision
//----------------------
recursiveCubicBezier(v, x1, y1, x12, y12, x123, y123, x1234, y1234, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
recursiveCubicBezier(v, x1234, y1234, x234, y234, x34, y34, x4, y4, level+1, distanceToleranceSquare, angleTolerance, cuspLimit)
}

View File

@@ -1,90 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff
package draw2d
type DashVertexConverter struct {
command VertexCommand
next VertexConverter
x, y, distance float64
dash []float64
currentDash int
dashOffset float64
}
func NewDashConverter(dash []float64, dashOffset float64, converter VertexConverter) *DashVertexConverter {
var dasher DashVertexConverter
dasher.dash = dash
dasher.currentDash = 0
dasher.dashOffset = dashOffset
dasher.next = converter
return &dasher
}
func (dasher *DashVertexConverter) NextCommand(cmd VertexCommand) {
dasher.command = cmd
if dasher.command == VertexStopCommand {
dasher.next.NextCommand(VertexStopCommand)
}
}
func (dasher *DashVertexConverter) Vertex(x, y float64) {
switch dasher.command {
case VertexStartCommand:
dasher.start(x, y)
default:
dasher.lineTo(x, y)
}
dasher.command = VertexNoCommand
}
func (dasher *DashVertexConverter) start(x, y float64) {
dasher.next.NextCommand(VertexStartCommand)
dasher.next.Vertex(x, y)
dasher.x, dasher.y = x, y
dasher.distance = dasher.dashOffset
dasher.currentDash = 0
}
func (dasher *DashVertexConverter) lineTo(x, y float64) {
rest := dasher.dash[dasher.currentDash] - dasher.distance
for rest < 0 {
dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
rest = dasher.dash[dasher.currentDash] - dasher.distance
}
d := distance(dasher.x, dasher.y, x, y)
for d >= rest {
k := rest / d
lx := dasher.x + k*(x-dasher.x)
ly := dasher.y + k*(y-dasher.y)
if dasher.currentDash%2 == 0 {
// line
dasher.next.Vertex(lx, ly)
} else {
// gap
dasher.next.NextCommand(VertexStopCommand)
dasher.next.NextCommand(VertexStartCommand)
dasher.next.Vertex(lx, ly)
}
d = d - rest
dasher.x, dasher.y = lx, ly
dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
rest = dasher.dash[dasher.currentDash]
}
dasher.distance = d
if dasher.currentDash%2 == 0 {
// line
dasher.next.Vertex(x, y)
} else {
// gap
dasher.next.NextCommand(VertexStopCommand)
dasher.next.NextCommand(VertexStartCommand)
dasher.next.Vertex(x, y)
}
if dasher.distance >= dasher.dash[dasher.currentDash] {
dasher.distance = dasher.distance - dasher.dash[dasher.currentDash]
dasher.currentDash = (dasher.currentDash + 1) % len(dasher.dash)
}
dasher.x, dasher.y = x, y
}

View File

@@ -1,23 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff
package draw2d
type DemuxConverter struct {
converters []VertexConverter
}
func NewDemuxConverter(converters ...VertexConverter) *DemuxConverter {
return &DemuxConverter{converters}
}
func (dc *DemuxConverter) NextCommand(cmd VertexCommand) {
for _, converter := range dc.converters {
converter.NextCommand(cmd)
}
}
func (dc *DemuxConverter) Vertex(x, y float64) {
for _, converter := range dc.converters {
converter.Vertex(x, y)
}
}

View File

@@ -1,5 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff
// The package draw2d provide a Graphic Context that can draw vectorial figure on surface.
package draw2d

View File

@@ -1,97 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff
package draw2d
import (
"code.google.com/p/freetype-go/freetype/truetype"
"io/ioutil"
"log"
"path"
)
var (
fontFolder = "../resource/font/"
fonts = make(map[string]*truetype.Font)
)
type FontStyle byte
const (
FontStyleNormal FontStyle = iota
FontStyleBold
FontStyleItalic
)
type FontFamily byte
const (
FontFamilySans FontFamily = iota
FontFamilySerif
FontFamilyMono
)
type FontData struct {
Name string
Family FontFamily
Style FontStyle
}
func fontFileName(fontData FontData) string {
fontFileName := fontData.Name
switch fontData.Family {
case FontFamilySans:
fontFileName += "s"
case FontFamilySerif:
fontFileName += "r"
case FontFamilyMono:
fontFileName += "m"
}
if fontData.Style&FontStyleBold != 0 {
fontFileName += "b"
} else {
fontFileName += "r"
}
if fontData.Style&FontStyleItalic != 0 {
fontFileName += "i"
}
fontFileName += ".ttf"
return fontFileName
}
func RegisterFont(fontData FontData, font *truetype.Font) {
fonts[fontFileName(fontData)] = font
}
func GetFont(fontData FontData) *truetype.Font {
fontFileName := fontFileName(fontData)
font := fonts[fontFileName]
if font != nil {
return font
}
fonts[fontFileName] = loadFont(fontFileName)
return fonts[fontFileName]
}
func GetFontFolder() string {
return fontFolder
}
func SetFontFolder(folder string) {
fontFolder = folder
}
func loadFont(fontFileName string) *truetype.Font {
fontBytes, err := ioutil.ReadFile(path.Join(fontFolder, fontFileName))
if err != nil {
log.Println(err)
return nil
}
font, err := truetype.Parse(fontBytes)
if err != nil {
log.Println(err)
return nil
}
return font
}

View File

@@ -1,55 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"image"
"image/color"
)
type FillRule int
const (
FillRuleEvenOdd FillRule = iota
FillRuleWinding
)
type GraphicContext interface {
Path
// Create a new path
BeginPath()
GetMatrixTransform() MatrixTransform
SetMatrixTransform(tr MatrixTransform)
ComposeMatrixTransform(tr MatrixTransform)
Rotate(angle float64)
Translate(tx, ty float64)
Scale(sx, sy float64)
SetStrokeColor(c color.Color)
SetFillColor(c color.Color)
SetFillRule(f FillRule)
SetLineWidth(lineWidth float64)
SetLineCap(cap Cap)
SetLineJoin(join Join)
SetLineDash(dash []float64, dashOffset float64)
SetFontSize(fontSize float64)
GetFontSize() float64
SetFontData(fontData FontData)
GetFontData() FontData
DrawImage(image image.Image)
Save()
Restore()
Clear()
ClearRect(x1, y1, x2, y2 int)
SetDPI(dpi int)
GetDPI() int
GetStringBounds(s string) (left, top, right, bottom float64)
CreateStringPath(text string, x, y float64) (cursor float64)
FillString(text string) (cursor float64)
FillStringAt(text string, x, y float64) (cursor float64)
StrokeString(text string) (cursor float64)
StrokeStringAt(text string, x, y float64) (cursor float64)
Stroke(paths ...*PathStorage)
Fill(paths ...*PathStorage)
FillStroke(paths ...*PathStorage)
}

View File

@@ -1,359 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"code.google.com/p/freetype-go/freetype/raster"
"code.google.com/p/freetype-go/freetype/truetype"
"errors"
"image"
"image/color"
"image/draw"
"log"
"math"
)
type Painter interface {
raster.Painter
SetColor(color color.Color)
}
var (
defaultFontData = FontData{"luxi", FontFamilySans, FontStyleNormal}
)
type ImageGraphicContext struct {
*StackGraphicContext
img draw.Image
painter Painter
fillRasterizer *raster.Rasterizer
strokeRasterizer *raster.Rasterizer
glyphBuf *truetype.GlyphBuf
DPI int
}
/**
* Create a new Graphic context from an image
*/
func NewGraphicContext(img draw.Image) *ImageGraphicContext {
var painter Painter
switch selectImage := img.(type) {
case *image.RGBA:
painter = raster.NewRGBAPainter(selectImage)
default:
panic("Image type not supported")
}
return NewGraphicContextWithPainter(img, painter)
}
// Create a new Graphic context from an image and a Painter (see Freetype-go)
func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphicContext {
width, height := img.Bounds().Dx(), img.Bounds().Dy()
dpi := 92
gc := &ImageGraphicContext{
NewStackGraphicContext(),
img,
painter,
raster.NewRasterizer(width, height),
raster.NewRasterizer(width, height),
truetype.NewGlyphBuf(),
dpi,
}
return gc
}
func (gc *ImageGraphicContext) GetDPI() int {
return gc.DPI
}
func (gc *ImageGraphicContext) Clear() {
width, height := gc.img.Bounds().Dx(), gc.img.Bounds().Dy()
gc.ClearRect(0, 0, width, height)
}
func (gc *ImageGraphicContext) ClearRect(x1, y1, x2, y2 int) {
imageColor := image.NewUniform(gc.Current.FillColor)
draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP, draw.Over)
}
func (gc *ImageGraphicContext) DrawImage(img image.Image) {
DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter)
}
func (gc *ImageGraphicContext) FillString(text string) (cursor float64) {
return gc.FillStringAt(text, 0, 0)
}
func (gc *ImageGraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
width := gc.CreateStringPath(text, x, y)
gc.Fill()
return width
}
func (gc *ImageGraphicContext) StrokeString(text string) (cursor float64) {
return gc.StrokeStringAt(text, 0, 0)
}
func (gc *ImageGraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
width := gc.CreateStringPath(text, x, y)
gc.Stroke()
return width
}
func (gc *ImageGraphicContext) loadCurrentFont() (*truetype.Font, error) {
font := GetFont(gc.Current.FontData)
if font == nil {
font = GetFont(defaultFontData)
}
if font == nil {
return nil, errors.New("No font set, and no default font available.")
}
gc.SetFont(font)
gc.SetFontSize(gc.Current.FontSize)
return font, nil
}
func fUnitsToFloat64(x int32) float64 {
scaled := x << 2
return float64(scaled/256) + float64(scaled%256)/256.0
}
// p is a truetype.Point measured in FUnits and positive Y going upwards.
// The returned value is the same thing measured in floating point and positive Y
// going downwards.
func pointToF64Point(p truetype.Point) (x, y float64) {
return fUnitsToFloat64(p.X), -fUnitsToFloat64(p.Y)
}
// drawContour draws the given closed contour at the given sub-pixel offset.
func (gc *ImageGraphicContext) drawContour(ps []truetype.Point, dx, dy float64) {
if len(ps) == 0 {
return
}
startX, startY := pointToF64Point(ps[0])
gc.MoveTo(startX+dx, startY+dy)
q0X, q0Y, on0 := startX, startY, true
for _, p := range ps[1:] {
qX, qY := pointToF64Point(p)
on := p.Flags&0x01 != 0
if on {
if on0 {
gc.LineTo(qX+dx, qY+dy)
} else {
gc.QuadCurveTo(q0X+dx, q0Y+dy, qX+dx, qY+dy)
}
} else {
if on0 {
// No-op.
} else {
midX := (q0X + qX) / 2
midY := (q0Y + qY) / 2
gc.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy)
}
}
q0X, q0Y, on0 = qX, qY, on
}
// Close the curve.
if on0 {
gc.LineTo(startX+dx, startY+dy)
} else {
gc.QuadCurveTo(q0X+dx, q0Y+dy, startX+dx, startY+dy)
}
}
func (gc *ImageGraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error {
if err := gc.glyphBuf.Load(gc.Current.font, gc.Current.scale, glyph, truetype.NoHinting); err != nil {
return err
}
e0 := 0
for _, e1 := range gc.glyphBuf.End {
gc.drawContour(gc.glyphBuf.Point[e0:e1], dx, dy)
e0 = e1
}
return nil
}
// CreateStringPath creates a path from the string s at x, y, and returns the string width.
// The text is placed so that the left edge of the em square of the first character of s
// and the baseline intersect at x, y. The majority of the affected pixels will be
// above and to the right of the point, but some may be below or to the left.
// For example, drawing a string that starts with a 'J' in an italic font may
// affect pixels below and left of the point.
func (gc *ImageGraphicContext) CreateStringPath(s string, x, y float64) float64 {
font, err := gc.loadCurrentFont()
if err != nil {
log.Println(err)
return 0.0
}
startx := x
prev, hasPrev := truetype.Index(0), false
for _, rune := range s {
index := font.Index(rune)
if hasPrev {
x += fUnitsToFloat64(font.Kerning(gc.Current.scale, prev, index))
}
err := gc.drawGlyph(index, x, y)
if err != nil {
log.Println(err)
return startx - x
}
x += fUnitsToFloat64(font.HMetric(gc.Current.scale, index).AdvanceWidth)
prev, hasPrev = index, true
}
return x - startx
}
// GetStringBounds returns the approximate pixel bounds of the string s at x, y.
// The the left edge of the em square of the first character of s
// and the baseline intersect at 0, 0 in the returned coordinates.
// Therefore the top and left coordinates may well be negative.
func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
font, err := gc.loadCurrentFont()
if err != nil {
log.Println(err)
return 0, 0, 0, 0
}
top, left, bottom, right = 10e6, 10e6, -10e6, -10e6
cursor := 0.0
prev, hasPrev := truetype.Index(0), false
for _, rune := range s {
index := font.Index(rune)
if hasPrev {
cursor += fUnitsToFloat64(font.Kerning(gc.Current.scale, prev, index))
}
if err := gc.glyphBuf.Load(gc.Current.font, gc.Current.scale, index, truetype.NoHinting); err != nil {
log.Println(err)
return 0, 0, 0, 0
}
e0 := 0
for _, e1 := range gc.glyphBuf.End {
ps := gc.glyphBuf.Point[e0:e1]
for _, p := range ps {
x, y := pointToF64Point(p)
top = math.Min(top, y)
bottom = math.Max(bottom, y)
left = math.Min(left, x+cursor)
right = math.Max(right, x+cursor)
}
}
cursor += fUnitsToFloat64(font.HMetric(gc.Current.scale, index).AdvanceWidth)
prev, hasPrev = index, true
}
return left, top, right, bottom
}
// recalc recalculates scale and bounds values from the font size, screen
// resolution and font metrics, and invalidates the glyph cache.
func (gc *ImageGraphicContext) recalc() {
gc.Current.scale = int32(gc.Current.FontSize * float64(gc.DPI) * (64.0 / 72.0))
}
// SetDPI sets the screen resolution in dots per inch.
func (gc *ImageGraphicContext) SetDPI(dpi int) {
gc.DPI = dpi
gc.recalc()
}
// SetFont sets the font used to draw text.
func (gc *ImageGraphicContext) SetFont(font *truetype.Font) {
gc.Current.font = font
}
// SetFontSize sets the font size in points (as in ``a 12 point font'').
func (gc *ImageGraphicContext) SetFontSize(fontSize float64) {
gc.Current.FontSize = fontSize
gc.recalc()
}
func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) {
gc.painter.SetColor(color)
rasterizer.Rasterize(gc.painter)
rasterizer.Clear()
gc.Current.Path.Clear()
}
/**** second method ****/
func (gc *ImageGraphicContext) Stroke(paths ...*PathStorage) {
paths = append(paths, gc.Current.Path)
gc.strokeRasterizer.UseNonZeroWinding = true
stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer)))
stroker.HalfLineWidth = gc.Current.LineWidth / 2
var pathConverter *PathConverter
if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
dasher := NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
pathConverter = NewPathConverter(dasher)
} else {
pathConverter = NewPathConverter(stroker)
}
pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
pathConverter.Convert(paths...)
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
}
/**** second method ****/
func (gc *ImageGraphicContext) Fill(paths ...*PathStorage) {
paths = append(paths, gc.Current.Path)
gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()
/**** first method ****/
pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer)))
pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
pathConverter.Convert(paths...)
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
}
/* second method */
func (gc *ImageGraphicContext) FillStroke(paths ...*PathStorage) {
gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()
gc.strokeRasterizer.UseNonZeroWinding = true
filler := NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer))
stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer)))
stroker.HalfLineWidth = gc.Current.LineWidth / 2
demux := NewDemuxConverter(filler, stroker)
paths = append(paths, gc.Current.Path)
pathConverter := NewPathConverter(demux)
pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
pathConverter.Convert(paths...)
gc.paint(gc.fillRasterizer, gc.Current.FillColor)
gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
}
func (f FillRule) UseNonZeroWinding() bool {
switch f {
case FillRuleEvenOdd:
return false
case FillRuleWinding:
return true
}
return false
}
func (c Cap) Convert() raster.Capper {
switch c {
case RoundCap:
return raster.RoundCapper
case ButtCap:
return raster.ButtCapper
case SquareCap:
return raster.SquareCapper
}
return raster.RoundCapper
}
func (j Join) Convert() raster.Joiner {
switch j {
case RoundJoin:
return raster.RoundJoiner
case BevelJoin:
return raster.BevelJoiner
}
return raster.RoundJoiner
}

View File

@@ -1,52 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"math"
)
func distance(x1, y1, x2, y2 float64) float64 {
dx := x2 - x1
dy := y2 - y1
return float64(math.Sqrt(dx*dx + dy*dy))
}
func vectorDistance(dx, dy float64) float64 {
return float64(math.Sqrt(dx*dx + dy*dy))
}
func squareDistance(x1, y1, x2, y2 float64) float64 {
dx := x2 - x1
dy := y2 - y1
return dx*dx + dy*dy
}
func min(x, y float64) float64 {
if x < y {
return x
}
return y
}
func max(x, y float64) float64 {
if x > y {
return x
}
return y
}
func minMax(x, y float64) (min, max float64) {
if x > y {
return y, x
}
return x, y
}
func minUint32(a, b uint32) uint32 {
if a < b {
return a
}
return b
}

View File

@@ -1,92 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
/*
import (
"image/draw"
"image"
"freetype-go.googlecode.com/hg/freetype/raster"
)*/
const M = 1<<16 - 1
/*
type NRGBAPainter struct {
// The image to compose onto.
Image *image.NRGBA
// The Porter-Duff composition operator.
Op draw.Op
// The 16-bit color to paint the spans.
cr, cg, cb, ca uint32
}
// Paint satisfies the Painter interface by painting ss onto an image.RGBA.
func (r *NRGBAPainter) Paint(ss []raster.Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
base := s.Y * r.Image.Stride
p := r.Image.Pix[base+s.X0 : base+s.X1]
// This code is duplicated from drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go.
// TODO(nigeltao): Factor out common code into a utility function, once the compiler
// can inline such function calls.
ma := s.A >> 16
if r.Op == draw.Over {
for i, nrgba := range p {
dr, dg, db, da := nrgba.
a := M - (r.ca*ma)/M
da = (da*a + r.ca*ma) / M
if da != 0 {
dr = minUint32(M, (dr*a+r.cr*ma)/da)
dg = minUint32(M, (dg*a+r.cg*ma)/da)
db = minUint32(M, (db*a+r.cb*ma)/da)
} else {
dr, dg, db = 0, 0, 0
}
p[i] = image.NRGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
}
} else {
for i, nrgba := range p {
dr, dg, db, da := nrgba.RGBA()
a := M - ma
da = (da*a + r.ca*ma) / M
if da != 0 {
dr = minUint32(M, (dr*a+r.cr*ma)/da)
dg = minUint32(M, (dg*a+r.cg*ma)/da)
db = minUint32(M, (db*a+r.cb*ma)/da)
} else {
dr, dg, db = 0, 0, 0
}
p[i] = image.NRGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)}
}
}
}
}
// SetColor sets the color to paint the spans.
func (r *NRGBAPainter) SetColor(c image.Color) {
r.cr, r.cg, r.cb, r.ca = c.RGBA()
}
// NewRGBAPainter creates a new RGBAPainter for the given image.
func NewNRGBAPainter(m *image.NRGBA) *NRGBAPainter {
return &NRGBAPainter{Image: m}
}
*/

View File

@@ -1,27 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
type Path interface {
// Return the current point of the path
LastPoint() (x, y float64)
// Create a new subpath that start at the specified point
MoveTo(x, y float64)
// Create a new subpath that start at the specified point
// relative to the current point
RMoveTo(dx, dy float64)
// Add a line to the current subpath
LineTo(x, y float64)
// Add a line to the current subpath
// relative to the current point
RLineTo(dx, dy float64)
QuadCurveTo(cx, cy, x, y float64)
RQuadCurveTo(dcx, dcy, dx, dy float64)
CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64)
RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64)
ArcTo(cx, cy, rx, ry, startAngle, angle float64)
RArcTo(dcx, dcy, rx, ry, startAngle, angle float64)
Close()
}

View File

@@ -1,70 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff
package draw2d
import (
"code.google.com/p/freetype-go/freetype/raster"
)
type VertexAdder struct {
command VertexCommand
adder raster.Adder
}
func NewVertexAdder(adder raster.Adder) *VertexAdder {
return &VertexAdder{VertexNoCommand, adder}
}
func (vertexAdder *VertexAdder) NextCommand(cmd VertexCommand) {
vertexAdder.command = cmd
}
func (vertexAdder *VertexAdder) Vertex(x, y float64) {
switch vertexAdder.command {
case VertexStartCommand:
vertexAdder.adder.Start(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)})
default:
vertexAdder.adder.Add1(raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)})
}
vertexAdder.command = VertexNoCommand
}
type PathAdder struct {
adder raster.Adder
firstPoint raster.Point
ApproximationScale float64
}
func NewPathAdder(adder raster.Adder) *PathAdder {
return &PathAdder{adder, raster.Point{0, 0}, 1}
}
func (pathAdder *PathAdder) Convert(paths ...*PathStorage) {
for _, path := range paths {
j := 0
for _, cmd := range path.commands {
switch cmd {
case MoveTo:
pathAdder.firstPoint = raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}
pathAdder.adder.Start(pathAdder.firstPoint)
j += 2
case LineTo:
pathAdder.adder.Add1(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)})
j += 2
case QuadCurveTo:
pathAdder.adder.Add2(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}, raster.Point{raster.Fix32(path.vertices[j+2] * 256), raster.Fix32(path.vertices[j+3] * 256)})
j += 4
case CubicCurveTo:
pathAdder.adder.Add3(raster.Point{raster.Fix32(path.vertices[j] * 256), raster.Fix32(path.vertices[j+1] * 256)}, raster.Point{raster.Fix32(path.vertices[j+2] * 256), raster.Fix32(path.vertices[j+3] * 256)}, raster.Point{raster.Fix32(path.vertices[j+4] * 256), raster.Fix32(path.vertices[j+5] * 256)})
j += 6
case ArcTo:
lastPoint := arcAdder(pathAdder.adder, path.vertices[j], path.vertices[j+1], path.vertices[j+2], path.vertices[j+3], path.vertices[j+4], path.vertices[j+5], pathAdder.ApproximationScale)
pathAdder.adder.Add1(lastPoint)
j += 6
case Close:
pathAdder.adder.Add1(pathAdder.firstPoint)
}
}
}
}

View File

@@ -1,173 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 06/12/2010 by Laurent Le Goff
package draw2d
import (
"math"
)
type PathConverter struct {
converter VertexConverter
ApproximationScale, AngleTolerance, CuspLimit float64
startX, startY, x, y float64
}
func NewPathConverter(converter VertexConverter) *PathConverter {
return &PathConverter{converter, 1, 0, 0, 0, 0, 0, 0}
}
func (c *PathConverter) Convert(paths ...*PathStorage) {
for _, path := range paths {
j := 0
for _, cmd := range path.commands {
j = j + c.ConvertCommand(cmd, path.vertices[j:]...)
}
c.converter.NextCommand(VertexStopCommand)
}
}
func (c *PathConverter) ConvertCommand(cmd PathCmd, vertices ...float64) int {
switch cmd {
case MoveTo:
c.x, c.y = vertices[0], vertices[1]
c.startX, c.startY = c.x, c.y
c.converter.NextCommand(VertexStopCommand)
c.converter.NextCommand(VertexStartCommand)
c.converter.Vertex(c.x, c.y)
return 2
case LineTo:
c.x, c.y = vertices[0], vertices[1]
if c.startX == c.x && c.startY == c.y {
c.converter.NextCommand(VertexCloseCommand)
}
c.converter.Vertex(c.x, c.y)
c.converter.NextCommand(VertexJoinCommand)
return 2
case QuadCurveTo:
quadraticBezier(c.converter, c.x, c.y, vertices[0], vertices[1], vertices[2], vertices[3], c.ApproximationScale, c.AngleTolerance)
c.x, c.y = vertices[2], vertices[3]
if c.startX == c.x && c.startY == c.y {
c.converter.NextCommand(VertexCloseCommand)
}
c.converter.Vertex(c.x, c.y)
return 4
case CubicCurveTo:
cubicBezier(c.converter, c.x, c.y, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale, c.AngleTolerance, c.CuspLimit)
c.x, c.y = vertices[4], vertices[5]
if c.startX == c.x && c.startY == c.y {
c.converter.NextCommand(VertexCloseCommand)
}
c.converter.Vertex(c.x, c.y)
return 6
case ArcTo:
c.x, c.y = arc(c.converter, vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], c.ApproximationScale)
if c.startX == c.x && c.startY == c.y {
c.converter.NextCommand(VertexCloseCommand)
}
c.converter.Vertex(c.x, c.y)
return 6
case Close:
c.converter.NextCommand(VertexCloseCommand)
c.converter.Vertex(c.startX, c.startY)
return 0
}
return 0
}
func (c *PathConverter) MoveTo(x, y float64) *PathConverter {
c.x, c.y = x, y
c.startX, c.startY = c.x, c.y
c.converter.NextCommand(VertexStopCommand)
c.converter.NextCommand(VertexStartCommand)
c.converter.Vertex(c.x, c.y)
return c
}
func (c *PathConverter) RMoveTo(dx, dy float64) *PathConverter {
c.MoveTo(c.x+dx, c.y+dy)
return c
}
func (c *PathConverter) LineTo(x, y float64) *PathConverter {
c.x, c.y = x, y
if c.startX == c.x && c.startY == c.y {
c.converter.NextCommand(VertexCloseCommand)
}
c.converter.Vertex(c.x, c.y)
c.converter.NextCommand(VertexJoinCommand)
return c
}
func (c *PathConverter) RLineTo(dx, dy float64) *PathConverter {
c.LineTo(c.x+dx, c.y+dy)
return c
}
func (c *PathConverter) QuadCurveTo(cx, cy, x, y float64) *PathConverter {
quadraticBezier(c.converter, c.x, c.y, cx, cy, x, y, c.ApproximationScale, c.AngleTolerance)
c.x, c.y = x, y
if c.startX == c.x && c.startY == c.y {
c.converter.NextCommand(VertexCloseCommand)
}
c.converter.Vertex(c.x, c.y)
return c
}
func (c *PathConverter) RQuadCurveTo(dcx, dcy, dx, dy float64) *PathConverter {
c.QuadCurveTo(c.x+dcx, c.y+dcy, c.x+dx, c.y+dy)
return c
}
func (c *PathConverter) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathConverter {
cubicBezier(c.converter, c.x, c.y, cx1, cy1, cx2, cy2, x, y, c.ApproximationScale, c.AngleTolerance, c.CuspLimit)
c.x, c.y = x, y
if c.startX == c.x && c.startY == c.y {
c.converter.NextCommand(VertexCloseCommand)
}
c.converter.Vertex(c.x, c.y)
return c
}
func (c *PathConverter) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) *PathConverter {
c.CubicCurveTo(c.x+dcx1, c.y+dcy1, c.x+dcx2, c.y+dcy2, c.x+dx, c.y+dy)
return c
}
func (c *PathConverter) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathConverter {
endAngle := startAngle + angle
clockWise := true
if angle < 0 {
clockWise = false
}
// normalize
if clockWise {
for endAngle < startAngle {
endAngle += math.Pi * 2.0
}
} else {
for startAngle < endAngle {
startAngle += math.Pi * 2.0
}
}
startX := cx + math.Cos(startAngle)*rx
startY := cy + math.Sin(startAngle)*ry
c.MoveTo(startX, startY)
c.x, c.y = arc(c.converter, cx, cy, rx, ry, startAngle, angle, c.ApproximationScale)
if c.startX == c.x && c.startY == c.y {
c.converter.NextCommand(VertexCloseCommand)
}
c.converter.Vertex(c.x, c.y)
return c
}
func (c *PathConverter) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *PathConverter {
c.ArcTo(c.x+dcx, c.y+dcy, rx, ry, startAngle, angle)
return c
}
func (c *PathConverter) Close() *PathConverter {
c.converter.NextCommand(VertexCloseCommand)
c.converter.Vertex(c.startX, c.startY)
return c
}

View File

@@ -1,190 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"fmt"
"math"
)
type PathCmd int
const (
MoveTo PathCmd = iota
LineTo
QuadCurveTo
CubicCurveTo
ArcTo
Close
)
type PathStorage struct {
commands []PathCmd
vertices []float64
x, y float64
}
func NewPathStorage() (p *PathStorage) {
p = new(PathStorage)
p.commands = make([]PathCmd, 0, 256)
p.vertices = make([]float64, 0, 256)
return
}
func (p *PathStorage) Clear() {
p.commands = p.commands[0:0]
p.vertices = p.vertices[0:0]
return
}
func (p *PathStorage) appendToPath(cmd PathCmd, vertices ...float64) {
if cap(p.vertices) <= len(p.vertices)+6 {
a := make([]PathCmd, len(p.commands), cap(p.commands)+256)
b := make([]float64, len(p.vertices), cap(p.vertices)+256)
copy(a, p.commands)
p.commands = a
copy(b, p.vertices)
p.vertices = b
}
p.commands = p.commands[0 : len(p.commands)+1]
p.commands[len(p.commands)-1] = cmd
copy(p.vertices[len(p.vertices):len(p.vertices)+len(vertices)], vertices)
p.vertices = p.vertices[0 : len(p.vertices)+len(vertices)]
}
func (src *PathStorage) Copy() (dest *PathStorage) {
dest = new(PathStorage)
dest.commands = make([]PathCmd, len(src.commands))
copy(dest.commands, src.commands)
dest.vertices = make([]float64, len(src.vertices))
copy(dest.vertices, src.vertices)
return dest
}
func (p *PathStorage) LastPoint() (x, y float64) {
return p.x, p.y
}
func (p *PathStorage) IsEmpty() bool {
return len(p.commands) == 0
}
func (p *PathStorage) Close() *PathStorage {
p.appendToPath(Close)
return p
}
func (p *PathStorage) MoveTo(x, y float64) *PathStorage {
p.appendToPath(MoveTo, x, y)
p.x = x
p.y = y
return p
}
func (p *PathStorage) RMoveTo(dx, dy float64) *PathStorage {
x, y := p.LastPoint()
p.MoveTo(x+dx, y+dy)
return p
}
func (p *PathStorage) LineTo(x, y float64) *PathStorage {
p.appendToPath(LineTo, x, y)
p.x = x
p.y = y
return p
}
func (p *PathStorage) RLineTo(dx, dy float64) *PathStorage {
x, y := p.LastPoint()
p.LineTo(x+dx, y+dy)
return p
}
func (p *PathStorage) QuadCurveTo(cx, cy, x, y float64) *PathStorage {
p.appendToPath(QuadCurveTo, cx, cy, x, y)
p.x = x
p.y = y
return p
}
func (p *PathStorage) RQuadCurveTo(dcx, dcy, dx, dy float64) *PathStorage {
x, y := p.LastPoint()
p.QuadCurveTo(x+dcx, y+dcy, x+dx, y+dy)
return p
}
func (p *PathStorage) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) *PathStorage {
p.appendToPath(CubicCurveTo, cx1, cy1, cx2, cy2, x, y)
p.x = x
p.y = y
return p
}
func (p *PathStorage) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) *PathStorage {
x, y := p.LastPoint()
p.CubicCurveTo(x+dcx1, y+dcy1, x+dcx2, y+dcy2, x+dx, y+dy)
return p
}
func (p *PathStorage) ArcTo(cx, cy, rx, ry, startAngle, angle float64) *PathStorage {
endAngle := startAngle + angle
clockWise := true
if angle < 0 {
clockWise = false
}
// normalize
if clockWise {
for endAngle < startAngle {
endAngle += math.Pi * 2.0
}
} else {
for startAngle < endAngle {
startAngle += math.Pi * 2.0
}
}
startX := cx + math.Cos(startAngle)*rx
startY := cy + math.Sin(startAngle)*ry
if len(p.commands) > 0 {
p.LineTo(startX, startY)
} else {
p.MoveTo(startX, startY)
}
p.appendToPath(ArcTo, cx, cy, rx, ry, startAngle, angle)
p.x = cx + math.Cos(endAngle)*rx
p.y = cy + math.Sin(endAngle)*ry
return p
}
func (p *PathStorage) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) *PathStorage {
x, y := p.LastPoint()
p.ArcTo(x+dcx, y+dcy, rx, ry, startAngle, angle)
return p
}
func (p *PathStorage) String() string {
s := ""
j := 0
for _, cmd := range p.commands {
switch cmd {
case MoveTo:
s += fmt.Sprintf("MoveTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
j = j + 2
case LineTo:
s += fmt.Sprintf("LineTo: %f, %f\n", p.vertices[j], p.vertices[j+1])
j = j + 2
case QuadCurveTo:
s += fmt.Sprintf("QuadCurveTo: %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3])
j = j + 4
case CubicCurveTo:
s += fmt.Sprintf("CubicCurveTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
j = j + 6
case ArcTo:
s += fmt.Sprintf("ArcTo: %f, %f, %f, %f, %f, %f\n", p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5])
j = j + 6
case Close:
s += "Close\n"
}
}
return s
}

View File

@@ -1,203 +0,0 @@
// 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.0 / 8,
0.0 / 8,
3.0 / 8,
6.0 / 8,
1.0 / 8,
4.0 / 8,
7.0 / 8,
2.0 / 8,
}
var SUBPIXEL_OFFSETS_SAMPLE_8_FIXED = [8]Fix{
Fix(SUBPIXEL_OFFSETS_SAMPLE_8[0] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_8[1] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_8[2] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_8[3] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_8[4] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_8[5] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_8[6] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_8[7] * FIXED_FLOAT_COEF),
}
var SUBPIXEL_OFFSETS_SAMPLE_16 = [16]float64{
1.0 / 16,
8.0 / 16,
4.0 / 16,
15.0 / 16,
11.0 / 16,
2.0 / 16,
6.0 / 16,
14.0 / 16,
10.0 / 16,
3.0 / 16,
7.0 / 16,
12.0 / 16,
0.0 / 16,
9.0 / 16,
5.0 / 16,
13.0 / 16,
}
var SUBPIXEL_OFFSETS_SAMPLE_16_FIXED = [16]Fix{
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[0] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[1] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[2] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[3] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[4] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[5] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[6] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[7] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[8] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[9] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[10] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[11] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[12] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[13] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[14] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_16[15] * FIXED_FLOAT_COEF),
}
var SUBPIXEL_OFFSETS_SAMPLE_32 = [32]float64{
28.0 / 32,
13.0 / 32,
6.0 / 32,
23.0 / 32,
0.0 / 32,
17.0 / 32,
10.0 / 32,
27.0 / 32,
4.0 / 32,
21.0 / 32,
14.0 / 32,
31.0 / 32,
8.0 / 32,
25.0 / 32,
18.0 / 32,
3.0 / 32,
12.0 / 32,
29.0 / 32,
22.0 / 32,
7.0 / 32,
16.0 / 32,
1.0 / 32,
26.0 / 32,
11.0 / 32,
20.0 / 32,
5.0 / 32,
30.0 / 32,
15.0 / 32,
24.0 / 32,
9.0 / 32,
2.0 / 32,
19.0 / 32,
}
var SUBPIXEL_OFFSETS_SAMPLE_32_FIXED = [32]Fix{
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[0] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[1] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[2] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[3] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[4] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[5] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[6] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[7] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[8] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[9] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[10] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[11] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[12] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[13] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[14] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[15] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[16] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[17] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[18] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[19] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[20] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[21] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[22] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[23] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[24] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[25] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[26] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[27] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[28] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[29] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[30] * FIXED_FLOAT_COEF),
Fix(SUBPIXEL_OFFSETS_SAMPLE_32[31] * FIXED_FLOAT_COEF),
}
var coverageTable = [256]uint8{
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
}

View File

@@ -1,320 +0,0 @@
// Copyright 2011 The draw2d Authors. All rights reserved.
// created: 27/05/2011 by Laurent Le Goff
package raster
import (
"image"
"image/color"
"unsafe"
)
const (
SUBPIXEL_SHIFT = 3
SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT
)
var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_8_FIXED
type SUBPIXEL_DATA uint8
type NON_ZERO_MASK_DATA_UNIT uint8
type Rasterizer8BitsSample struct {
MaskBuffer []SUBPIXEL_DATA
WindingBuffer []NON_ZERO_MASK_DATA_UNIT
Width int
BufferWidth int
Height int
ClipBound [4]float64
RemappingMatrix [6]float64
}
/* width and height define the maximum output size for the filler.
* The filler will output to larger bitmaps as well, but the output will
* be cropped.
*/
func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample {
var r Rasterizer8BitsSample
// 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 *color.RGBA, polygon *Polygon, tr [6]float64) {
// memset 0 the mask buffer
r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
// inline matrix multiplication
transform := [6]float64{
tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
}
clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
clipRect = intersect(clipRect, r.ClipBound)
p := 0
l := len(*polygon) / 2
var edges [32]PolygonEdge
for p < l {
edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
for k := 0; k < edgeCount; k++ {
r.addEvenOddEdge(&edges[k])
}
p += 16
}
r.fillEvenOdd(img, color, clipRect)
}
//! Adds an edge to be used with even-odd fill.
func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) {
x := Fix(edge.X * FIXED_FLOAT_COEF)
slope := Fix(edge.Slope * FIXED_FLOAT_COEF)
slopeFix := Fix(0)
if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP {
slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT
}
var mask SUBPIXEL_DATA
var ySub uint32
var xp, yLine int
for y := edge.FirstLine; y <= edge.LastLine; y++ {
ySub = uint32(y & (SUBPIXEL_COUNT - 1))
xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT)
mask = SUBPIXEL_DATA(1 << ySub)
yLine = y >> SUBPIXEL_SHIFT
r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask
x += slope
if y&SLOPE_FIX_MASK == 0 {
x += slopeFix
}
}
}
//! Adds an edge to be used with non-zero winding fill.
func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) {
x := Fix(edge.X * FIXED_FLOAT_COEF)
slope := Fix(edge.Slope * FIXED_FLOAT_COEF)
slopeFix := Fix(0)
if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP {
slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT
}
var mask SUBPIXEL_DATA
var ySub uint32
var xp, yLine int
winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding)
for y := edge.FirstLine; y <= edge.LastLine; y++ {
ySub = uint32(y & (SUBPIXEL_COUNT - 1))
xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT)
mask = SUBPIXEL_DATA(1 << ySub)
yLine = y >> SUBPIXEL_SHIFT
r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask
r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding
x += slope
if y&SLOPE_FIX_MASK == 0 {
x += slopeFix
}
}
}
// Renders the mask to the canvas with even-odd fill.
func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, 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 := 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 *color.RGBA, polygon *Polygon, tr [6]float64) {
r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT)
// inline matrix multiplication
transform := [6]float64{
tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
}
clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
clipRect = intersect(clipRect, r.ClipBound)
p := 0
l := len(*polygon) / 2
var edges [32]PolygonEdge
for p < l {
edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
for k := 0; k < edgeCount; k++ {
r.addNonZeroEdge(&edges[k])
}
p += 16
}
r.fillNonZero(img, color, clipRect)
}
//! Renders the mask to the canvas with non-zero winding fill.
func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
var x, y uint32
minX := uint32(clipBound[0])
maxX := uint32(clipBound[2])
minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
//pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
pixColor := (*uint32)(unsafe.Pointer(color))
cs1 := *pixColor & 0xff00ff
cs2 := *pixColor >> 8 & 0xff00ff
stride := uint32(img.Stride)
var mask SUBPIXEL_DATA
var n uint32
var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT
for n = 0; n < SUBPIXEL_COUNT; n++ {
values[n] = 0
}
for y = minY; y < maxY; y++ {
tp := img.Pix[y*stride:]
mask = 0
for x = minX; x <= maxX; x++ {
p := (*uint32)(unsafe.Pointer(&tp[x]))
temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x]
if temp != 0 {
var bit SUBPIXEL_DATA = 1
for n = 0; n < SUBPIXEL_COUNT; n++ {
if temp&bit != 0 {
t := values[n]
values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n]
if (t == 0 || values[n] == 0) && t != values[n] {
mask ^= bit
}
}
bit <<= 1
}
}
// 8bits
alpha := uint32(coverageTable[mask])
// 16bits
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
// 32bits
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
// alpha is in range of 0 to SUBPIXEL_COUNT
invAlpha := uint32(SUBPIXEL_COUNT) - alpha
ct1 := *p & 0xff00ff * invAlpha
ct2 := *p >> 8 & 0xff00ff * invAlpha
ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
*p = ct1 + ct2
}
}
}

View File

@@ -1,303 +0,0 @@
// Copyright 2011 The draw2d Authors. All rights reserved.
// created: 27/05/2011 by Laurent Le Goff
package raster
import (
"image"
"image/color"
"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 *color.RGBA, polygon *Polygon, tr [6]float64) {
// memset 0 the mask buffer
r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
// inline matrix multiplication
transform := [6]float64{
tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
}
clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
clipRect = intersect(clipRect, r.ClipBound)
p := 0
l := len(*polygon) / 2
var edges [32]PolygonEdge
for p < l {
edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
for k := 0; k < edgeCount; k++ {
r.addEvenOddEdge(&edges[k])
}
p += 16
}
r.fillEvenOdd(img, color, clipRect)
}
//! Adds an edge to be used with even-odd fill.
func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) {
x := edge.X
slope := edge.Slope
var ySub, mask SUBPIXEL_DATA
var xp, yLine int
for y := edge.FirstLine; y <= edge.LastLine; y++ {
ySub = SUBPIXEL_DATA(y & (SUBPIXEL_COUNT - 1))
xp = int(x + SUBPIXEL_OFFSETS[ySub])
mask = SUBPIXEL_DATA(1 << ySub)
yLine = y >> SUBPIXEL_SHIFT
r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask
x += slope
}
}
// Renders the mask to the canvas with even-odd fill.
func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
var x, y uint32
minX := uint32(clipBound[0])
maxX := uint32(clipBound[2])
minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
//pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
pixColor := (*uint32)(unsafe.Pointer(color))
cs1 := *pixColor & 0xff00ff
cs2 := *pixColor >> 8 & 0xff00ff
stride := uint32(img.Stride)
var mask SUBPIXEL_DATA
for y = minY; y < maxY; y++ {
tp := img.Pix[y*stride:]
mask = 0
for x = minX; x <= maxX; x++ {
p := (*uint32)(unsafe.Pointer(&tp[x]))
mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x]
// 8bits
alpha := uint32(coverageTable[mask])
// 16bits
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
// 32bits
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
// alpha is in range of 0 to SUBPIXEL_COUNT
invAlpha := uint32(SUBPIXEL_COUNT) - alpha
ct1 := *p & 0xff00ff * invAlpha
ct2 := *p >> 8 & 0xff00ff * invAlpha
ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
*p = ct1 + ct2
}
}
}
/*
* Renders the polygon with non-zero winding fill.
* param aTarget the target bitmap.
* param aPolygon the polygon to render.
* param aColor the color to be used for rendering.
* param aTransformation the transformation matrix.
*/
func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *color.RGBA, 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 *color.RGBA, clipBound [4]float64) {
var x, y uint32
minX := uint32(clipBound[0])
maxX := uint32(clipBound[2])
minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
//pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
pixColor := (*uint32)(unsafe.Pointer(color))
cs1 := *pixColor & 0xff00ff
cs2 := *pixColor >> 8 & 0xff00ff
stride := uint32(img.Stride)
var mask SUBPIXEL_DATA
var n uint32
var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT
for n = 0; n < SUBPIXEL_COUNT; n++ {
values[n] = 0
}
for y = minY; y < maxY; y++ {
tp := img.Pix[y*stride:]
mask = 0
for x = minX; x <= maxX; x++ {
p := (*uint32)(unsafe.Pointer(&tp[x]))
temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x]
if temp != 0 {
var bit SUBPIXEL_DATA = 1
for n = 0; n < SUBPIXEL_COUNT; n++ {
if temp&bit != 0 {
t := values[n]
values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n]
if (t == 0 || values[n] == 0) && t != values[n] {
mask ^= bit
}
}
bit <<= 1
}
}
// 8bits
alpha := uint32(coverageTable[mask])
// 16bits
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
// 32bits
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff] + coverageTable[(mask >> 16) & 0xff] + coverageTable[(mask >> 24) & 0xff])
// alpha is in range of 0 to SUBPIXEL_COUNT
invAlpha := uint32(SUBPIXEL_COUNT) - alpha
ct1 := *p & 0xff00ff * invAlpha
ct2 := *p >> 8 & 0xff00ff * invAlpha
ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
*p = ct1 + ct2
}
}
}

View File

@@ -1,320 +0,0 @@
// Copyright 2011 The draw2d Authors. All rights reserved.
// created: 27/05/2011 by Laurent Le Goff
package raster
import (
"image"
"image/color"
"unsafe"
)
const (
SUBPIXEL_SHIFT = 5
SUBPIXEL_COUNT = 1 << SUBPIXEL_SHIFT
)
var SUBPIXEL_OFFSETS = SUBPIXEL_OFFSETS_SAMPLE_32_FIXED
type SUBPIXEL_DATA uint32
type NON_ZERO_MASK_DATA_UNIT uint8
type Rasterizer8BitsSample struct {
MaskBuffer []SUBPIXEL_DATA
WindingBuffer []NON_ZERO_MASK_DATA_UNIT
Width int
BufferWidth int
Height int
ClipBound [4]float64
RemappingMatrix [6]float64
}
/* width and height define the maximum output size for the filler.
* The filler will output to larger bitmaps as well, but the output will
* be cropped.
*/
func NewRasterizer8BitsSample(width, height int) *Rasterizer8BitsSample {
var r Rasterizer8BitsSample
// Scale the coordinates by SUBPIXEL_COUNT in vertical direction
// The sampling point for the sub-pixel is at the top right corner. This
// adjustment moves it to the pixel center.
r.RemappingMatrix = [6]float64{1, 0, 0, SUBPIXEL_COUNT, 0.5 / SUBPIXEL_COUNT, -0.5 * SUBPIXEL_COUNT}
r.Width = width
r.Height = height
// The buffer used for filling needs to be one pixel wider than the bitmap.
// This is because the end flag that turns the fill of is the first pixel
// after the actually drawn edge.
r.BufferWidth = width + 1
r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*height)
r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*height*SUBPIXEL_COUNT)
r.ClipBound = clip(0, 0, width, height, SUBPIXEL_COUNT)
return &r
}
func clip(x, y, width, height, scale int) [4]float64 {
var clipBound [4]float64
offset := 0.99 / float64(scale)
clipBound[0] = float64(x) + offset
clipBound[2] = float64(x+width) - offset
clipBound[1] = float64(y * scale)
clipBound[3] = float64((y + height) * scale)
return clipBound
}
func intersect(r1, r2 [4]float64) [4]float64 {
if r1[0] < r2[0] {
r1[0] = r2[0]
}
if r1[2] > r2[2] {
r1[2] = r2[2]
}
if r1[0] > r1[2] {
r1[0] = r1[2]
}
if r1[1] < r2[1] {
r1[1] = r2[1]
}
if r1[3] > r2[3] {
r1[3] = r2[3]
}
if r1[1] > r1[3] {
r1[1] = r1[3]
}
return r1
}
func (r *Rasterizer8BitsSample) RenderEvenOdd(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) {
// memset 0 the mask buffer
r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
// inline matrix multiplication
transform := [6]float64{
tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
}
clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
clipRect = intersect(clipRect, r.ClipBound)
p := 0
l := len(*polygon) / 2
var edges [32]PolygonEdge
for p < l {
edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
for k := 0; k < edgeCount; k++ {
r.addEvenOddEdge(&edges[k])
}
p += 16
}
r.fillEvenOdd(img, color, clipRect)
}
//! Adds an edge to be used with even-odd fill.
func (r *Rasterizer8BitsSample) addEvenOddEdge(edge *PolygonEdge) {
x := Fix(edge.X * FIXED_FLOAT_COEF)
slope := Fix(edge.Slope * FIXED_FLOAT_COEF)
slopeFix := Fix(0)
if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP {
slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT
}
var mask SUBPIXEL_DATA
var ySub uint32
var xp, yLine int
for y := edge.FirstLine; y <= edge.LastLine; y++ {
ySub = uint32(y & (SUBPIXEL_COUNT - 1))
xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT)
mask = SUBPIXEL_DATA(1 << ySub)
yLine = y >> SUBPIXEL_SHIFT
r.MaskBuffer[yLine*r.BufferWidth+xp] ^= mask
x += slope
if y&SLOPE_FIX_MASK == 0 {
x += slopeFix
}
}
}
//! Adds an edge to be used with non-zero winding fill.
func (r *Rasterizer8BitsSample) addNonZeroEdge(edge *PolygonEdge) {
x := Fix(edge.X * FIXED_FLOAT_COEF)
slope := Fix(edge.Slope * FIXED_FLOAT_COEF)
slopeFix := Fix(0)
if edge.LastLine-edge.FirstLine >= SLOPE_FIX_STEP {
slopeFix = Fix(edge.Slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) - slope<<SLOPE_FIX_SHIFT
}
var mask SUBPIXEL_DATA
var ySub uint32
var xp, yLine int
winding := NON_ZERO_MASK_DATA_UNIT(edge.Winding)
for y := edge.FirstLine; y <= edge.LastLine; y++ {
ySub = uint32(y & (SUBPIXEL_COUNT - 1))
xp = int((x + SUBPIXEL_OFFSETS[ySub]) >> FIXED_SHIFT)
mask = SUBPIXEL_DATA(1 << ySub)
yLine = y >> SUBPIXEL_SHIFT
r.MaskBuffer[yLine*r.BufferWidth+xp] |= mask
r.WindingBuffer[(yLine*r.BufferWidth+xp)*SUBPIXEL_COUNT+int(ySub)] += winding
x += slope
if y&SLOPE_FIX_MASK == 0 {
x += slopeFix
}
}
}
// Renders the mask to the canvas with even-odd fill.
func (r *Rasterizer8BitsSample) fillEvenOdd(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
var x, y uint32
minX := uint32(clipBound[0])
maxX := uint32(clipBound[2])
minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
//pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
pixColor := (*uint32)(unsafe.Pointer(color))
cs1 := *pixColor & 0xff00ff
cs2 := *pixColor >> 8 & 0xff00ff
stride := uint32(img.Stride)
var mask SUBPIXEL_DATA
for y = minY; y < maxY; y++ {
tp := img.Pix[y*stride:]
mask = 0
for x = minX; x <= maxX; x++ {
p := (*uint32)(unsafe.Pointer(&tp[x]))
mask ^= r.MaskBuffer[y*uint32(r.BufferWidth)+x]
// 8bits
//alpha := uint32(coverageTable[mask])
// 16bits
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
// 32bits
alpha := uint32(coverageTable[mask&0xff] + coverageTable[mask>>8&0xff] + coverageTable[mask>>16&0xff] + coverageTable[mask>>24&0xff])
// alpha is in range of 0 to SUBPIXEL_COUNT
invAlpha := uint32(SUBPIXEL_COUNT) - alpha
ct1 := *p & 0xff00ff * invAlpha
ct2 := *p >> 8 & 0xff00ff * invAlpha
ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
*p = ct1 + ct2
}
}
}
/*
* Renders the polygon with non-zero winding fill.
* param aTarget the target bitmap.
* param aPolygon the polygon to render.
* param aColor the color to be used for rendering.
* param aTransformation the transformation matrix.
*/
func (r *Rasterizer8BitsSample) RenderNonZeroWinding(img *image.RGBA, color *color.RGBA, polygon *Polygon, tr [6]float64) {
r.MaskBuffer = make([]SUBPIXEL_DATA, r.BufferWidth*r.Height)
r.WindingBuffer = make([]NON_ZERO_MASK_DATA_UNIT, r.BufferWidth*r.Height*SUBPIXEL_COUNT)
// inline matrix multiplication
transform := [6]float64{
tr[0]*r.RemappingMatrix[0] + tr[1]*r.RemappingMatrix[2],
tr[1]*r.RemappingMatrix[3] + tr[0]*r.RemappingMatrix[1],
tr[2]*r.RemappingMatrix[0] + tr[3]*r.RemappingMatrix[2],
tr[3]*r.RemappingMatrix[3] + tr[2]*r.RemappingMatrix[1],
tr[4]*r.RemappingMatrix[0] + tr[5]*r.RemappingMatrix[2] + r.RemappingMatrix[4],
tr[5]*r.RemappingMatrix[3] + tr[4]*r.RemappingMatrix[1] + r.RemappingMatrix[5],
}
clipRect := clip(img.Bounds().Min.X, img.Bounds().Min.Y, img.Bounds().Dx(), img.Bounds().Dy(), SUBPIXEL_COUNT)
clipRect = intersect(clipRect, r.ClipBound)
p := 0
l := len(*polygon) / 2
var edges [32]PolygonEdge
for p < l {
edgeCount := polygon.getEdges(p, 16, edges[:], transform, clipRect)
for k := 0; k < edgeCount; k++ {
r.addNonZeroEdge(&edges[k])
}
p += 16
}
r.fillNonZero(img, color, clipRect)
}
//! Renders the mask to the canvas with non-zero winding fill.
func (r *Rasterizer8BitsSample) fillNonZero(img *image.RGBA, color *color.RGBA, clipBound [4]float64) {
var x, y uint32
minX := uint32(clipBound[0])
maxX := uint32(clipBound[2])
minY := uint32(clipBound[1]) >> SUBPIXEL_SHIFT
maxY := uint32(clipBound[3]) >> SUBPIXEL_SHIFT
//pixColor := (uint32(color.R) << 24) | (uint32(color.G) << 16) | (uint32(color.B) << 8) | uint32(color.A)
pixColor := (*uint32)(unsafe.Pointer(color))
cs1 := *pixColor & 0xff00ff
cs2 := *pixColor >> 8 & 0xff00ff
stride := uint32(img.Stride)
var mask SUBPIXEL_DATA
var n uint32
var values [SUBPIXEL_COUNT]NON_ZERO_MASK_DATA_UNIT
for n = 0; n < SUBPIXEL_COUNT; n++ {
values[n] = 0
}
for y = minY; y < maxY; y++ {
tp := img.Pix[y*stride:]
mask = 0
for x = minX; x <= maxX; x++ {
p := (*uint32)(unsafe.Pointer(&tp[x]))
temp := r.MaskBuffer[y*uint32(r.BufferWidth)+x]
if temp != 0 {
var bit SUBPIXEL_DATA = 1
for n = 0; n < SUBPIXEL_COUNT; n++ {
if temp&bit != 0 {
t := values[n]
values[n] += r.WindingBuffer[(y*uint32(r.BufferWidth)+x)*SUBPIXEL_COUNT+n]
if (t == 0 || values[n] == 0) && t != values[n] {
mask ^= bit
}
}
bit <<= 1
}
}
// 8bits
//alpha := uint32(coverageTable[mask])
// 16bits
//alpha := uint32(coverageTable[mask & 0xff] + coverageTable[(mask >> 8) & 0xff])
// 32bits
alpha := uint32(coverageTable[mask&0xff] + coverageTable[mask>>8&0xff] + coverageTable[mask>>16&0xff] + coverageTable[mask>>24&0xff])
// alpha is in range of 0 to SUBPIXEL_COUNT
invAlpha := uint32(SUBPIXEL_COUNT) - alpha
ct1 := *p & 0xff00ff * invAlpha
ct2 := *p >> 8 & 0xff00ff * invAlpha
ct1 = (ct1 + cs1*alpha) >> SUBPIXEL_SHIFT & 0xff00ff
ct2 = (ct2 + cs2*alpha) << (8 - SUBPIXEL_SHIFT) & 0xff00ff00
*p = ct1 + ct2
}
}
}

View File

@@ -1,17 +0,0 @@
package raster
type Fix int32
const (
FIXED_SHIFT = 16
FIXED_FLOAT_COEF = 1 << FIXED_SHIFT
)
/*! Fixed point math inevitably introduces rounding error to the DDA. The error is
* fixed every now and then by a separate fix value. The defines below set these.
*/
const (
SLOPE_FIX_SHIFT = 8
SLOPE_FIX_STEP = 1 << SLOPE_FIX_SHIFT
SLOPE_FIX_MASK = SLOPE_FIX_STEP - 1
)

View File

@@ -1,55 +0,0 @@
// Copyright 2011 The draw2d Authors. All rights reserved.
// created: 27/05/2011 by Laurent Le Goff
package raster
import (
"image/color"
"image/draw"
)
func abs(i int) int {
if i < 0 {
return -i
}
return i
}
func PolylineBresenham(img draw.Image, c color.Color, s ...float64) {
for i := 2; i < len(s); i += 2 {
Bresenham(img, c, int(s[i-2]+0.5), int(s[i-1]+0.5), int(s[i]+0.5), int(s[i+1]+0.5))
}
}
func Bresenham(img draw.Image, color color.Color, x0, y0, x1, y1 int) {
dx := abs(x1 - x0)
dy := abs(y1 - y0)
var sx, sy int
if x0 < x1 {
sx = 1
} else {
sx = -1
}
if y0 < y1 {
sy = 1
} else {
sy = -1
}
err := dx - dy
var e2 int
for {
img.Set(x0, y0, color)
if x0 == x1 && y0 == y1 {
return
}
e2 = 2 * err
if e2 > -dy {
err = err - dy
x0 = x0 + sx
}
if e2 < dx {
err = err + dx
y0 = y0 + sy
}
}
}

View File

@@ -1,581 +0,0 @@
// Copyright 2011 The draw2d Authors. All rights reserved.
// created: 27/05/2011 by Laurent Le Goff
package raster
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
}
//! A more optimized representation of a polygon edge.
type PolygonScanEdge struct {
FirstLine, LastLine int
Winding int16
X Fix
Slope Fix
SlopeFix Fix
NextEdge *PolygonScanEdge
}
//! Calculates the edges of the polygon with transformation and clipping to edges array.
/*! \param startIndex the index for the first vertex.
* \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(startY + 1)
lastLine := int(endY+1) - 1
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(start + 1)
lastLine := int(end+1) - 1
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
}
type VertexData struct {
X, Y float64
ClipFlags int
Line int
}
//! Calculates the edges of the polygon with transformation and clipping to edges array.
/*! Note that this may return upto three times the amount of edges that the polygon has vertices,
* in the unlucky case where both left and right side get clipped for all edges.
* \param edges the array for result edges. This should be able to contain 2*aVertexCount edges.
* \param aTransformation the transformation matrix for the polygon.
* \param aClipRectangle the clip rectangle.
* \return the amount of edges in the result.
*/
func (p Polygon) getScanEdges(edges []PolygonScanEdge, tr [6]float64, clipBound [4]float64) int {
var n int
vertexData := make([]VertexData, len(p)/2+1)
for n = 0; n < len(vertexData)-1; n = n + 1 {
k := n * 2
vertexData[n].X = p[k]*tr[0] + p[k+1]*tr[2] + tr[4]
vertexData[n].Y = p[k]*tr[1] + p[k+1]*tr[3] + tr[5]
// Calculate clip flags for all vertices.
vertexData[n].ClipFlags = POLYGON_CLIP_NONE
if vertexData[n].X < clipBound[0] {
vertexData[n].ClipFlags |= POLYGON_CLIP_LEFT
} else if vertexData[n].X >= clipBound[2] {
vertexData[n].ClipFlags |= POLYGON_CLIP_RIGHT
}
if vertexData[n].Y < clipBound[1] {
vertexData[n].ClipFlags |= POLYGON_CLIP_TOP
} else if vertexData[n].Y >= clipBound[3] {
vertexData[n].ClipFlags |= POLYGON_CLIP_BOTTOM
}
// Calculate line of the vertex. If the vertex is clipped by top or bottom, the line
// is determined by the clip rectangle.
if vertexData[n].ClipFlags&POLYGON_CLIP_TOP != 0 {
vertexData[n].Line = int(clipBound[1])
} else if vertexData[n].ClipFlags&POLYGON_CLIP_BOTTOM != 0 {
vertexData[n].Line = int(clipBound[3] - 1)
} else {
vertexData[n].Line = int(vertexData[n].Y+1) - 1
}
}
// Copy the data from 0 to the last entry to make the data to loop.
vertexData[len(vertexData)-1] = vertexData[0]
// Transform the first vertex; store.
// Process mVertexCount - 1 times, next is n+1
// copy the first vertex to
// Process 1 time, next is n
edgeCount := 0
for n = 0; n < len(vertexData)-1; n++ {
clipSum := vertexData[n].ClipFlags | vertexData[n+1].ClipFlags
clipUnion := vertexData[n].ClipFlags & vertexData[n+1].ClipFlags
if clipUnion&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) == 0 &&
vertexData[n].Line != vertexData[n+1].Line {
var startIndex, endIndex int
var winding int16
if vertexData[n].Y < vertexData[n+1].Y {
startIndex = n
endIndex = n + 1
winding = 1
} else {
startIndex = n + 1
endIndex = n
winding = -1
}
firstLine := vertexData[startIndex].Line + 1
lastLine := vertexData[endIndex].Line
if clipUnion&POLYGON_CLIP_RIGHT != 0 {
// Both clip to right, edge is a vertical line on the right side
edges[edgeCount].FirstLine = firstLine
edges[edgeCount].LastLine = lastLine
edges[edgeCount].Winding = winding
edges[edgeCount].X = Fix(clipBound[2] * FIXED_FLOAT_COEF)
edges[edgeCount].Slope = 0
edges[edgeCount].SlopeFix = 0
edgeCount++
} else if clipUnion&POLYGON_CLIP_LEFT != 0 {
// Both clip to left, edge is a vertical line on the left side
edges[edgeCount].FirstLine = firstLine
edges[edgeCount].LastLine = lastLine
edges[edgeCount].Winding = winding
edges[edgeCount].X = Fix(clipBound[0] * FIXED_FLOAT_COEF)
edges[edgeCount].Slope = 0
edges[edgeCount].SlopeFix = 0
edgeCount++
} else if clipSum&(POLYGON_CLIP_RIGHT|POLYGON_CLIP_LEFT) == 0 {
// No clipping in the horizontal direction
slope := (vertexData[endIndex].X -
vertexData[startIndex].X) /
(vertexData[endIndex].Y -
vertexData[startIndex].Y)
// If there is vertical clip (for the top) it will be processed here. The calculation
// should be done for all non-clipping edges as well to determine the accurate position
// where the edge crosses the first scanline.
startx := vertexData[startIndex].X +
(float64(firstLine)-vertexData[startIndex].Y)*slope
edges[edgeCount].FirstLine = firstLine
edges[edgeCount].LastLine = lastLine
edges[edgeCount].Winding = winding
edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
if lastLine-firstLine >= SLOPE_FIX_STEP {
edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
} else {
edges[edgeCount].SlopeFix = 0
}
edgeCount++
} else {
// Clips to left or right or both.
slope := (vertexData[endIndex].X -
vertexData[startIndex].X) /
(vertexData[endIndex].Y -
vertexData[startIndex].Y)
// The edge may clip to both left and right.
// The clip results in one or two new vertices, and one to three segments.
// The rounding for scanlines may produce a result where any of the segments is
// ignored.
// The start is always above the end. Calculate the clip positions to clipVertices.
// It is possible that only one of the vertices exist. This will be detected from the
// clip flags of the vertex later, so they are initialized here.
var clipVertices [2]VertexData
if vertexData[startIndex].X <
vertexData[endIndex].X {
clipVertices[0].X = clipBound[0]
clipVertices[1].X = clipBound[2]
clipVertices[0].ClipFlags = POLYGON_CLIP_LEFT
clipVertices[1].ClipFlags = POLYGON_CLIP_RIGHT
} else {
clipVertices[0].X = clipBound[2]
clipVertices[1].X = clipBound[0]
clipVertices[0].ClipFlags = POLYGON_CLIP_RIGHT
clipVertices[1].ClipFlags = POLYGON_CLIP_LEFT
}
var p int
for p = 0; p < 2; p++ {
// Check if either of the vertices crosses the edge marked for the clip vertex
if clipSum&clipVertices[p].ClipFlags != 0 {
// The the vertex is required, calculate it.
clipVertices[p].Y = vertexData[startIndex].Y +
(clipVertices[p].X-
vertexData[startIndex].X)/slope
// If there is clipping in the vertical direction, the new vertex may be clipped.
if clipSum&(POLYGON_CLIP_TOP|POLYGON_CLIP_BOTTOM) != 0 {
if clipVertices[p].Y < clipBound[1] {
clipVertices[p].ClipFlags = POLYGON_CLIP_TOP
clipVertices[p].Line = int(clipBound[1])
} else if clipVertices[p].Y > clipBound[3] {
clipVertices[p].ClipFlags = POLYGON_CLIP_BOTTOM
clipVertices[p].Line = int(clipBound[3] - 1)
} else {
clipVertices[p].ClipFlags = 0
clipVertices[p].Line = int(clipVertices[p].Y+1) - 1
}
} else {
clipVertices[p].ClipFlags = 0
clipVertices[p].Line = int(clipVertices[p].Y+1) - 1
}
}
}
// Now there are three or four vertices, in the top-to-bottom order of start, clip0, clip1,
// end. What kind of edges are required for connecting these can be determined from the
// clip flags.
// -if clip vertex has horizontal clip flags, it doesn't exist. No edge is generated.
// -if start vertex or end vertex has horizontal clip flag, the edge to/from the clip vertex is vertical
// -if the line of two vertices is the same, the edge is not generated, since the edge doesn't
// cross any scanlines.
// The alternative patterns are:
// start - clip0 - clip1 - end
// start - clip0 - end
// start - clip1 - end
var topClipIndex, bottomClipIndex int
if (clipVertices[0].ClipFlags|clipVertices[1].ClipFlags)&
(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) == 0 {
// Both sides are clipped, the order is start-clip0-clip1-end
topClipIndex = 0
bottomClipIndex = 1
// Add the edge from clip0 to clip1
// Check that the line is different for the vertices.
if clipVertices[0].Line != clipVertices[1].Line {
firstClipLine := clipVertices[0].Line + 1
startx := vertexData[startIndex].X +
(float64(firstClipLine)-vertexData[startIndex].Y)*slope
edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
edges[edgeCount].FirstLine = firstClipLine
edges[edgeCount].LastLine = clipVertices[1].Line
edges[edgeCount].Winding = winding
if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
} else {
edges[edgeCount].SlopeFix = 0
}
edgeCount++
}
} else {
// Clip at either side, check which side. The clip flag is on for the vertex
// that doesn't exist, i.e. has not been clipped to be inside the rect.
if clipVertices[0].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
topClipIndex = 1
bottomClipIndex = 1
} else {
topClipIndex = 0
bottomClipIndex = 0
}
}
// Generate the edges from start - clip top and clip bottom - end
// Clip top and clip bottom may be the same vertex if there is only one
// clipped vertex.
// Check that the line is different for the vertices.
if vertexData[startIndex].Line != clipVertices[topClipIndex].Line {
edges[edgeCount].FirstLine = firstLine
edges[edgeCount].LastLine = clipVertices[topClipIndex].Line
edges[edgeCount].Winding = winding
// If startIndex is clipped, the edge is a vertical one.
if vertexData[startIndex].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
edges[edgeCount].X = Fix(clipVertices[topClipIndex].X * FIXED_FLOAT_COEF)
edges[edgeCount].Slope = 0
edges[edgeCount].SlopeFix = 0
} else {
startx := vertexData[startIndex].X +
(float64(firstLine)-vertexData[startIndex].Y)*slope
edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
} else {
edges[edgeCount].SlopeFix = 0
}
}
edgeCount++
}
// Check that the line is different for the vertices.
if clipVertices[bottomClipIndex].Line != vertexData[endIndex].Line {
firstClipLine := clipVertices[bottomClipIndex].Line + 1
edges[edgeCount].FirstLine = firstClipLine
edges[edgeCount].LastLine = lastLine
edges[edgeCount].Winding = winding
// If endIndex is clipped, the edge is a vertical one.
if vertexData[endIndex].ClipFlags&(POLYGON_CLIP_LEFT|POLYGON_CLIP_RIGHT) != 0 {
edges[edgeCount].X = Fix(clipVertices[bottomClipIndex].X * FIXED_FLOAT_COEF)
edges[edgeCount].Slope = 0
edges[edgeCount].SlopeFix = 0
} else {
startx := vertexData[startIndex].X +
(float64(firstClipLine)-vertexData[startIndex].Y)*slope
edges[edgeCount].X = Fix(startx * FIXED_FLOAT_COEF)
edges[edgeCount].Slope = Fix(slope * FIXED_FLOAT_COEF)
if edges[edgeCount].LastLine-edges[edgeCount].FirstLine >= SLOPE_FIX_STEP {
edges[edgeCount].SlopeFix = Fix(slope*SLOPE_FIX_STEP*FIXED_FLOAT_COEF) -
edges[edgeCount].Slope<<SLOPE_FIX_SHIFT
} else {
edges[edgeCount].SlopeFix = 0
}
}
edgeCount++
}
}
}
}
return edgeCount
}

View File

@@ -1,200 +0,0 @@
package raster
import (
"bufio"
"code.google.com/p/draw2d/draw2d/curve"
"code.google.com/p/freetype-go/freetype/raster"
"image"
"image/color"
"image/png"
"log"
"os"
"testing"
)
var flattening_threshold float64 = 0.5
func savepng(filePath string, m image.Image) {
f, err := os.Create(filePath)
if err != nil {
log.Println(err)
os.Exit(1)
}
defer f.Close()
b := bufio.NewWriter(f)
err = png.Encode(b, m)
if err != nil {
log.Println(err)
os.Exit(1)
}
err = b.Flush()
if err != nil {
log.Println(err)
os.Exit(1)
}
}
type Path struct {
points []float64
}
func (p *Path) LineTo(x, y float64) {
if len(p.points)+2 > cap(p.points) {
points := make([]float64, len(p.points)+2, len(p.points)+32)
copy(points, p.points)
p.points = points
} else {
p.points = p.points[0 : len(p.points)+2]
}
p.points[len(p.points)-2] = x
p.points[len(p.points)-1] = y
}
func TestFreetype(t *testing.T) {
var p Path
p.LineTo(10, 190)
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
c.Segment(&p, flattening_threshold)
poly := Polygon(p.points)
color := color.RGBA{0, 0, 0, 0xff}
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
rasterizer := raster.NewRasterizer(200, 200)
rasterizer.UseNonZeroWinding = false
rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
for j := 0; j < len(poly); j = j + 2 {
rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
}
painter := raster.NewRGBAPainter(img)
painter.SetColor(color)
rasterizer.Rasterize(painter)
savepng("_testFreetype.png", img)
}
func TestFreetypeNonZeroWinding(t *testing.T) {
var p Path
p.LineTo(10, 190)
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
c.Segment(&p, flattening_threshold)
poly := Polygon(p.points)
color := color.RGBA{0, 0, 0, 0xff}
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
rasterizer := raster.NewRasterizer(200, 200)
rasterizer.UseNonZeroWinding = true
rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
for j := 0; j < len(poly); j = j + 2 {
rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
}
painter := raster.NewRGBAPainter(img)
painter.SetColor(color)
rasterizer.Rasterize(painter)
savepng("_testFreetypeNonZeroWinding.png", img)
}
func TestRasterizer(t *testing.T) {
img := image.NewRGBA(image.Rect(0, 0, 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 := color.RGBA{0, 0, 0, 0xff}
tr := [6]float64{1, 0, 0, 1, 0, 0}
r := NewRasterizer8BitsSample(200, 200)
//PolylineBresenham(img, image.Black, poly...)
r.RenderEvenOdd(img, &color, &poly, tr)
savepng("_testRasterizer.png", img)
}
func TestRasterizerNonZeroWinding(t *testing.T) {
img := image.NewRGBA(image.Rect(0, 0, 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 := color.RGBA{0, 0, 0, 0xff}
tr := [6]float64{1, 0, 0, 1, 0, 0}
r := NewRasterizer8BitsSample(200, 200)
//PolylineBresenham(img, image.Black, poly...)
r.RenderNonZeroWinding(img, &color, &poly, tr)
savepng("_testRasterizerNonZeroWinding.png", img)
}
func BenchmarkFreetype(b *testing.B) {
var p Path
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 := color.RGBA{0, 0, 0, 0xff}
for i := 0; i < b.N; i++ {
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
rasterizer := raster.NewRasterizer(200, 200)
rasterizer.UseNonZeroWinding = false
rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
for j := 0; j < len(poly); j = j + 2 {
rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
}
painter := raster.NewRGBAPainter(img)
painter.SetColor(color)
rasterizer.Rasterize(painter)
}
}
func BenchmarkFreetypeNonZeroWinding(b *testing.B) {
var p Path
p.LineTo(10, 190)
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
c.Segment(&p, flattening_threshold)
poly := Polygon(p.points)
color := color.RGBA{0, 0, 0, 0xff}
for i := 0; i < b.N; i++ {
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
rasterizer := raster.NewRasterizer(200, 200)
rasterizer.UseNonZeroWinding = true
rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
for j := 0; j < len(poly); j = j + 2 {
rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
}
painter := raster.NewRGBAPainter(img)
painter.SetColor(color)
rasterizer.Rasterize(painter)
}
}
func BenchmarkRasterizerNonZeroWinding(b *testing.B) {
var p Path
p.LineTo(10, 190)
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
c.Segment(&p, flattening_threshold)
poly := Polygon(p.points)
color := color.RGBA{0, 0, 0, 0xff}
tr := [6]float64{1, 0, 0, 1, 0, 0}
for i := 0; i < b.N; i++ {
img := image.NewRGBA(image.Rect(0, 0, 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 := color.RGBA{0, 0, 0, 0xff}
tr := [6]float64{1, 0, 0, 1, 0, 0}
for i := 0; i < b.N; i++ {
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
rasterizer := NewRasterizer8BitsSample(200, 200)
rasterizer.RenderEvenOdd(img, &color, &poly, tr)
}
}

View File

@@ -1,150 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
// see http://pippin.gimp.org/image_processing/chap_resampling.html
package draw2d
import (
"image"
"image/color"
"image/draw"
"math"
)
type ImageFilter int
const (
LinearFilter ImageFilter = iota
BilinearFilter
BicubicFilter
)
//see http://pippin.gimp.org/image_processing/chap_resampling.html
func getColorLinear(img image.Image, x, y float64) color.Color {
return img.At(int(x), int(y))
}
func getColorBilinear(img image.Image, x, y float64) color.Color {
x0 := math.Floor(x)
y0 := math.Floor(y)
dx := x - x0
dy := y - y0
rt, gt, bt, at := img.At(int(x0), int(y0)).RGBA()
r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = img.At(int(x0+1), int(y0)).RGBA()
r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = img.At(int(x0+1), int(y0+1)).RGBA()
r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = img.At(int(x0), int(y0+1)).RGBA()
r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
r := int(lerp(lerp(r0, r1, dx), lerp(r3, r2, dx), dy))
g := int(lerp(lerp(g0, g1, dx), lerp(g3, g2, dx), dy))
b := int(lerp(lerp(b0, b1, dx), lerp(b3, b2, dx), dy))
a := int(lerp(lerp(a0, a1, dx), lerp(a3, a2, dx), dy))
return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
}
/**
-- LERP
-- /lerp/, vi.,n.
--
-- Quasi-acronym for Linear Interpolation, used as a verb or noun for
-- the operation. "Bresenham's algorithm lerps incrementally between the
-- two endpoints of the line." (From Jargon File (4.4.4, 14 Aug 2003)
*/
func lerp(v1, v2, ratio float64) float64 {
return v1*(1-ratio) + v2*ratio
}
func getColorCubicRow(img image.Image, x, y, offset float64) color.Color {
c0 := img.At(int(x), int(y))
c1 := img.At(int(x+1), int(y))
c2 := img.At(int(x+2), int(y))
c3 := img.At(int(x+3), int(y))
rt, gt, bt, at := c0.RGBA()
r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c1.RGBA()
r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c2.RGBA()
r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c3.RGBA()
r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
r, g, b, a := cubic(offset, r0, r1, r2, r3), cubic(offset, g0, g1, g2, g3), cubic(offset, b0, b1, b2, b3), cubic(offset, a0, a1, a2, a3)
return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
}
func getColorBicubic(img image.Image, x, y float64) color.Color {
x0 := math.Floor(x)
y0 := math.Floor(y)
dx := x - x0
dy := y - y0
c0 := getColorCubicRow(img, x0-1, y0-1, dx)
c1 := getColorCubicRow(img, x0-1, y0, dx)
c2 := getColorCubicRow(img, x0-1, y0+1, dx)
c3 := getColorCubicRow(img, x0-1, y0+2, dx)
rt, gt, bt, at := c0.RGBA()
r0, g0, b0, a0 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c1.RGBA()
r1, g1, b1, a1 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c2.RGBA()
r2, g2, b2, a2 := float64(rt), float64(gt), float64(bt), float64(at)
rt, gt, bt, at = c3.RGBA()
r3, g3, b3, a3 := float64(rt), float64(gt), float64(bt), float64(at)
r, g, b, a := cubic(dy, r0, r1, r2, r3), cubic(dy, g0, g1, g2, g3), cubic(dy, b0, b1, b2, b3), cubic(dy, a0, a1, a2, a3)
return color.RGBA{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
}
func cubic(offset, v0, v1, v2, v3 float64) uint32 {
// offset is the offset of the sampled value between v1 and v2
return uint32(((((-7*v0+21*v1-21*v2+7*v3)*offset+
(15*v0-36*v1+27*v2-6*v3))*offset+
(-9*v0+9*v2))*offset + (v0 + 16*v1 + v2)) / 18.0)
}
func DrawImage(src image.Image, dest draw.Image, tr MatrixTransform, op draw.Op, filter ImageFilter) {
bounds := src.Bounds()
x0, y0, x1, y1 := float64(bounds.Min.X), float64(bounds.Min.Y), float64(bounds.Max.X), float64(bounds.Max.Y)
tr.TransformRectangle(&x0, &y0, &x1, &y1)
var x, y, u, v float64
var c1, c2, cr color.Color
var r, g, b, a, ia, r1, g1, b1, a1, r2, g2, b2, a2 uint32
var color color.RGBA
for x = x0; x < x1; x++ {
for y = y0; y < y1; y++ {
u = x
v = y
tr.InverseTransform(&u, &v)
if bounds.Min.X <= int(u) && bounds.Max.X > int(u) && bounds.Min.Y <= int(v) && bounds.Max.Y > int(v) {
c1 = dest.At(int(x), int(y))
switch filter {
case LinearFilter:
c2 = src.At(int(u), int(v))
case BilinearFilter:
c2 = getColorBilinear(src, u, v)
case BicubicFilter:
c2 = getColorBicubic(src, u, v)
}
switch op {
case draw.Over:
r1, g1, b1, a1 = c1.RGBA()
r2, g2, b2, a2 = c2.RGBA()
ia = M - a2
r = ((r1 * ia) / M) + r2
g = ((g1 * ia) / M) + g2
b = ((b1 * ia) / M) + b2
a = ((a1 * ia) / M) + a2
color.R = uint8(r >> 8)
color.G = uint8(g >> 8)
color.B = uint8(b >> 8)
color.A = uint8(a >> 8)
cr = color
default:
cr = c2
}
dest.Set(int(x), int(y), cr)
}
}
}
}

View File

@@ -1,208 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"code.google.com/p/freetype-go/freetype/truetype"
"image"
"image/color"
)
type StackGraphicContext struct {
Current *ContextStack
}
type ContextStack struct {
Tr MatrixTransform
Path *PathStorage
LineWidth float64
Dash []float64
DashOffset float64
StrokeColor color.Color
FillColor color.Color
FillRule FillRule
Cap Cap
Join Join
FontSize float64
FontData FontData
font *truetype.Font
// fontSize and dpi are used to calculate scale. scale is the number of
// 26.6 fixed point units in 1 em.
scale int32
previous *ContextStack
}
/**
* Create a new Graphic context from an image
*/
func NewStackGraphicContext() *StackGraphicContext {
gc := &StackGraphicContext{}
gc.Current = new(ContextStack)
gc.Current.Tr = NewIdentityMatrix()
gc.Current.Path = NewPathStorage()
gc.Current.LineWidth = 1.0
gc.Current.StrokeColor = image.Black
gc.Current.FillColor = image.White
gc.Current.Cap = RoundCap
gc.Current.FillRule = FillRuleEvenOdd
gc.Current.Join = RoundJoin
gc.Current.FontSize = 10
gc.Current.FontData = defaultFontData
return gc
}
func (gc *StackGraphicContext) GetMatrixTransform() MatrixTransform {
return gc.Current.Tr
}
func (gc *StackGraphicContext) SetMatrixTransform(Tr MatrixTransform) {
gc.Current.Tr = Tr
}
func (gc *StackGraphicContext) ComposeMatrixTransform(Tr MatrixTransform) {
gc.Current.Tr = Tr.Multiply(gc.Current.Tr)
}
func (gc *StackGraphicContext) Rotate(angle float64) {
gc.Current.Tr = NewRotationMatrix(angle).Multiply(gc.Current.Tr)
}
func (gc *StackGraphicContext) Translate(tx, ty float64) {
gc.Current.Tr = NewTranslationMatrix(tx, ty).Multiply(gc.Current.Tr)
}
func (gc *StackGraphicContext) Scale(sx, sy float64) {
gc.Current.Tr = NewScaleMatrix(sx, sy).Multiply(gc.Current.Tr)
}
func (gc *StackGraphicContext) SetStrokeColor(c color.Color) {
gc.Current.StrokeColor = c
}
func (gc *StackGraphicContext) SetFillColor(c color.Color) {
gc.Current.FillColor = c
}
func (gc *StackGraphicContext) SetFillRule(f FillRule) {
gc.Current.FillRule = f
}
func (gc *StackGraphicContext) SetLineWidth(LineWidth float64) {
gc.Current.LineWidth = LineWidth
}
func (gc *StackGraphicContext) SetLineCap(Cap Cap) {
gc.Current.Cap = Cap
}
func (gc *StackGraphicContext) SetLineJoin(Join Join) {
gc.Current.Join = Join
}
func (gc *StackGraphicContext) SetLineDash(Dash []float64, DashOffset float64) {
gc.Current.Dash = Dash
gc.Current.DashOffset = DashOffset
}
func (gc *StackGraphicContext) SetFontSize(FontSize float64) {
gc.Current.FontSize = FontSize
}
func (gc *StackGraphicContext) GetFontSize() float64 {
return gc.Current.FontSize
}
func (gc *StackGraphicContext) SetFontData(FontData FontData) {
gc.Current.FontData = FontData
}
func (gc *StackGraphicContext) GetFontData() FontData {
return gc.Current.FontData
}
func (gc *StackGraphicContext) BeginPath() {
gc.Current.Path.Clear()
}
func (gc *StackGraphicContext) IsEmpty() bool {
return gc.Current.Path.IsEmpty()
}
func (gc *StackGraphicContext) LastPoint() (float64, float64) {
return gc.Current.Path.LastPoint()
}
func (gc *StackGraphicContext) MoveTo(x, y float64) {
gc.Current.Path.MoveTo(x, y)
}
func (gc *StackGraphicContext) RMoveTo(dx, dy float64) {
gc.Current.Path.RMoveTo(dx, dy)
}
func (gc *StackGraphicContext) LineTo(x, y float64) {
gc.Current.Path.LineTo(x, y)
}
func (gc *StackGraphicContext) RLineTo(dx, dy float64) {
gc.Current.Path.RLineTo(dx, dy)
}
func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) {
gc.Current.Path.QuadCurveTo(cx, cy, x, y)
}
func (gc *StackGraphicContext) RQuadCurveTo(dcx, dcy, dx, dy float64) {
gc.Current.Path.RQuadCurveTo(dcx, dcy, dx, dy)
}
func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) {
gc.Current.Path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y)
}
func (gc *StackGraphicContext) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float64) {
gc.Current.Path.RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy)
}
func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float64) {
gc.Current.Path.ArcTo(cx, cy, rx, ry, startAngle, angle)
}
func (gc *StackGraphicContext) RArcTo(dcx, dcy, rx, ry, startAngle, angle float64) {
gc.Current.Path.RArcTo(dcx, dcy, rx, ry, startAngle, angle)
}
func (gc *StackGraphicContext) Close() {
gc.Current.Path.Close()
}
func (gc *StackGraphicContext) Save() {
context := new(ContextStack)
context.FontSize = gc.Current.FontSize
context.FontData = gc.Current.FontData
context.LineWidth = gc.Current.LineWidth
context.StrokeColor = gc.Current.StrokeColor
context.FillColor = gc.Current.FillColor
context.FillRule = gc.Current.FillRule
context.Dash = gc.Current.Dash
context.DashOffset = gc.Current.DashOffset
context.Cap = gc.Current.Cap
context.Join = gc.Current.Join
context.Path = gc.Current.Path.Copy()
context.font = gc.Current.font
context.scale = gc.Current.scale
copy(context.Tr[:], gc.Current.Tr[:])
context.previous = gc.Current
gc.Current = context
}
func (gc *StackGraphicContext) Restore() {
if gc.Current.previous != nil {
oldContext := gc.Current
gc.Current = gc.Current.previous
oldContext.previous = nil
}
}

View File

@@ -1,135 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 13/12/2010 by Laurent Le Goff
package draw2d
type Cap int
const (
RoundCap Cap = iota
ButtCap
SquareCap
)
type Join int
const (
BevelJoin Join = iota
RoundJoin
MiterJoin
)
type LineStroker struct {
Next VertexConverter
HalfLineWidth float64
Cap Cap
Join Join
vertices []float64
rewind []float64
x, y, nx, ny float64
command VertexCommand
}
func NewLineStroker(c Cap, j Join, converter VertexConverter) *LineStroker {
l := new(LineStroker)
l.Next = converter
l.HalfLineWidth = 0.5
l.vertices = make([]float64, 0, 256)
l.rewind = make([]float64, 0, 256)
l.Cap = c
l.Join = j
l.command = VertexNoCommand
return l
}
func (l *LineStroker) NextCommand(command VertexCommand) {
l.command = command
if command == VertexStopCommand {
l.Next.NextCommand(VertexStartCommand)
for i, j := 0, 1; j < len(l.vertices); i, j = i+2, j+2 {
l.Next.Vertex(l.vertices[i], l.vertices[j])
l.Next.NextCommand(VertexNoCommand)
}
for i, j := len(l.rewind)-2, len(l.rewind)-1; j > 0; i, j = i-2, j-2 {
l.Next.NextCommand(VertexNoCommand)
l.Next.Vertex(l.rewind[i], l.rewind[j])
}
if len(l.vertices) > 1 {
l.Next.NextCommand(VertexNoCommand)
l.Next.Vertex(l.vertices[0], l.vertices[1])
}
l.Next.NextCommand(VertexStopCommand)
// reinit vertices
l.vertices = l.vertices[0:0]
l.rewind = l.rewind[0:0]
l.x, l.y, l.nx, l.ny = 0, 0, 0, 0
}
}
func (l *LineStroker) Vertex(x, y float64) {
switch l.command {
case VertexNoCommand:
l.line(l.x, l.y, x, y)
case VertexJoinCommand:
l.joinLine(l.x, l.y, l.nx, l.ny, x, y)
case VertexStartCommand:
l.x, l.y = x, y
case VertexCloseCommand:
l.line(l.x, l.y, x, y)
l.joinLine(l.x, l.y, l.nx, l.ny, x, y)
l.closePolygon()
}
l.command = VertexNoCommand
}
func (l *LineStroker) appendVertex(vertices ...float64) {
s := len(vertices) / 2
if len(l.vertices)+s >= cap(l.vertices) {
v := make([]float64, len(l.vertices), cap(l.vertices)+128)
copy(v, l.vertices)
l.vertices = v
v = make([]float64, len(l.rewind), cap(l.rewind)+128)
copy(v, l.rewind)
l.rewind = v
}
copy(l.vertices[len(l.vertices):len(l.vertices)+s], vertices[:s])
l.vertices = l.vertices[0 : len(l.vertices)+s]
copy(l.rewind[len(l.rewind):len(l.rewind)+s], vertices[s:])
l.rewind = l.rewind[0 : len(l.rewind)+s]
}
func (l *LineStroker) closePolygon() {
if len(l.vertices) > 1 {
l.appendVertex(l.vertices[0], l.vertices[1], l.rewind[0], l.rewind[1])
}
}
func (l *LineStroker) line(x1, y1, x2, y2 float64) {
dx := (x2 - x1)
dy := (y2 - y1)
d := vectorDistance(dx, dy)
if d != 0 {
nx := dy * l.HalfLineWidth / d
ny := -(dx * l.HalfLineWidth / d)
l.appendVertex(x1+nx, y1+ny, x2+nx, y2+ny, x1-nx, y1-ny, x2-nx, y2-ny)
l.x, l.y, l.nx, l.ny = x2, y2, nx, ny
}
}
func (l *LineStroker) joinLine(x1, y1, nx1, ny1, x2, y2 float64) {
dx := (x2 - x1)
dy := (y2 - y1)
d := vectorDistance(dx, dy)
if d != 0 {
nx := dy * l.HalfLineWidth / d
ny := -(dx * l.HalfLineWidth / d)
/* l.join(x1, y1, x1 + nx, y1 - ny, nx, ny, x1 + ny2, y1 + nx2, nx2, ny2)
l.join(x1, y1, x1 - ny1, y1 - nx1, nx1, ny1, x1 - ny2, y1 - nx2, nx2, ny2)*/
l.appendVertex(x1+nx, y1+ny, x2+nx, y2+ny, x1-nx, y1-ny, x2-nx, y2-ny)
l.x, l.y, l.nx, l.ny = x2, y2, nx, ny
}
}

View File

@@ -1,306 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
import (
"code.google.com/p/freetype-go/freetype/raster"
"math"
)
type MatrixTransform [6]float64
const (
epsilon = 1e-6
)
func (tr MatrixTransform) Determinant() float64 {
return tr[0]*tr[3] - tr[1]*tr[2]
}
func (tr MatrixTransform) Transform(points ...*float64) {
for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
x := *points[i]
y := *points[j]
*points[i] = x*tr[0] + y*tr[2] + tr[4]
*points[j] = x*tr[1] + y*tr[3] + tr[5]
}
}
func (tr MatrixTransform) TransformArray(points []float64) {
for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
x := points[i]
y := points[j]
points[i] = x*tr[0] + y*tr[2] + tr[4]
points[j] = x*tr[1] + y*tr[3] + tr[5]
}
}
func (tr MatrixTransform) TransformRectangle(x0, y0, x2, y2 *float64) {
x1 := *x2
y1 := *y0
x3 := *x0
y3 := *y2
tr.Transform(x0, y0, &x1, &y1, x2, y2, &x3, &y3)
*x0, x1 = minMax(*x0, x1)
*x2, x3 = minMax(*x2, x3)
*y0, y1 = minMax(*y0, y1)
*y2, y3 = minMax(*y2, y3)
*x0 = min(*x0, *x2)
*y0 = min(*y0, *y2)
*x2 = max(x1, x3)
*y2 = max(y1, y3)
}
func (tr MatrixTransform) TransformRasterPoint(points ...*raster.Point) {
for _, point := range points {
x := float64(point.X) / 256
y := float64(point.Y) / 256
point.X = raster.Fix32((x*tr[0] + y*tr[2] + tr[4]) * 256)
point.Y = raster.Fix32((x*tr[1] + y*tr[3] + tr[5]) * 256)
}
}
func (tr MatrixTransform) InverseTransform(points ...*float64) {
d := tr.Determinant() // matrix determinant
for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
x := *points[i]
y := *points[j]
*points[i] = ((x-tr[4])*tr[3] - (y-tr[5])*tr[2]) / d
*points[j] = ((y-tr[5])*tr[0] - (x-tr[4])*tr[1]) / d
}
}
// ******************** Vector transformations ********************
func (tr MatrixTransform) VectorTransform(points ...*float64) {
for i, j := 0, 1; j < len(points); i, j = i+2, j+2 {
x := *points[i]
y := *points[j]
*points[i] = x*tr[0] + y*tr[2]
*points[j] = x*tr[1] + y*tr[3]
}
}
// ******************** Transformations creation ********************
/** Creates an identity transformation. */
func NewIdentityMatrix() MatrixTransform {
return [6]float64{1, 0, 0, 1, 0, 0}
}
/**
* Creates a transformation with a translation, that,
* transform point1 into point2.
*/
func NewTranslationMatrix(tx, ty float64) MatrixTransform {
return [6]float64{1, 0, 0, 1, tx, ty}
}
/**
* Creates a transformation with a sx, sy scale factor
*/
func NewScaleMatrix(sx, sy float64) MatrixTransform {
return [6]float64{sx, 0, 0, sy, 0, 0}
}
/**
* Creates a rotation transformation.
*/
func NewRotationMatrix(angle float64) MatrixTransform {
c := math.Cos(angle)
s := math.Sin(angle)
return [6]float64{c, s, -s, c, 0, 0}
}
/**
* Creates a transformation, combining a scale and a translation, that transform rectangle1 into rectangle2.
*/
func NewMatrixTransform(rectangle1, rectangle2 [4]float64) MatrixTransform {
xScale := (rectangle2[2] - rectangle2[0]) / (rectangle1[2] - rectangle1[0])
yScale := (rectangle2[3] - rectangle2[1]) / (rectangle1[3] - rectangle1[1])
xOffset := rectangle2[0] - (rectangle1[0] * xScale)
yOffset := rectangle2[1] - (rectangle1[1] * yScale)
return [6]float64{xScale, 0, 0, yScale, xOffset, yOffset}
}
// ******************** Transformations operations ********************
/**
* Returns a transformation that is the inverse of the given transformation.
*/
func (tr MatrixTransform) GetInverseTransformation() MatrixTransform {
d := tr.Determinant() // matrix determinant
return [6]float64{
tr[3] / d,
-tr[1] / d,
-tr[2] / d,
tr[0] / d,
(tr[2]*tr[5] - tr[3]*tr[4]) / d,
(tr[1]*tr[4] - tr[0]*tr[5]) / d}
}
func (tr1 MatrixTransform) Multiply(tr2 MatrixTransform) MatrixTransform {
return [6]float64{
tr1[0]*tr2[0] + tr1[1]*tr2[2],
tr1[1]*tr2[3] + tr1[0]*tr2[1],
tr1[2]*tr2[0] + tr1[3]*tr2[2],
tr1[3]*tr2[3] + tr1[2]*tr2[1],
tr1[4]*tr2[0] + tr1[5]*tr2[2] + tr2[4],
tr1[5]*tr2[3] + tr1[4]*tr2[1] + tr2[5]}
}
func (tr *MatrixTransform) Scale(sx, sy float64) *MatrixTransform {
tr[0] = sx * tr[0]
tr[1] = sx * tr[1]
tr[2] = sy * tr[2]
tr[3] = sy * tr[3]
return tr
}
func (tr *MatrixTransform) Translate(tx, ty float64) *MatrixTransform {
tr[4] = tx*tr[0] + ty*tr[2] + tr[4]
tr[5] = ty*tr[3] + tx*tr[1] + tr[5]
return tr
}
func (tr *MatrixTransform) Rotate(angle float64) *MatrixTransform {
c := math.Cos(angle)
s := math.Sin(angle)
t0 := c*tr[0] + s*tr[2]
t1 := s*tr[3] + c*tr[1]
t2 := c*tr[2] - s*tr[0]
t3 := c*tr[3] - s*tr[1]
tr[0] = t0
tr[1] = t1
tr[2] = t2
tr[3] = t3
return tr
}
func (tr MatrixTransform) GetTranslation() (x, y float64) {
return tr[4], tr[5]
}
func (tr MatrixTransform) GetScaling() (x, y float64) {
return tr[0], tr[3]
}
func (tr MatrixTransform) GetScale() float64 {
x := 0.707106781*tr[0] + 0.707106781*tr[1]
y := 0.707106781*tr[2] + 0.707106781*tr[3]
return math.Sqrt(x*x + y*y)
}
func (tr MatrixTransform) GetMaxAbsScaling() (s float64) {
sx := math.Abs(tr[0])
sy := math.Abs(tr[3])
if sx > sy {
return sx
}
return sy
}
func (tr MatrixTransform) GetMinAbsScaling() (s float64) {
sx := math.Abs(tr[0])
sy := math.Abs(tr[3])
if sx > sy {
return sy
}
return sx
}
// ******************** Testing ********************
/**
* Tests if a two transformation are equal. A tolerance is applied when
* comparing matrix elements.
*/
func (tr1 MatrixTransform) Equals(tr2 MatrixTransform) bool {
for i := 0; i < 6; i = i + 1 {
if !fequals(tr1[i], tr2[i]) {
return false
}
}
return true
}
/**
* Tests if a transformation is the identity transformation. A tolerance
* is applied when comparing matrix elements.
*/
func (tr MatrixTransform) IsIdentity() bool {
return fequals(tr[4], 0) && fequals(tr[5], 0) && tr.IsTranslation()
}
/**
* Tests if a transformation is is a pure translation. A tolerance
* is applied when comparing matrix elements.
*/
func (tr MatrixTransform) IsTranslation() bool {
return fequals(tr[0], 1) && fequals(tr[1], 0) && fequals(tr[2], 0) && fequals(tr[3], 1)
}
/**
* Compares two floats.
* return true if the distance between the two floats is less than epsilon, false otherwise
*/
func fequals(float1, float2 float64) bool {
return math.Abs(float1-float2) <= epsilon
}
// this VertexConverter apply the Matrix transformation tr
type VertexMatrixTransform struct {
tr MatrixTransform
Next VertexConverter
}
func NewVertexMatrixTransform(tr MatrixTransform, converter VertexConverter) *VertexMatrixTransform {
return &VertexMatrixTransform{tr, converter}
}
// Vertex Matrix Transform
func (vmt *VertexMatrixTransform) NextCommand(command VertexCommand) {
vmt.Next.NextCommand(command)
}
func (vmt *VertexMatrixTransform) Vertex(x, y float64) {
u := x*vmt.tr[0] + y*vmt.tr[2] + vmt.tr[4]
v := x*vmt.tr[1] + y*vmt.tr[3] + vmt.tr[5]
vmt.Next.Vertex(u, v)
}
// this adder apply a Matrix transformation to points
type MatrixTransformAdder struct {
tr MatrixTransform
next raster.Adder
}
func NewMatrixTransformAdder(tr MatrixTransform, adder raster.Adder) *MatrixTransformAdder {
return &MatrixTransformAdder{tr, adder}
}
// Start starts a new curve at the given point.
func (mta MatrixTransformAdder) Start(a raster.Point) {
mta.tr.TransformRasterPoint(&a)
mta.next.Start(a)
}
// Add1 adds a linear segment to the current curve.
func (mta MatrixTransformAdder) Add1(b raster.Point) {
mta.tr.TransformRasterPoint(&b)
mta.next.Add1(b)
}
// Add2 adds a quadratic segment to the current curve.
func (mta MatrixTransformAdder) Add2(b, c raster.Point) {
mta.tr.TransformRasterPoint(&b, &c)
mta.next.Add2(b, c)
}
// Add3 adds a cubic segment to the current curve.
func (mta MatrixTransformAdder) Add3(b, c, d raster.Point) {
mta.tr.TransformRasterPoint(&b, &c, &d)
mta.next.Add3(b, c, d)
}

View File

@@ -1,19 +0,0 @@
// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff
package draw2d
type VertexCommand byte
const (
VertexNoCommand VertexCommand = iota
VertexStartCommand
VertexJoinCommand
VertexCloseCommand
VertexStopCommand
)
type VertexConverter interface {
NextCommand(cmd VertexCommand)
Vertex(x, y float64)
}

View File

@@ -1,280 +0,0 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package raster
import (
"fmt"
"math"
)
// A Fix32 is a 24.8 fixed point number.
type Fix32 int32
// A Fix64 is a 48.16 fixed point number.
type Fix64 int64
// String returns a human-readable representation of a 24.8 fixed point number.
// For example, the number one-and-a-quarter becomes "1:064".
func (x Fix32) String() string {
if x < 0 {
x = -x
return fmt.Sprintf("-%d:%03d", int32(x/256), int32(x%256))
}
return fmt.Sprintf("%d:%03d", int32(x/256), int32(x%256))
}
// String returns a human-readable representation of a 48.16 fixed point number.
// For example, the number one-and-a-quarter becomes "1:16384".
func (x Fix64) String() string {
if x < 0 {
x = -x
return fmt.Sprintf("-%d:%05d", int64(x/65536), int64(x%65536))
}
return fmt.Sprintf("%d:%05d", int64(x/65536), int64(x%65536))
}
// maxAbs returns the maximum of abs(a) and abs(b).
func maxAbs(a, b Fix32) Fix32 {
if a < 0 {
a = -a
}
if b < 0 {
b = -b
}
if a < b {
return b
}
return a
}
// A Point represents a two-dimensional point or vector, in 24.8 fixed point
// format.
type Point struct {
X, Y Fix32
}
// String returns a human-readable representation of a Point.
func (p Point) String() string {
return "(" + p.X.String() + ", " + p.Y.String() + ")"
}
// Add returns the vector p + q.
func (p Point) Add(q Point) Point {
return Point{p.X + q.X, p.Y + q.Y}
}
// Sub returns the vector p - q.
func (p Point) Sub(q Point) Point {
return Point{p.X - q.X, p.Y - q.Y}
}
// Mul returns the vector k * p.
func (p Point) Mul(k Fix32) Point {
return Point{p.X * k / 256, p.Y * k / 256}
}
// Neg returns the vector -p, or equivalently p rotated by 180 degrees.
func (p Point) Neg() Point {
return Point{-p.X, -p.Y}
}
// Dot returns the dot product p·q.
func (p Point) Dot(q Point) Fix64 {
px, py := int64(p.X), int64(p.Y)
qx, qy := int64(q.X), int64(q.Y)
return Fix64(px*qx + py*qy)
}
// Len returns the length of the vector p.
func (p Point) Len() Fix32 {
// TODO(nigeltao): use fixed point math.
x := float64(p.X)
y := float64(p.Y)
return Fix32(math.Sqrt(x*x + y*y))
}
// Norm returns the vector p normalized to the given length, or the zero Point
// if p is degenerate.
func (p Point) Norm(length Fix32) Point {
d := p.Len()
if d == 0 {
return Point{}
}
s, t := int64(length), int64(d)
x := int64(p.X) * s / t
y := int64(p.Y) * s / t
return Point{Fix32(x), Fix32(y)}
}
// Rot45CW returns the vector p rotated clockwise by 45 degrees.
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
func (p Point) Rot45CW() Point {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (+px - py) * 181 / 256
qy := (+px + py) * 181 / 256
return Point{Fix32(qx), Fix32(qy)}
}
// Rot90CW returns the vector p rotated clockwise by 90 degrees.
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
func (p Point) Rot90CW() Point {
return Point{-p.Y, p.X}
}
// Rot135CW returns the vector p rotated clockwise by 135 degrees.
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
func (p Point) Rot135CW() Point {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (-px - py) * 181 / 256
qy := (+px - py) * 181 / 256
return Point{Fix32(qx), Fix32(qy)}
}
// Rot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
func (p Point) Rot45CCW() Point {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (+px + py) * 181 / 256
qy := (-px + py) * 181 / 256
return Point{Fix32(qx), Fix32(qy)}
}
// Rot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
func (p Point) Rot90CCW() Point {
return Point{p.Y, -p.X}
}
// Rot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
func (p Point) Rot135CCW() Point {
// 181/256 is approximately 1/√2, or sin(π/4).
px, py := int64(p.X), int64(p.Y)
qx := (-px + py) * 181 / 256
qy := (-px - py) * 181 / 256
return Point{Fix32(qx), Fix32(qy)}
}
// An Adder accumulates points on a curve.
type Adder interface {
// Start starts a new curve at the given point.
Start(a Point)
// Add1 adds a linear segment to the current curve.
Add1(b Point)
// Add2 adds a quadratic segment to the current curve.
Add2(b, c Point)
// Add3 adds a cubic segment to the current curve.
Add3(b, c, d Point)
}
// A Path is a sequence of curves, and a curve is a start point followed by a
// sequence of linear, quadratic or cubic segments.
type Path []Fix32
// String returns a human-readable representation of a Path.
func (p Path) String() string {
s := ""
for i := 0; i < len(p); {
if i != 0 {
s += " "
}
switch p[i] {
case 0:
s += "S0" + fmt.Sprint([]Fix32(p[i+1:i+3]))
i += 4
case 1:
s += "A1" + fmt.Sprint([]Fix32(p[i+1:i+3]))
i += 4
case 2:
s += "A2" + fmt.Sprint([]Fix32(p[i+1:i+5]))
i += 6
case 3:
s += "A3" + fmt.Sprint([]Fix32(p[i+1:i+7]))
i += 8
default:
panic("freetype/raster: bad path")
}
}
return s
}
// Clear cancels any previous calls to p.Start or p.AddXxx.
func (p *Path) Clear() {
*p = (*p)[:0]
}
// Start starts a new curve at the given point.
func (p *Path) Start(a Point) {
*p = append(*p, 0, a.X, a.Y, 0)
}
// Add1 adds a linear segment to the current curve.
func (p *Path) Add1(b Point) {
*p = append(*p, 1, b.X, b.Y, 1)
}
// Add2 adds a quadratic segment to the current curve.
func (p *Path) Add2(b, c Point) {
*p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
}
// Add3 adds a cubic segment to the current curve.
func (p *Path) Add3(b, c, d Point) {
*p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
}
// AddPath adds the Path q to p.
func (p *Path) AddPath(q Path) {
*p = append(*p, q...)
}
// AddStroke adds a stroked Path.
func (p *Path) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
Stroke(p, q, width, cr, jr)
}
// firstPoint returns the first point in a non-empty Path.
func (p Path) firstPoint() Point {
return Point{p[1], p[2]}
}
// lastPoint returns the last point in a non-empty Path.
func (p Path) lastPoint() Point {
return Point{p[len(p)-3], p[len(p)-2]}
}
// addPathReversed adds q reversed to p.
// For example, if q consists of a linear segment from A to B followed by a
// quadratic segment from B to C to D, then the values of q looks like:
// index: 01234567890123
// value: 0AA01BB12CCDD2
// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
func addPathReversed(p Adder, q Path) {
if len(q) == 0 {
return
}
i := len(q) - 1
for {
switch q[i] {
case 0:
return
case 1:
i -= 4
p.Add1(Point{q[i-2], q[i-1]})
case 2:
i -= 6
p.Add2(Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]})
case 3:
i -= 8
p.Add3(Point{q[i+4], q[i+5]}, Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]})
default:
panic("freetype/raster: bad path")
}
}
}

View File

@@ -1,292 +0,0 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package raster
import (
"image"
"image/color"
"image/draw"
"math"
)
// A Span is a horizontal segment of pixels with constant alpha. X0 is an
// inclusive bound and X1 is exclusive, the same as for slices. A fully
// opaque Span has A == 1<<32 - 1.
type Span struct {
Y, X0, X1 int
A uint32
}
// A Painter knows how to paint a batch of Spans. Rasterization may involve
// Painting multiple batches, and done will be true for the final batch.
// The Spans' Y values are monotonically increasing during a rasterization.
// Paint may use all of ss as scratch space during the call.
type Painter interface {
Paint(ss []Span, done bool)
}
// The PainterFunc type adapts an ordinary function to the Painter interface.
type PainterFunc func(ss []Span, done bool)
// Paint just delegates the call to f.
func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
// An AlphaOverPainter is a Painter that paints Spans onto an image.Alpha
// using the Over Porter-Duff composition operator.
type AlphaOverPainter struct {
Image *image.Alpha
}
// Paint satisfies the Painter interface by painting ss onto an image.Alpha.
func (r AlphaOverPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
p := r.Image.Pix[base+s.X0 : base+s.X1]
a := int(s.A >> 24)
for i, c := range p {
v := int(c)
p[i] = uint8((v*255 + (255-v)*a) / 255)
}
}
}
// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
return AlphaOverPainter{m}
}
// An AlphaSrcPainter is a Painter that paints Spans onto an image.Alpha
// using the Src Porter-Duff composition operator.
type AlphaSrcPainter struct {
Image *image.Alpha
}
// Paint satisfies the Painter interface by painting ss onto an image.Alpha.
func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
p := r.Image.Pix[base+s.X0 : base+s.X1]
color := uint8(s.A >> 24)
for i := range p {
p[i] = color
}
}
}
// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
return AlphaSrcPainter{m}
}
type RGBAPainter struct {
// The image to compose onto.
Image *image.RGBA
// The Porter-Duff composition operator.
Op draw.Op
// The 16-bit color to paint the spans.
cr, cg, cb, ca uint32
}
// Paint satisfies the Painter interface by painting ss onto an image.RGBA.
func (r *RGBAPainter) Paint(ss []Span, done bool) {
b := r.Image.Bounds()
for _, s := range ss {
if s.Y < b.Min.Y {
continue
}
if s.Y >= b.Max.Y {
return
}
if s.X0 < b.Min.X {
s.X0 = b.Min.X
}
if s.X1 > b.Max.X {
s.X1 = b.Max.X
}
if s.X0 >= s.X1 {
continue
}
// This code is similar to drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go.
ma := s.A >> 16
const m = 1<<16 - 1
i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
i1 := i0 + (s.X1-s.X0)*4
if r.Op == draw.Over {
for i := i0; i < i1; i += 4 {
dr := uint32(r.Image.Pix[i+0])
dg := uint32(r.Image.Pix[i+1])
db := uint32(r.Image.Pix[i+2])
da := uint32(r.Image.Pix[i+3])
a := (m - (r.ca * ma / m)) * 0x101
r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
}
} else {
for i := i0; i < i1; i += 4 {
r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
}
}
}
}
// SetColor sets the color to paint the spans.
func (r *RGBAPainter) SetColor(c color.Color) {
r.cr, r.cg, r.cb, r.ca = c.RGBA()
}
// NewRGBAPainter creates a new RGBAPainter for the given image.
func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
return &RGBAPainter{Image: m}
}
// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
// be either fully opaque or fully transparent.
type MonochromePainter struct {
Painter Painter
y, x0, x1 int
}
// Paint delegates to the wrapped Painter after quantizing each Span's alpha
// value and merging adjacent fully opaque Spans.
func (m *MonochromePainter) Paint(ss []Span, done bool) {
// We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
j := 0
for _, s := range ss {
if s.A >= 1<<31 {
if m.y == s.Y && m.x1 == s.X0 {
m.x1 = s.X1
} else {
ss[j] = Span{m.y, m.x0, m.x1, 1<<32 - 1}
j++
m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
}
}
}
if done {
// Flush the accumulated Span.
finalSpan := Span{m.y, m.x0, m.x1, 1<<32 - 1}
if j < len(ss) {
ss[j] = finalSpan
j++
m.Painter.Paint(ss[:j], true)
} else if j == len(ss) {
m.Painter.Paint(ss, false)
if cap(ss) > 0 {
ss = ss[:1]
} else {
ss = make([]Span, 1)
}
ss[0] = finalSpan
m.Painter.Paint(ss, true)
} else {
panic("unreachable")
}
// Reset the accumulator, so that this Painter can be re-used.
m.y, m.x0, m.x1 = 0, 0, 0
} else {
m.Painter.Paint(ss[:j], false)
}
}
// NewMonochromePainter creates a new MonochromePainter that wraps the given
// Painter.
func NewMonochromePainter(p Painter) *MonochromePainter {
return &MonochromePainter{Painter: p}
}
// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
// on each Span's alpha value.
type GammaCorrectionPainter struct {
// The wrapped Painter.
Painter Painter
// Precomputed alpha values for linear interpolation, with fully opaque == 1<<16-1.
a [256]uint16
// Whether gamma correction is a no-op.
gammaIsOne bool
}
// Paint delegates to the wrapped Painter after performing gamma-correction
// on each Span.
func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
if !g.gammaIsOne {
const (
M = 0x1010101 // 255*M == 1<<32-1
N = 0x8080 // N = M>>9, and N < 1<<16-1
)
for i, s := range ss {
if s.A == 0 || s.A == 1<<32-1 {
continue
}
p, q := s.A/M, (s.A%M)>>9
// The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
a := uint32(g.a[p])*(N-q) + uint32(g.a[p+1])*q
a = (a + N/2) / N
// Convert the alpha from 16-bit (which is g.a's range) to 32-bit.
a |= a << 16
ss[i].A = a
}
}
g.Painter.Paint(ss, done)
}
// SetGamma sets the gamma value.
func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
if gamma == 1.0 {
g.gammaIsOne = true
return
}
g.gammaIsOne = false
for i := 0; i < 256; i++ {
a := float64(i) / 0xff
a = math.Pow(a, gamma)
g.a[i] = uint16(0xffff * a)
}
}
// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
// the given Painter.
func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
g := &GammaCorrectionPainter{Painter: p}
g.SetGamma(gamma)
return g
}

View File

@@ -1,579 +0,0 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
// The raster package provides an anti-aliasing 2-D rasterizer.
//
// It is part of the larger Freetype-Go suite of font-related packages,
// but the raster package is not specific to font rasterization, and can
// be used standalone without any other Freetype-Go package.
//
// Rasterization is done by the same area/coverage accumulation algorithm
// as the Freetype "smooth" module, and the Anti-Grain Geometry library.
// A description of the area/coverage algorithm is at
// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
package raster
import (
"strconv"
)
// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
// area/coverage for the pixel at (xi, yi).
type cell struct {
xi int
area, cover int
next int
}
type Rasterizer struct {
// If false, the default behavior is to use the even-odd winding fill
// rule during Rasterize.
UseNonZeroWinding bool
// An offset (in pixels) to the painted spans.
Dx, Dy int
// The width of the Rasterizer. The height is implicit in len(cellIndex).
width int
// splitScaleN is the scaling factor used to determine how many times
// to decompose a quadratic or cubic segment into a linear approximation.
splitScale2, splitScale3 int
// The current pen position.
a Point
// The current cell and its area/coverage being accumulated.
xi, yi int
area, cover int
// Saved cells.
cell []cell
// Linked list of cells, one per row.
cellIndex []int
// Buffers.
cellBuf [256]cell
cellIndexBuf [64]int
spanBuf [64]Span
}
// findCell returns the index in r.cell for the cell corresponding to
// (r.xi, r.yi). The cell is created if necessary.
func (r *Rasterizer) findCell() int {
if r.yi < 0 || r.yi >= len(r.cellIndex) {
return -1
}
xi := r.xi
if xi < 0 {
xi = -1
} else if xi > r.width {
xi = r.width
}
i, prev := r.cellIndex[r.yi], -1
for i != -1 && r.cell[i].xi <= xi {
if r.cell[i].xi == xi {
return i
}
i, prev = r.cell[i].next, i
}
c := len(r.cell)
if c == cap(r.cell) {
buf := make([]cell, c, 4*c)
copy(buf, r.cell)
r.cell = buf[0 : c+1]
} else {
r.cell = r.cell[0 : c+1]
}
r.cell[c] = cell{xi, 0, 0, i}
if prev == -1 {
r.cellIndex[r.yi] = c
} else {
r.cell[prev].next = c
}
return c
}
// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
func (r *Rasterizer) saveCell() {
if r.area != 0 || r.cover != 0 {
i := r.findCell()
if i != -1 {
r.cell[i].area += r.area
r.cell[i].cover += r.cover
}
r.area = 0
r.cover = 0
}
}
// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
func (r *Rasterizer) setCell(xi, yi int) {
if r.xi != xi || r.yi != yi {
r.saveCell()
r.xi, r.yi = xi, yi
}
}
// scan accumulates area/coverage for the yi'th scanline, going from
// x0 to x1 in the horizontal direction (in 24.8 fixed point co-ordinates)
// and from y0f to y1f fractional vertical units within that scanline.
func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
// Break the 24.8 fixed point X co-ordinates into integral and fractional parts.
x0i := int(x0) / 256
x0f := x0 - Fix32(256*x0i)
x1i := int(x1) / 256
x1f := x1 - Fix32(256*x1i)
// A perfectly horizontal scan.
if y0f == y1f {
r.setCell(x1i, yi)
return
}
dx, dy := x1-x0, y1f-y0f
// A single cell scan.
if x0i == x1i {
r.area += int((x0f + x1f) * dy)
r.cover += int(dy)
return
}
// There are at least two cells. Apart from the first and last cells,
// all intermediate cells go through the full width of the cell,
// or 256 units in 24.8 fixed point format.
var (
p, q, edge0, edge1 Fix32
xiDelta int
)
if dx > 0 {
p, q = (256-x0f)*dy, dx
edge0, edge1, xiDelta = 0, 256, 1
} else {
p, q = x0f*dy, -dx
edge0, edge1, xiDelta = 256, 0, -1
}
yDelta, yRem := p/q, p%q
if yRem < 0 {
yDelta -= 1
yRem += q
}
// Do the first cell.
xi, y := x0i, y0f
r.area += int((x0f + edge1) * yDelta)
r.cover += int(yDelta)
xi, y = xi+xiDelta, y+yDelta
r.setCell(xi, yi)
if xi != x1i {
// Do all the intermediate cells.
p = 256 * (y1f - y + yDelta)
fullDelta, fullRem := p/q, p%q
if fullRem < 0 {
fullDelta -= 1
fullRem += q
}
yRem -= q
for xi != x1i {
yDelta = fullDelta
yRem += fullRem
if yRem >= 0 {
yDelta += 1
yRem -= q
}
r.area += int(256 * yDelta)
r.cover += int(yDelta)
xi, y = xi+xiDelta, y+yDelta
r.setCell(xi, yi)
}
}
// Do the last cell.
yDelta = y1f - y
r.area += int((edge0 + x1f) * yDelta)
r.cover += int(yDelta)
}
// Start starts a new curve at the given point.
func (r *Rasterizer) Start(a Point) {
r.setCell(int(a.X/256), int(a.Y/256))
r.a = a
}
// Add1 adds a linear segment to the current curve.
func (r *Rasterizer) Add1(b Point) {
x0, y0 := r.a.X, r.a.Y
x1, y1 := b.X, b.Y
dx, dy := x1-x0, y1-y0
// Break the 24.8 fixed point Y co-ordinates into integral and fractional parts.
y0i := int(y0) / 256
y0f := y0 - Fix32(256*y0i)
y1i := int(y1) / 256
y1f := y1 - Fix32(256*y1i)
if y0i == y1i {
// There is only one scanline.
r.scan(y0i, x0, y0f, x1, y1f)
} else if dx == 0 {
// This is a vertical line segment. We avoid calling r.scan and instead
// manipulate r.area and r.cover directly.
var (
edge0, edge1 Fix32
yiDelta int
)
if dy > 0 {
edge0, edge1, yiDelta = 0, 256, 1
} else {
edge0, edge1, yiDelta = 256, 0, -1
}
x0i, yi := int(x0)/256, y0i
x0fTimes2 := (int(x0) - (256 * x0i)) * 2
// Do the first pixel.
dcover := int(edge1 - y0f)
darea := int(x0fTimes2 * dcover)
r.area += darea
r.cover += dcover
yi += yiDelta
r.setCell(x0i, yi)
// Do all the intermediate pixels.
dcover = int(edge1 - edge0)
darea = int(x0fTimes2 * dcover)
for yi != y1i {
r.area += darea
r.cover += dcover
yi += yiDelta
r.setCell(x0i, yi)
}
// Do the last pixel.
dcover = int(y1f - edge0)
darea = int(x0fTimes2 * dcover)
r.area += darea
r.cover += dcover
} else {
// There are at least two scanlines. Apart from the first and last scanlines,
// all intermediate scanlines go through the full height of the row, or 256
// units in 24.8 fixed point format.
var (
p, q, edge0, edge1 Fix32
yiDelta int
)
if dy > 0 {
p, q = (256-y0f)*dx, dy
edge0, edge1, yiDelta = 0, 256, 1
} else {
p, q = y0f*dx, -dy
edge0, edge1, yiDelta = 256, 0, -1
}
xDelta, xRem := p/q, p%q
if xRem < 0 {
xDelta -= 1
xRem += q
}
// Do the first scanline.
x, yi := x0, y0i
r.scan(yi, x, y0f, x+xDelta, edge1)
x, yi = x+xDelta, yi+yiDelta
r.setCell(int(x)/256, yi)
if yi != y1i {
// Do all the intermediate scanlines.
p = 256 * dx
fullDelta, fullRem := p/q, p%q
if fullRem < 0 {
fullDelta -= 1
fullRem += q
}
xRem -= q
for yi != y1i {
xDelta = fullDelta
xRem += fullRem
if xRem >= 0 {
xDelta += 1
xRem -= q
}
r.scan(yi, x, edge0, x+xDelta, edge1)
x, yi = x+xDelta, yi+yiDelta
r.setCell(int(x)/256, yi)
}
}
// Do the last scanline.
r.scan(yi, x, edge0, x1, y1f)
}
// The next lineTo starts from b.
r.a = b
}
// Add2 adds a quadratic segment to the current curve.
func (r *Rasterizer) Add2(b, c Point) {
// Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
// Specifically, how much the middle point b deviates from (a+c)/2.
dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / Fix32(r.splitScale2)
nsplit := 0
for dev > 0 {
dev /= 4
nsplit++
}
// dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit is 16.
const maxNsplit = 16
if nsplit > maxNsplit {
panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
}
// Recursively decompose the curve nSplit levels deep.
var (
pStack [2*maxNsplit + 3]Point
sStack [maxNsplit + 1]int
i int
)
sStack[0] = nsplit
pStack[0] = c
pStack[1] = b
pStack[2] = r.a
for i >= 0 {
s := sStack[i]
p := pStack[2*i:]
if s > 0 {
// Split the quadratic curve p[:3] into an equivalent set of two shorter curves:
// p[:3] and p[2:5]. The new p[4] is the old p[2], and p[0] is unchanged.
mx := p[1].X
p[4].X = p[2].X
p[3].X = (p[4].X + mx) / 2
p[1].X = (p[0].X + mx) / 2
p[2].X = (p[1].X + p[3].X) / 2
my := p[1].Y
p[4].Y = p[2].Y
p[3].Y = (p[4].Y + my) / 2
p[1].Y = (p[0].Y + my) / 2
p[2].Y = (p[1].Y + p[3].Y) / 2
// The two shorter curves have one less split to do.
sStack[i] = s - 1
sStack[i+1] = s - 1
i++
} else {
// Replace the level-0 quadratic with a two-linear-piece approximation.
midx := (p[0].X + 2*p[1].X + p[2].X) / 4
midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
r.Add1(Point{midx, midy})
r.Add1(p[0])
i--
}
}
}
// Add3 adds a cubic segment to the current curve.
func (r *Rasterizer) Add3(b, c, d Point) {
// Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / Fix32(r.splitScale2)
dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / Fix32(r.splitScale3)
nsplit := 0
for dev2 > 0 || dev3 > 0 {
dev2 /= 8
dev3 /= 4
nsplit++
}
// devN is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit is 16.
const maxNsplit = 16
if nsplit > maxNsplit {
panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
}
// Recursively decompose the curve nSplit levels deep.
var (
pStack [3*maxNsplit + 4]Point
sStack [maxNsplit + 1]int
i int
)
sStack[0] = nsplit
pStack[0] = d
pStack[1] = c
pStack[2] = b
pStack[3] = r.a
for i >= 0 {
s := sStack[i]
p := pStack[3*i:]
if s > 0 {
// Split the cubic curve p[:4] into an equivalent set of two shorter curves:
// p[:4] and p[3:7]. The new p[6] is the old p[3], and p[0] is unchanged.
m01x := (p[0].X + p[1].X) / 2
m12x := (p[1].X + p[2].X) / 2
m23x := (p[2].X + p[3].X) / 2
p[6].X = p[3].X
p[5].X = m23x
p[1].X = m01x
p[2].X = (m01x + m12x) / 2
p[4].X = (m12x + m23x) / 2
p[3].X = (p[2].X + p[4].X) / 2
m01y := (p[0].Y + p[1].Y) / 2
m12y := (p[1].Y + p[2].Y) / 2
m23y := (p[2].Y + p[3].Y) / 2
p[6].Y = p[3].Y
p[5].Y = m23y
p[1].Y = m01y
p[2].Y = (m01y + m12y) / 2
p[4].Y = (m12y + m23y) / 2
p[3].Y = (p[2].Y + p[4].Y) / 2
// The two shorter curves have one less split to do.
sStack[i] = s - 1
sStack[i+1] = s - 1
i++
} else {
// Replace the level-0 cubic with a two-linear-piece approximation.
midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
r.Add1(Point{midx, midy})
r.Add1(p[0])
i--
}
}
}
// AddPath adds the given Path.
func (r *Rasterizer) AddPath(p Path) {
for i := 0; i < len(p); {
switch p[i] {
case 0:
r.Start(Point{p[i+1], p[i+2]})
i += 4
case 1:
r.Add1(Point{p[i+1], p[i+2]})
i += 4
case 2:
r.Add2(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]})
i += 6
case 3:
r.Add3(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]}, Point{p[i+5], p[i+6]})
i += 8
default:
panic("freetype/raster: bad path")
}
}
}
// AddStroke adds a stroked Path.
func (r *Rasterizer) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
Stroke(r, q, width, cr, jr)
}
// Converts an area value to a uint32 alpha value. A completely filled pixel
// corresponds to an area of 256*256*2, and an alpha of 1<<32-1. The
// conversion of area values greater than this depends on the winding rule:
// even-odd or non-zero.
func (r *Rasterizer) areaToAlpha(area int) uint32 {
// The C Freetype implementation (version 2.3.12) does "alpha := area>>1" without
// the +1. Round-to-nearest gives a more symmetric result than round-down.
// The C implementation also returns 8-bit alpha, not 32-bit alpha.
a := (area + 1) >> 1
if a < 0 {
a = -a
}
alpha := uint32(a)
if r.UseNonZeroWinding {
if alpha > 0xffff {
alpha = 0xffff
}
} else {
alpha &= 0x1ffff
if alpha > 0x10000 {
alpha = 0x20000 - alpha
} else if alpha == 0x10000 {
alpha = 0x0ffff
}
}
alpha |= alpha << 16
return alpha
}
// Rasterize converts r's accumulated curves into Spans for p. The Spans
// passed to p are non-overlapping, and sorted by Y and then X. They all
// have non-zero width (and 0 <= X0 < X1 <= r.width) and non-zero A, except
// for the final Span, which has Y, X0, X1 and A all equal to zero.
func (r *Rasterizer) Rasterize(p Painter) {
r.saveCell()
s := 0
for yi := 0; yi < len(r.cellIndex); yi++ {
xi, cover := 0, 0
for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
if cover != 0 && r.cell[c].xi > xi {
alpha := r.areaToAlpha(cover * 256 * 2)
if alpha != 0 {
xi0, xi1 := xi, r.cell[c].xi
if xi0 < 0 {
xi0 = 0
}
if xi1 >= r.width {
xi1 = r.width
}
if xi0 < xi1 {
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
s++
}
}
}
cover += r.cell[c].cover
alpha := r.areaToAlpha(cover*256*2 - r.cell[c].area)
xi = r.cell[c].xi + 1
if alpha != 0 {
xi0, xi1 := r.cell[c].xi, xi
if xi0 < 0 {
xi0 = 0
}
if xi1 >= r.width {
xi1 = r.width
}
if xi0 < xi1 {
r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
s++
}
}
if s > len(r.spanBuf)-2 {
p.Paint(r.spanBuf[:s], false)
s = 0
}
}
}
p.Paint(r.spanBuf[:s], true)
}
// Clear cancels any previous calls to r.Start or r.AddXxx.
func (r *Rasterizer) Clear() {
r.a = Point{}
r.xi = 0
r.yi = 0
r.area = 0
r.cover = 0
r.cell = r.cell[:0]
for i := 0; i < len(r.cellIndex); i++ {
r.cellIndex[i] = -1
}
}
// SetBounds sets the maximum width and height of the rasterized image and
// calls Clear. The width and height are in pixels, not Fix32 units.
func (r *Rasterizer) SetBounds(width, height int) {
if width < 0 {
width = 0
}
if height < 0 {
height = 0
}
// Use the same ssN heuristic as the C Freetype implementation.
// The C implementation uses the values 32, 16, but those are in
// 26.6 fixed point units, and we use 24.8 fixed point everywhere.
ss2, ss3 := 128, 64
if width > 24 || height > 24 {
ss2, ss3 = 2*ss2, 2*ss3
if width > 120 || height > 120 {
ss2, ss3 = 2*ss2, 2*ss3
}
}
r.width = width
r.splitScale2 = ss2
r.splitScale3 = ss3
r.cell = r.cellBuf[:0]
if height > len(r.cellIndexBuf) {
r.cellIndex = make([]int, height)
} else {
r.cellIndex = r.cellIndexBuf[:height]
}
r.Clear()
}
// NewRasterizer creates a new Rasterizer with the given bounds.
func NewRasterizer(width, height int) *Rasterizer {
r := new(Rasterizer)
r.SetBounds(width, height)
return r
}

View File

@@ -1,466 +0,0 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package raster
// Two points are considered practically equal if the square of the distance
// between them is less than one quarter (i.e. 16384 / 65536 in Fix64).
const epsilon = 16384
// A Capper signifies how to begin or end a stroked path.
type Capper interface {
// Cap adds a cap to p given a pivot point and the normal vector of a
// terminal segment. The normal's length is half of the stroke width.
Cap(p Adder, halfWidth Fix32, pivot, n1 Point)
}
// The CapperFunc type adapts an ordinary function to be a Capper.
type CapperFunc func(Adder, Fix32, Point, Point)
func (f CapperFunc) Cap(p Adder, halfWidth Fix32, pivot, n1 Point) {
f(p, halfWidth, pivot, n1)
}
// A Joiner signifies how to join interior nodes of a stroked path.
type Joiner interface {
// Join adds a join to the two sides of a stroked path given a pivot
// point and the normal vectors of the trailing and leading segments.
// Both normals have length equal to half of the stroke width.
Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point)
}
// The JoinerFunc type adapts an ordinary function to be a Joiner.
type JoinerFunc func(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point)
func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point) {
f(lhs, rhs, halfWidth, pivot, n0, n1)
}
// RoundCapper adds round caps to a stroked path.
var RoundCapper Capper = CapperFunc(roundCapper)
func roundCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
// The cubic Bézier approximation to a circle involves the magic number
// (√2 - 1) * 4/3, which is approximately 141/256.
const k = 141
e := n1.Rot90CCW()
side := pivot.Add(e)
start, end := pivot.Sub(n1), pivot.Add(n1)
d, e := n1.Mul(k), e.Mul(k)
p.Add3(start.Add(e), side.Sub(d), side)
p.Add3(side.Add(d), end.Add(e), end)
}
// ButtCapper adds butt caps to a stroked path.
var ButtCapper Capper = CapperFunc(buttCapper)
func buttCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
p.Add1(pivot.Add(n1))
}
// SquareCapper adds square caps to a stroked path.
var SquareCapper Capper = CapperFunc(squareCapper)
func squareCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
e := n1.Rot90CCW()
side := pivot.Add(e)
p.Add1(side.Sub(n1))
p.Add1(side.Add(n1))
p.Add1(pivot.Add(n1))
}
// RoundJoiner adds round joins to a stroked path.
var RoundJoiner Joiner = JoinerFunc(roundJoiner)
func roundJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
dot := n0.Rot90CW().Dot(n1)
if dot >= 0 {
addArc(lhs, pivot, n0, n1)
rhs.Add1(pivot.Sub(n1))
} else {
lhs.Add1(pivot.Add(n1))
addArc(rhs, pivot, n0.Neg(), n1.Neg())
}
}
// BevelJoiner adds bevel joins to a stroked path.
var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
func bevelJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
lhs.Add1(pivot.Add(n1))
rhs.Add1(pivot.Sub(n1))
}
// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
// the two possible arcs is taken, i.e. the one spanning <= 180 degrees.
// The two vectors n0 and n1 must be of equal length.
func addArc(p Adder, pivot, n0, n1 Point) {
// r2 is the square of the length of n0.
r2 := n0.Dot(n0)
if r2 < epsilon {
// The arc radius is so small that we collapse to a straight line.
p.Add1(pivot.Add(n1))
return
}
// We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
// a final quadratic segment from s to n1. Each 45-degree segment has control
// points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled, rotated and
// translated. tan(π/8) is approximately 106/256.
const tpo8 = 106
var s Point
// We determine which octant the angle between n0 and n1 is in via three dot products.
// m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135 degrees.
m0 := n0.Rot45CW()
m1 := n0.Rot90CW()
m2 := m0.Rot90CW()
if m1.Dot(n1) >= 0 {
if n0.Dot(n1) >= 0 {
if m2.Dot(n1) <= 0 {
// n1 is between 0 and 45 degrees clockwise of n0.
s = n0
} else {
// n1 is between 45 and 90 degrees clockwise of n0.
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
s = m0
}
} else {
pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
p.Add2(pm1.Add(n0t), pm1)
if m0.Dot(n1) >= 0 {
// n1 is between 90 and 135 degrees clockwise of n0.
s = m1
} else {
// n1 is between 135 and 180 degrees clockwise of n0.
p.Add2(pm1.Sub(n0t), pivot.Add(m2))
s = m2
}
}
} else {
if n0.Dot(n1) >= 0 {
if m0.Dot(n1) >= 0 {
// n1 is between 0 and 45 degrees counter-clockwise of n0.
s = n0
} else {
// n1 is between 45 and 90 degrees counter-clockwise of n0.
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
s = m2.Neg()
}
} else {
pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
p.Add2(pm1.Add(n0t), pm1)
if m2.Dot(n1) <= 0 {
// n1 is between 90 and 135 degrees counter-clockwise of n0.
s = m1.Neg()
} else {
// n1 is between 135 and 180 degrees counter-clockwise of n0.
p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
s = m0.Neg()
}
}
}
// The final quadratic segment has two endpoints s and n1 and the middle
// control point is a multiple of s.Add(n1), i.e. it is on the angle bisector
// of those two points. The multiple ranges between 128/256 and 150/256 as
// the angle between s and n1 ranges between 0 and 45 degrees.
// When the angle is 0 degrees (i.e. s and n1 are coincident) then s.Add(n1)
// is twice s and so the middle control point of the degenerate quadratic
// segment should be half s.Add(n1), and half = 128/256.
// When the angle is 45 degrees then 150/256 is the ratio of the lengths of
// the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
// d is the normalized dot product between s and n1. Since the angle ranges
// between 0 and 45 degrees then d ranges between 256/256 and 181/256.
d := 256 * s.Dot(n1) / r2
multiple := Fix32(150 - 22*(d-181)/(256-181))
p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
}
// midpoint returns the midpoint of two Points.
func midpoint(a, b Point) Point {
return Point{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
}
// angleGreaterThan45 returns whether the angle between two vectors is more
// than 45 degrees.
func angleGreaterThan45(v0, v1 Point) bool {
v := v0.Rot45CCW()
return v.Dot(v1) < 0 || v.Rot90CW().Dot(v1) < 0
}
// interpolate returns the point (1-t)*a + t*b.
func interpolate(a, b Point, t Fix64) Point {
s := 65536 - t
x := s*Fix64(a.X) + t*Fix64(b.X)
y := s*Fix64(a.Y) + t*Fix64(b.Y)
return Point{Fix32(x >> 16), Fix32(y >> 16)}
}
// curviest2 returns the value of t for which the quadratic parametric curve
// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
//
// The curvature of the parametric curve f(t) = (x(t), y(t)) is
// |xy″-yx″| / (x²+y²)^(3/2).
//
// Let d = b-a and e = c-2*b+a, so that f(t) = 2*d+2*e*t and f″(t) = 2*e.
// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
//
// Thus, curvature is extreme where the denominator is extreme, i.e. where
// (x²+y²) is extreme. The first order condition is that
// 2*x*x″+2*y*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
func curviest2(a, b, c Point) Fix64 {
dx := int64(b.X - a.X)
dy := int64(b.Y - a.Y)
ex := int64(c.X - 2*b.X + a.X)
ey := int64(c.Y - 2*b.Y + a.Y)
if ex == 0 && ey == 0 {
return 32768
}
return Fix64(-65536 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
}
// A stroker holds state for stroking a path.
type stroker struct {
// p is the destination that records the stroked path.
p Adder
// u is the half-width of the stroke.
u Fix32
// cr and jr specify how to end and connect path segments.
cr Capper
jr Joiner
// r is the reverse path. Stroking a path involves constructing two
// parallel paths 2*u apart. The first path is added immediately to p,
// the second path is accumulated in r and eventually added in reverse.
r Path
// a is the most recent segment point. anorm is the segment normal of
// length u at that point.
a, anorm Point
}
// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
func (k *stroker) addNonCurvy2(b, c Point) {
// We repeatedly divide the segment at its middle until it is straight
// enough to approximate the stroke by just translating the control points.
// ds and ps are stacks of depths and points. t is the top of the stack.
const maxDepth = 5
var (
ds [maxDepth + 1]int
ps [2*maxDepth + 3]Point
t int
)
// Initially the ps stack has one quadratic segment of depth zero.
ds[0] = 0
ps[2] = k.a
ps[1] = b
ps[0] = c
anorm := k.anorm
var cnorm Point
for {
depth := ds[t]
a := ps[2*t+2]
b := ps[2*t+1]
c := ps[2*t+0]
ab := b.Sub(a)
bc := c.Sub(b)
abIsSmall := ab.Dot(ab) < Fix64(1<<16)
bcIsSmall := bc.Dot(bc) < Fix64(1<<16)
if abIsSmall && bcIsSmall {
// Approximate the segment by a circular arc.
cnorm = bc.Norm(k.u).Rot90CCW()
mac := midpoint(a, c)
addArc(k.p, mac, anorm, cnorm)
addArc(&k.r, mac, anorm.Neg(), cnorm.Neg())
} else if depth < maxDepth && angleGreaterThan45(ab, bc) {
// Divide the segment in two and push both halves on the stack.
mab := midpoint(a, b)
mbc := midpoint(b, c)
t++
ds[t+0] = depth + 1
ds[t-1] = depth + 1
ps[2*t+2] = a
ps[2*t+1] = mab
ps[2*t+0] = midpoint(mab, mbc)
ps[2*t-1] = mbc
continue
} else {
// Translate the control points.
bnorm := c.Sub(a).Norm(k.u).Rot90CCW()
cnorm = bc.Norm(k.u).Rot90CCW()
k.p.Add2(b.Add(bnorm), c.Add(cnorm))
k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
}
if t == 0 {
k.a, k.anorm = c, cnorm
return
}
t--
anorm = cnorm
}
panic("unreachable")
}
// Add1 adds a linear segment to the stroker.
func (k *stroker) Add1(b Point) {
bnorm := b.Sub(k.a).Norm(k.u).Rot90CCW()
if len(k.r) == 0 {
k.p.Start(k.a.Add(bnorm))
k.r.Start(k.a.Sub(bnorm))
} else {
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
}
k.p.Add1(b.Add(bnorm))
k.r.Add1(b.Sub(bnorm))
k.a, k.anorm = b, bnorm
}
// Add2 adds a quadratic segment to the stroker.
func (k *stroker) Add2(b, c Point) {
ab := b.Sub(k.a)
bc := c.Sub(b)
abnorm := ab.Norm(k.u).Rot90CCW()
if len(k.r) == 0 {
k.p.Start(k.a.Add(abnorm))
k.r.Start(k.a.Sub(abnorm))
} else {
k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
}
// Approximate nearly-degenerate quadratics by linear segments.
abIsSmall := ab.Dot(ab) < epsilon
bcIsSmall := bc.Dot(bc) < epsilon
if abIsSmall || bcIsSmall {
acnorm := c.Sub(k.a).Norm(k.u).Rot90CCW()
k.p.Add1(c.Add(acnorm))
k.r.Add1(c.Sub(acnorm))
k.a, k.anorm = c, acnorm
return
}
// The quadratic segment (k.a, b, c) has a point of maximum curvature.
// If this occurs at an end point, we process the segment as a whole.
t := curviest2(k.a, b, c)
if t <= 0 || t >= 65536 {
k.addNonCurvy2(b, c)
return
}
// Otherwise, we perform a de Casteljau decomposition at the point of
// maximum curvature and process the two straighter parts.
mab := interpolate(k.a, b, t)
mbc := interpolate(b, c, t)
mabc := interpolate(mab, mbc, t)
// If the vectors ab and bc are close to being in opposite directions,
// then the decomposition can become unstable, so we approximate the
// quadratic segment by two linear segments joined by an arc.
bcnorm := bc.Norm(k.u).Rot90CCW()
if abnorm.Dot(bcnorm) < -Fix64(k.u)*Fix64(k.u)*2047/2048 {
pArc := abnorm.Dot(bc) < 0
k.p.Add1(mabc.Add(abnorm))
if pArc {
z := abnorm.Rot90CW()
addArc(k.p, mabc, abnorm, z)
addArc(k.p, mabc, z, bcnorm)
}
k.p.Add1(mabc.Add(bcnorm))
k.p.Add1(c.Add(bcnorm))
k.r.Add1(mabc.Sub(abnorm))
if !pArc {
z := abnorm.Rot90CW()
addArc(&k.r, mabc, abnorm.Neg(), z)
addArc(&k.r, mabc, z, bcnorm.Neg())
}
k.r.Add1(mabc.Sub(bcnorm))
k.r.Add1(c.Sub(bcnorm))
k.a, k.anorm = c, bcnorm
return
}
// Process the decomposed parts.
k.addNonCurvy2(mab, mabc)
k.addNonCurvy2(mbc, c)
}
// Add3 adds a cubic segment to the stroker.
func (k *stroker) Add3(b, c, d Point) {
panic("freetype/raster: stroke unimplemented for cubic segments")
}
// stroke adds the stroked Path q to p, where q consists of exactly one curve.
func (k *stroker) stroke(q Path) {
// Stroking is implemented by deriving two paths each k.u apart from q.
// The left-hand-side path is added immediately to k.p; the right-hand-side
// path is accumulated in k.r. Once we've finished adding the LHS to k.p,
// we add the RHS in reverse order.
k.r = make(Path, 0, len(q))
k.a = Point{q[1], q[2]}
for i := 4; i < len(q); {
switch q[i] {
case 1:
k.Add1(Point{q[i+1], q[i+2]})
i += 4
case 2:
k.Add2(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]})
i += 6
case 3:
k.Add3(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]}, Point{q[i+5], q[i+6]})
i += 8
default:
panic("freetype/raster: bad path")
}
}
if len(k.r) == 0 {
return
}
// TODO(nigeltao): if q is a closed curve then we should join the first and
// last segments instead of capping them.
k.cr.Cap(k.p, k.u, q.lastPoint(), k.anorm.Neg())
addPathReversed(k.p, k.r)
pivot := q.firstPoint()
k.cr.Cap(k.p, k.u, pivot, pivot.Sub(Point{k.r[1], k.r[2]}))
}
// Stroke adds q stroked with the given width to p. The result is typically
// self-intersecting and should be rasterized with UseNonZeroWinding.
// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
func Stroke(p Adder, q Path, width Fix32, cr Capper, jr Joiner) {
if len(q) == 0 {
return
}
if cr == nil {
cr = RoundCapper
}
if jr == nil {
jr = RoundJoiner
}
if q[0] != 0 {
panic("freetype/raster: bad path")
}
s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
i := 0
for j := 4; j < len(q); {
switch q[j] {
case 0:
s.stroke(q[i:j])
i, j = j, j+4
case 1:
j += 4
case 2:
j += 6
case 3:
j += 8
default:
panic("freetype/raster: bad path")
}
}
s.stroke(q[i:])
}

View File

@@ -1,530 +0,0 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package truetype
// Hinting is the policy for snapping a glyph's contours to pixel boundaries.
type Hinting int32
const (
// NoHinting means to not perform any hinting.
NoHinting Hinting = iota
// FullHinting means to use the font's hinting instructions.
FullHinting
// TODO: implement VerticalHinting.
)
// A Point is a co-ordinate pair plus whether it is ``on'' a contour or an
// ``off'' control point.
type Point struct {
X, Y int32
// The Flags' LSB means whether or not this Point is ``on'' the contour.
// Other bits are reserved for internal use.
Flags uint32
}
// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
// series of glyphs from a Font.
type GlyphBuf struct {
// AdvanceWidth is the glyph's advance width.
AdvanceWidth int32
// B is the glyph's bounding box.
B Bounds
// Point contains all Points from all contours of the glyph. If
// hinting was used to load a glyph then Unhinted contains those
// Points before they were hinted, and InFontUnits contains those
// Points before they were hinted and scaled.
Point, Unhinted, InFontUnits []Point
// End is the point indexes of the end point of each countour. The
// length of End is the number of contours in the glyph. The i'th
// contour consists of points Point[End[i-1]:End[i]], where End[-1]
// is interpreted to mean zero.
End []int
font *Font
scale int32
hinting Hinting
hinter hinter
// phantomPoints are the co-ordinates of the synthetic phantom points
// used for hinting and bounding box calculations.
phantomPoints [4]Point
// pp1x is the X co-ordinate of the first phantom point. The '1' is
// using 1-based indexing; pp1x is almost always phantomPoints[0].X.
// TODO: eliminate this and consistently use phantomPoints[0].X.
pp1x int32
// metricsSet is whether the glyph's metrics have been set yet. For a
// compound glyph, a sub-glyph may override the outer glyph's metrics.
metricsSet bool
// tmp is a scratch buffer.
tmp []Point
}
// Flags for decoding a glyph's contours. These flags are documented at
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
const (
flagOnCurve = 1 << iota
flagXShortVector
flagYShortVector
flagRepeat
flagPositiveXShortVector
flagPositiveYShortVector
// The remaining flags are for internal use.
flagTouchedX
flagTouchedY
)
// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
// dependent on the value of the flag{X,Y}ShortVector bits.
const (
flagThisXIsSame = flagPositiveXShortVector
flagThisYIsSame = flagPositiveYShortVector
)
// Load loads a glyph's contours from a Font, overwriting any previously
// loaded contours for this GlyphBuf. scale is the number of 26.6 fixed point
// units in 1 em, i is the glyph index, and h is the hinting policy.
func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h Hinting) error {
g.Point = g.Point[:0]
g.Unhinted = g.Unhinted[:0]
g.InFontUnits = g.InFontUnits[:0]
g.End = g.End[:0]
g.font = f
g.hinting = h
g.scale = scale
g.pp1x = 0
g.phantomPoints = [4]Point{}
g.metricsSet = false
if h != NoHinting {
if err := g.hinter.init(f, scale); err != nil {
return err
}
}
if err := g.load(0, i, true); err != nil {
return err
}
// TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
// and should be cleaned up once we have all the testScaling tests passing,
// plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
pp1x := g.pp1x
if h != NoHinting {
pp1x = g.phantomPoints[0].X
}
if pp1x != 0 {
for i := range g.Point {
g.Point[i].X -= pp1x
}
}
advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
if h != NoHinting {
if len(f.hdmx) >= 8 {
if n := u32(f.hdmx, 4); n > 3+uint32(i) {
for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
if int32(hdmx[0]) == scale>>6 {
advanceWidth = int32(hdmx[2+i]) << 6
break
}
}
}
}
advanceWidth = (advanceWidth + 32) &^ 63
}
g.AdvanceWidth = advanceWidth
// Set g.B to the 'control box', which is the bounding box of the Bézier
// curves' control points. This is easier to calculate, no smaller than
// and often equal to the tightest possible bounding box of the curves
// themselves. This approach is what C Freetype does. We can't just scale
// the nominal bounding box in the glyf data as the hinting process and
// phantom point adjustment may move points outside of that box.
if len(g.Point) == 0 {
g.B = Bounds{}
} else {
p := g.Point[0]
g.B.XMin = p.X
g.B.XMax = p.X
g.B.YMin = p.Y
g.B.YMax = p.Y
for _, p := range g.Point[1:] {
if g.B.XMin > p.X {
g.B.XMin = p.X
} else if g.B.XMax < p.X {
g.B.XMax = p.X
}
if g.B.YMin > p.Y {
g.B.YMin = p.Y
} else if g.B.YMax < p.Y {
g.B.YMax = p.Y
}
}
// Snap the box to the grid, if hinting is on.
if h != NoHinting {
g.B.XMin &^= 63
g.B.YMin &^= 63
g.B.XMax += 63
g.B.XMax &^= 63
g.B.YMax += 63
g.B.YMax &^= 63
}
}
return nil
}
func (g *GlyphBuf) load(recursion int32, i Index, useMyMetrics bool) (err error) {
// The recursion limit here is arbitrary, but defends against malformed glyphs.
if recursion >= 32 {
return UnsupportedError("excessive compound glyph recursion")
}
// Find the relevant slice of g.font.glyf.
var g0, g1 uint32
if g.font.locaOffsetFormat == locaOffsetFormatShort {
g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
} else {
g0 = u32(g.font.loca, 4*int(i))
g1 = u32(g.font.loca, 4*int(i)+4)
}
// Decode the contour count and nominal bounding box, from the first
// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
// and 6, are unused.
glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, int32(0), int32(0)
if g0+10 <= g1 {
glyf = g.font.glyf[g0:g1]
ne = int(int16(u16(glyf, 0)))
boundsXMin = int32(int16(u16(glyf, 2)))
boundsYMax = int32(int16(u16(glyf, 8)))
}
// Create the phantom points.
uhm, pp1x := g.font.unscaledHMetric(i), int32(0)
uvm := g.font.unscaledVMetric(i, boundsYMax)
g.phantomPoints = [4]Point{
{X: boundsXMin - uhm.LeftSideBearing},
{X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
{X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
}
if len(glyf) == 0 {
g.addPhantomsAndScale(len(g.Point), len(g.Point), true, true)
copy(g.phantomPoints[:], g.Point[len(g.Point)-4:])
g.Point = g.Point[:len(g.Point)-4]
return nil
}
// Load and hint the contours.
if ne < 0 {
if ne != -1 {
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
// "the values -2, -3, and so forth, are reserved for future use."
return UnsupportedError("negative number of contours")
}
pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
return err
}
} else {
np0, ne0 := len(g.Point), len(g.End)
program := g.loadSimple(glyf, ne)
g.addPhantomsAndScale(np0, np0, true, true)
pp1x = g.Point[len(g.Point)-4].X
if g.hinting != NoHinting {
if len(program) != 0 {
err := g.hinter.run(
program,
g.Point[np0:],
g.Unhinted[np0:],
g.InFontUnits[np0:],
g.End[ne0:],
)
if err != nil {
return err
}
}
// Drop the four phantom points.
g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
}
if useMyMetrics {
copy(g.phantomPoints[:], g.Point[len(g.Point)-4:])
}
g.Point = g.Point[:len(g.Point)-4]
if np0 != 0 {
// The hinting program expects the []End values to be indexed relative
// to the inner glyph, not the outer glyph, so we delay adding np0 until
// after the hinting program (if any) has run.
for i := ne0; i < len(g.End); i++ {
g.End[i] += np0
}
}
}
if useMyMetrics && !g.metricsSet {
g.metricsSet = true
g.pp1x = pp1x
}
return nil
}
// loadOffset is the initial offset for loadSimple and loadCompound. The first
// 10 bytes are the number of contours and the bounding box.
const loadOffset = 10
func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
offset := loadOffset
for i := 0; i < ne; i++ {
g.End = append(g.End, 1+int(u16(glyf, offset)))
offset += 2
}
// Note the TrueType hinting instructions.
instrLen := int(u16(glyf, offset))
offset += 2
program = glyf[offset : offset+instrLen]
offset += instrLen
np0 := len(g.Point)
np1 := np0 + int(g.End[len(g.End)-1])
// Decode the flags.
for i := np0; i < np1; {
c := uint32(glyf[offset])
offset++
g.Point = append(g.Point, Point{Flags: c})
i++
if c&flagRepeat != 0 {
count := glyf[offset]
offset++
for ; count > 0; count-- {
g.Point = append(g.Point, Point{Flags: c})
i++
}
}
}
// Decode the co-ordinates.
var x int16
for i := np0; i < np1; i++ {
f := g.Point[i].Flags
if f&flagXShortVector != 0 {
dx := int16(glyf[offset])
offset++
if f&flagPositiveXShortVector == 0 {
x -= dx
} else {
x += dx
}
} else if f&flagThisXIsSame == 0 {
x += int16(u16(glyf, offset))
offset += 2
}
g.Point[i].X = int32(x)
}
var y int16
for i := np0; i < np1; i++ {
f := g.Point[i].Flags
if f&flagYShortVector != 0 {
dy := int16(glyf[offset])
offset++
if f&flagPositiveYShortVector == 0 {
y -= dy
} else {
y += dy
}
} else if f&flagThisYIsSame == 0 {
y += int16(u16(glyf, offset))
offset += 2
}
g.Point[i].Y = int32(y)
}
return program
}
func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index,
glyf []byte, useMyMetrics bool) error {
// Flags for decoding a compound glyph. These flags are documented at
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
const (
flagArg1And2AreWords = 1 << iota
flagArgsAreXYValues
flagRoundXYToGrid
flagWeHaveAScale
flagUnused
flagMoreComponents
flagWeHaveAnXAndYScale
flagWeHaveATwoByTwo
flagWeHaveInstructions
flagUseMyMetrics
flagOverlapCompound
)
np0, ne0 := len(g.Point), len(g.End)
offset := loadOffset
for {
flags := u16(glyf, offset)
component := Index(u16(glyf, offset+2))
dx, dy, transform, hasTransform := int32(0), int32(0), [4]int32{}, false
if flags&flagArg1And2AreWords != 0 {
dx = int32(int16(u16(glyf, offset+4)))
dy = int32(int16(u16(glyf, offset+6)))
offset += 8
} else {
dx = int32(int16(int8(glyf[offset+4])))
dy = int32(int16(int8(glyf[offset+5])))
offset += 6
}
if flags&flagArgsAreXYValues == 0 {
return UnsupportedError("compound glyph transform vector")
}
if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
hasTransform = true
switch {
case flags&flagWeHaveAScale != 0:
transform[0] = int32(int16(u16(glyf, offset+0)))
transform[3] = transform[0]
offset += 2
case flags&flagWeHaveAnXAndYScale != 0:
transform[0] = int32(int16(u16(glyf, offset+0)))
transform[3] = int32(int16(u16(glyf, offset+2)))
offset += 4
case flags&flagWeHaveATwoByTwo != 0:
transform[0] = int32(int16(u16(glyf, offset+0)))
transform[1] = int32(int16(u16(glyf, offset+2)))
transform[2] = int32(int16(u16(glyf, offset+4)))
transform[3] = int32(int16(u16(glyf, offset+6)))
offset += 8
}
}
savedPP := g.phantomPoints
np0 := len(g.Point)
componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
if err := g.load(recursion+1, component, componentUMM); err != nil {
return err
}
if flags&flagUseMyMetrics == 0 {
g.phantomPoints = savedPP
}
if hasTransform {
for j := np0; j < len(g.Point); j++ {
p := &g.Point[j]
newX := int32((int64(p.X)*int64(transform[0])+1<<13)>>14) +
int32((int64(p.Y)*int64(transform[2])+1<<13)>>14)
newY := int32((int64(p.X)*int64(transform[1])+1<<13)>>14) +
int32((int64(p.Y)*int64(transform[3])+1<<13)>>14)
p.X, p.Y = newX, newY
}
}
dx = g.font.scale(g.scale * dx)
dy = g.font.scale(g.scale * dy)
if flags&flagRoundXYToGrid != 0 {
dx = (dx + 32) &^ 63
dy = (dy + 32) &^ 63
}
for j := np0; j < len(g.Point); j++ {
p := &g.Point[j]
p.X += dx
p.Y += dy
}
// TODO: also adjust g.InFontUnits and g.Unhinted?
if flags&flagMoreComponents == 0 {
break
}
}
instrLen := 0
if g.hinting != NoHinting && offset+2 <= len(glyf) {
instrLen = int(u16(glyf, offset))
offset += 2
}
g.addPhantomsAndScale(np0, len(g.Point), false, instrLen > 0)
points, ends := g.Point[np0:], g.End[ne0:]
g.Point = g.Point[:len(g.Point)-4]
for j := range points {
points[j].Flags &^= flagTouchedX | flagTouchedY
}
if instrLen == 0 {
if !g.metricsSet {
copy(g.phantomPoints[:], points[len(points)-4:])
}
return nil
}
// Hint the compound glyph.
program := glyf[offset : offset+instrLen]
// Temporarily adjust the ends to be relative to this compound glyph.
if np0 != 0 {
for i := range ends {
ends[i] -= np0
}
}
// Hinting instructions of a composite glyph completely refer to the
// (already) hinted subglyphs.
g.tmp = append(g.tmp[:0], points...)
if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
return err
}
if np0 != 0 {
for i := range ends {
ends[i] += np0
}
}
if !g.metricsSet {
copy(g.phantomPoints[:], points[len(points)-4:])
}
return nil
}
func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
// Add the four phantom points.
g.Point = append(g.Point, g.phantomPoints[:]...)
// Scale the points.
if simple && g.hinting != NoHinting {
g.InFontUnits = append(g.InFontUnits, g.Point[np1:]...)
}
for i := np1; i < len(g.Point); i++ {
p := &g.Point[i]
p.X = g.font.scale(g.scale * p.X)
p.Y = g.font.scale(g.scale * p.Y)
}
if g.hinting == NoHinting {
return
}
// Round the 1st phantom point to the grid, shifting all other points equally.
// Note that "all other points" starts from np0, not np1.
// TODO: delete this adjustment and the np0/np1 distinction, when
// we update the compatibility tests to C Freetype 2.5.3.
// See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
if adjust {
pp1x := g.Point[len(g.Point)-4].X
if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
for i := np0; i < len(g.Point); i++ {
g.Point[i].X += dx
}
}
}
if simple {
g.Unhinted = append(g.Unhinted, g.Point[np1:]...)
}
// Round the 2nd and 4th phantom point to the grid.
p := &g.Point[len(g.Point)-3]
p.X = (p.X + 32) &^ 63
p = &g.Point[len(g.Point)-1]
p.Y = (p.Y + 32) &^ 63
}
// TODO: is this necessary? The zero-valued GlyphBuf is perfectly usable.
// NewGlyphBuf returns a newly allocated GlyphBuf.
func NewGlyphBuf() *GlyphBuf {
return &GlyphBuf{
Point: make([]Point, 0, 256),
End: make([]int, 0, 32),
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,673 +0,0 @@
// Copyright 2012 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package truetype
import (
"reflect"
"strings"
"testing"
)
func TestBytecode(t *testing.T) {
testCases := []struct {
desc string
prog []byte
want []int32
errStr string
}{
{
"underflow",
[]byte{
opDUP,
},
nil,
"underflow",
},
{
"infinite loop",
[]byte{
opPUSHW000, // [-1]
0xff,
0xff,
opDUP, // [-1, -1]
opJMPR, // [-1]
},
nil,
"too many steps",
},
{
"unbalanced if/else",
[]byte{
opPUSHB000, // [0]
0,
opIF,
},
nil,
"unbalanced",
},
{
"vector set/gets",
[]byte{
opSVTCA1, // []
opGPV, // [0x4000, 0]
opSVTCA0, // [0x4000, 0]
opGFV, // [0x4000, 0, 0, 0x4000]
opNEG, // [0x4000, 0, 0, -0x4000]
opSPVFS, // [0x4000, 0]
opSFVTPV, // [0x4000, 0]
opPUSHB000, // [0x4000, 0, 1]
1,
opGFV, // [0x4000, 0, 1, 0, -0x4000]
opPUSHB000, // [0x4000, 0, 1, 0, -0x4000, 2]
2,
},
[]int32{0x4000, 0, 1, 0, -0x4000, 2},
"",
},
{
"jumps",
[]byte{
opPUSHB001, // [10, 2]
10,
2,
opJMPR, // [10]
opDUP, // not executed
opDUP, // [10, 10]
opPUSHB010, // [10, 10, 20, 2, 1]
20,
2,
1,
opJROT, // [10, 10, 20]
opDUP, // not executed
opDUP, // [10, 10, 20, 20]
opPUSHB010, // [10, 10, 20, 20, 30, 2, 1]
30,
2,
1,
opJROF, // [10, 10, 20, 20, 30]
opDUP, // [10, 10, 20, 20, 30, 30]
opDUP, // [10, 10, 20, 20, 30, 30, 30]
},
[]int32{10, 10, 20, 20, 30, 30, 30},
"",
},
{
"stack ops",
[]byte{
opPUSHB010, // [10, 20, 30]
10,
20,
30,
opCLEAR, // []
opPUSHB010, // [40, 50, 60]
40,
50,
60,
opSWAP, // [40, 60, 50]
opDUP, // [40, 60, 50, 50]
opDUP, // [40, 60, 50, 50, 50]
opPOP, // [40, 60, 50, 50]
opDEPTH, // [40, 60, 50, 50, 4]
opCINDEX, // [40, 60, 50, 50, 40]
opPUSHB000, // [40, 60, 50, 50, 40, 4]
4,
opMINDEX, // [40, 50, 50, 40, 60]
},
[]int32{40, 50, 50, 40, 60},
"",
},
{
"push ops",
[]byte{
opPUSHB000, // [255]
255,
opPUSHW001, // [255, -2, 253]
255,
254,
0,
253,
opNPUSHB, // [1, -2, 253, 1, 2]
2,
1,
2,
opNPUSHW, // [1, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809]
3,
4,
5,
6,
7,
8,
9,
},
[]int32{255, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809},
"",
},
{
"store ops",
[]byte{
opPUSHB011, // [1, 22, 3, 44]
1,
22,
3,
44,
opWS, // [1, 22]
opWS, // []
opPUSHB000, // [3]
3,
opRS, // [44]
},
[]int32{44},
"",
},
{
"comparison ops",
[]byte{
opPUSHB001, // [10, 20]
10,
20,
opLT, // [1]
opPUSHB001, // [1, 10, 20]
10,
20,
opLTEQ, // [1, 1]
opPUSHB001, // [1, 1, 10, 20]
10,
20,
opGT, // [1, 1, 0]
opPUSHB001, // [1, 1, 0, 10, 20]
10,
20,
opGTEQ, // [1, 1, 0, 0]
opEQ, // [1, 1, 1]
opNEQ, // [1, 0]
},
[]int32{1, 0},
"",
},
{
"odd/even",
// Calculate odd(2+31/64), odd(2+32/64), even(2), even(1).
[]byte{
opPUSHB000, // [159]
159,
opODD, // [0]
opPUSHB000, // [0, 160]
160,
opODD, // [0, 1]
opPUSHB000, // [0, 1, 128]
128,
opEVEN, // [0, 1, 1]
opPUSHB000, // [0, 1, 1, 64]
64,
opEVEN, // [0, 1, 1, 0]
},
[]int32{0, 1, 1, 0},
"",
},
{
"if true",
[]byte{
opPUSHB001, // [255, 1]
255,
1,
opIF,
opPUSHB000, // [255, 2]
2,
opEIF,
opPUSHB000, // [255, 2, 254]
254,
},
[]int32{255, 2, 254},
"",
},
{
"if false",
[]byte{
opPUSHB001, // [255, 0]
255,
0,
opIF,
opPUSHB000, // [255]
2,
opEIF,
opPUSHB000, // [255, 254]
254,
},
[]int32{255, 254},
"",
},
{
"if/else true",
[]byte{
opPUSHB000, // [1]
1,
opIF,
opPUSHB000, // [2]
2,
opELSE,
opPUSHB000, // not executed
3,
opEIF,
},
[]int32{2},
"",
},
{
"if/else false",
[]byte{
opPUSHB000, // [0]
0,
opIF,
opPUSHB000, // not executed
2,
opELSE,
opPUSHB000, // [3]
3,
opEIF,
},
[]int32{3},
"",
},
{
"if/else true if/else false",
// 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
[]byte{
opPUSHB010, // [255, 0, 1]
255,
0,
1,
opIF,
opIF,
opPUSHB001, // not executed
0x58,
0x58,
opELSE,
opPUSHW000, // [255, 0x5858]
0x58,
0x58,
opEIF,
opELSE,
opIF,
opNPUSHB, // not executed
3,
0x58,
0x58,
0x58,
opELSE,
opNPUSHW, // not executed
2,
0x58,
0x58,
0x58,
0x58,
opEIF,
opEIF,
opPUSHB000, // [255, 0x5858, 254]
254,
},
[]int32{255, 0x5858, 254},
"",
},
{
"if/else false if/else true",
// 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
[]byte{
opPUSHB010, // [255, 1, 0]
255,
1,
0,
opIF,
opIF,
opPUSHB001, // not executed
0x58,
0x58,
opELSE,
opPUSHW000, // not executed
0x58,
0x58,
opEIF,
opELSE,
opIF,
opNPUSHB, // [255, 0x58, 0x58, 0x58]
3,
0x58,
0x58,
0x58,
opELSE,
opNPUSHW, // not executed
2,
0x58,
0x58,
0x58,
0x58,
opEIF,
opEIF,
opPUSHB000, // [255, 0x58, 0x58, 0x58, 254]
254,
},
[]int32{255, 0x58, 0x58, 0x58, 254},
"",
},
{
"logical ops",
[]byte{
opPUSHB010, // [0, 10, 20]
0,
10,
20,
opAND, // [0, 1]
opOR, // [1]
opNOT, // [0]
},
[]int32{0},
"",
},
{
"arithmetic ops",
// Calculate abs((-(1 - (2*3)))/2 + 1/64).
// The answer is 5/2 + 1/64 in ideal numbers, or 161 in 26.6 fixed point math.
[]byte{
opPUSHB010, // [64, 128, 192]
1 << 6,
2 << 6,
3 << 6,
opMUL, // [64, 384]
opSUB, // [-320]
opNEG, // [320]
opPUSHB000, // [320, 128]
2 << 6,
opDIV, // [160]
opPUSHB000, // [160, 1]
1,
opADD, // [161]
opABS, // [161]
},
[]int32{161},
"",
},
{
"floor, ceiling",
[]byte{
opPUSHB000, // [96]
96,
opFLOOR, // [64]
opPUSHB000, // [64, 96]
96,
opCEILING, // [64, 128]
},
[]int32{64, 128},
"",
},
{
"rounding",
// Round 1.40625 (which is 90/64) under various rounding policies.
// See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
[]byte{
opROFF, // []
opPUSHB000, // [90]
90,
opROUND00, // [90]
opRTG, // [90]
opPUSHB000, // [90, 90]
90,
opROUND00, // [90, 64]
opRTHG, // [90, 64]
opPUSHB000, // [90, 64, 90]
90,
opROUND00, // [90, 64, 96]
opRDTG, // [90, 64, 96]
opPUSHB000, // [90, 64, 96, 90]
90,
opROUND00, // [90, 64, 96, 64]
opRUTG, // [90, 64, 96, 64]
opPUSHB000, // [90, 64, 96, 64, 90]
90,
opROUND00, // [90, 64, 96, 64, 128]
opRTDG, // [90, 64, 96, 64, 128]
opPUSHB000, // [90, 64, 96, 64, 128, 90]
90,
opROUND00, // [90, 64, 96, 64, 128, 96]
},
[]int32{90, 64, 96, 64, 128, 96},
"",
},
{
"super-rounding",
// See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
// and the sign preservation steps of the "Order of rounding operations" section.
[]byte{
opPUSHB000, // [0x58]
0x58,
opSROUND, // []
opPUSHW000, // [-81]
0xff,
0xaf,
opROUND00, // [-80]
opPUSHW000, // [-80, -80]
0xff,
0xb0,
opROUND00, // [-80, -80]
opPUSHW000, // [-80, -80, -17]
0xff,
0xef,
opROUND00, // [-80, -80, -16]
opPUSHW000, // [-80, -80, -16, -16]
0xff,
0xf0,
opROUND00, // [-80, -80, -16, -16]
opPUSHB000, // [-80, -80, -16, -16, 0]
0,
opROUND00, // [-80, -80, -16, -16, 16]
opPUSHB000, // [-80, -80, -16, -16, 16, 16]
16,
opROUND00, // [-80, -80, -16, -16, 16, 16]
opPUSHB000, // [-80, -80, -16, -16, 16, 16, 47]
47,
opROUND00, // [-80, -80, -16, -16, 16, 16, 16]
opPUSHB000, // [-80, -80, -16, -16, 16, 16, 16, 48]
48,
opROUND00, // [-80, -80, -16, -16, 16, 16, 16, 80]
},
[]int32{-80, -80, -16, -16, 16, 16, 16, 80},
"",
},
{
"roll",
[]byte{
opPUSHB010, // [1, 2, 3]
1,
2,
3,
opROLL, // [2, 3, 1]
},
[]int32{2, 3, 1},
"",
},
{
"max/min",
[]byte{
opPUSHW001, // [-2, -3]
0xff,
0xfe,
0xff,
0xfd,
opMAX, // [-2]
opPUSHW001, // [-2, -4, -5]
0xff,
0xfc,
0xff,
0xfb,
opMIN, // [-2, -5]
},
[]int32{-2, -5},
"",
},
{
"functions",
[]byte{
opPUSHB011, // [3, 7, 0, 3]
3,
7,
0,
3,
opFDEF, // Function #3 (not called)
opPUSHB000,
98,
opENDF,
opFDEF, // Function #0
opDUP,
opADD,
opENDF,
opFDEF, // Function #7
opPUSHB001,
10,
0,
opCALL,
opDUP,
opENDF,
opFDEF, // Function #3 (again)
opPUSHB000,
99,
opENDF,
opPUSHB001, // [2, 0]
2,
0,
opCALL, // [4]
opPUSHB000, // [4, 3]
3,
opLOOPCALL, // [99, 99, 99, 99]
opPUSHB000, // [99, 99, 99, 99, 7]
7,
opCALL, // [99, 99, 99, 99, 20, 20]
},
[]int32{99, 99, 99, 99, 20, 20},
"",
},
}
for _, tc := range testCases {
h := &hinter{}
h.init(&Font{
maxStorage: 32,
maxStackElements: 100,
}, 768)
err, errStr := h.run(tc.prog, nil, nil, nil, nil), ""
if err != nil {
errStr = err.Error()
}
if tc.errStr != "" {
if errStr == "" {
t.Errorf("%s: got no error, want %q", tc.desc, tc.errStr)
} else if !strings.Contains(errStr, tc.errStr) {
t.Errorf("%s: got error %q, want one containing %q", tc.desc, errStr, tc.errStr)
}
continue
}
if errStr != "" {
t.Errorf("%s: got error %q, want none", tc.desc, errStr)
continue
}
got := h.stack[:len(tc.want)]
if !reflect.DeepEqual(got, tc.want) {
t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want)
continue
}
}
}
// TestMove tests that the hinter.move method matches the output of the C
// Freetype implementation.
func TestMove(t *testing.T) {
h, p := hinter{}, Point{}
testCases := []struct {
pvX, pvY, fvX, fvY f2dot14
wantX, wantY int32
}{
{+0x4000, +0x0000, +0x4000, +0x0000, +1000, +0},
{+0x4000, +0x0000, -0x4000, +0x0000, +1000, +0},
{-0x4000, +0x0000, +0x4000, +0x0000, -1000, +0},
{-0x4000, +0x0000, -0x4000, +0x0000, -1000, +0},
{+0x0000, +0x4000, +0x0000, +0x4000, +0, +1000},
{+0x0000, +0x4000, +0x0000, -0x4000, +0, +1000},
{+0x4000, +0x0000, +0x2d41, +0x2d41, +1000, +1000},
{+0x4000, +0x0000, -0x2d41, +0x2d41, +1000, -1000},
{+0x4000, +0x0000, +0x2d41, -0x2d41, +1000, -1000},
{+0x4000, +0x0000, -0x2d41, -0x2d41, +1000, +1000},
{-0x4000, +0x0000, +0x2d41, +0x2d41, -1000, -1000},
{-0x4000, +0x0000, -0x2d41, +0x2d41, -1000, +1000},
{-0x4000, +0x0000, +0x2d41, -0x2d41, -1000, +1000},
{-0x4000, +0x0000, -0x2d41, -0x2d41, -1000, -1000},
{+0x376d, +0x2000, +0x2d41, +0x2d41, +732, +732},
{-0x376d, +0x2000, +0x2d41, +0x2d41, -2732, -2732},
{+0x376d, +0x2000, +0x2d41, -0x2d41, +2732, -2732},
{-0x376d, +0x2000, +0x2d41, -0x2d41, -732, +732},
{-0x376d, -0x2000, +0x2d41, +0x2d41, -732, -732},
{+0x376d, +0x2000, +0x4000, +0x0000, +1155, +0},
{+0x376d, +0x2000, +0x0000, +0x4000, +0, +2000},
}
for _, tc := range testCases {
p = Point{}
h.gs.pv = [2]f2dot14{tc.pvX, tc.pvY}
h.gs.fv = [2]f2dot14{tc.fvX, tc.fvY}
h.move(&p, 1000, true)
tx := p.Flags&flagTouchedX != 0
ty := p.Flags&flagTouchedY != 0
wantTX := tc.fvX != 0
wantTY := tc.fvY != 0
if p.X != tc.wantX || p.Y != tc.wantY || tx != wantTX || ty != wantTY {
t.Errorf("pv=%v, fv=%v\ngot %d, %d, %t, %t\nwant %d, %d, %t, %t",
h.gs.pv, h.gs.fv, p.X, p.Y, tx, ty, tc.wantX, tc.wantY, wantTX, wantTY)
continue
}
// Check that p is aligned with the freedom vector.
a := int64(p.X) * int64(tc.fvY)
b := int64(p.Y) * int64(tc.fvX)
if a != b {
t.Errorf("pv=%v, fv=%v, p=%v not aligned with fv", h.gs.pv, h.gs.fv, p)
continue
}
// Check that the projected p is 1000 away from the origin.
dotProd := (int64(p.X)*int64(tc.pvX) + int64(p.Y)*int64(tc.pvY) + 1<<13) >> 14
if dotProd != 1000 {
t.Errorf("pv=%v, fv=%v, p=%v not 1000 from origin", h.gs.pv, h.gs.fv, p)
continue
}
}
}
// TestNormalize tests that the normalize function matches the output of the C
// Freetype implementation.
func TestNormalize(t *testing.T) {
testCases := [][2]f2dot14{
{-15895, 3974},
{-15543, 5181},
{-14654, 7327},
{-11585, 11585},
{0, 16384},
{11585, 11585},
{14654, 7327},
{15543, 5181},
{15895, 3974},
{16066, 3213},
{16161, 2694},
{16219, 2317},
{16257, 2032},
{16284, 1809},
}
for i, want := range testCases {
got := normalize(f2dot14(i)-4, 1)
if got != want {
t.Errorf("i=%d: got %v, want %v", i, got, want)
}
}
}

View File

@@ -1,289 +0,0 @@
// Copyright 2012 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package truetype
// The Truetype opcodes are summarized at
// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
const (
opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
opSVTCA1 = 0x01 // .
opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
opSPVTCA1 = 0x03 // .
opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
opSFVTCA1 = 0x05 // .
opSPVTL0 = 0x06 // Set Projection Vector To Line
opSPVTL1 = 0x07 // .
opSFVTL0 = 0x08 // Set Freedom Vector To Line
opSFVTL1 = 0x09 // .
opSPVFS = 0x0a // Set Projection Vector From Stack
opSFVFS = 0x0b // Set Freedom Vector From Stack
opGPV = 0x0c // Get Projection Vector
opGFV = 0x0d // Get Freedom Vector
opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
opISECT = 0x0f // moves point p to the InterSECTion of two lines
opSRP0 = 0x10 // Set Reference Point 0
opSRP1 = 0x11 // Set Reference Point 1
opSRP2 = 0x12 // Set Reference Point 2
opSZP0 = 0x13 // Set Zone Pointer 0
opSZP1 = 0x14 // Set Zone Pointer 1
opSZP2 = 0x15 // Set Zone Pointer 2
opSZPS = 0x16 // Set Zone PointerS
opSLOOP = 0x17 // Set LOOP variable
opRTG = 0x18 // Round To Grid
opRTHG = 0x19 // Round To Half Grid
opSMD = 0x1a // Set Minimum Distance
opELSE = 0x1b // ELSE clause
opJMPR = 0x1c // JuMP Relative
opSCVTCI = 0x1d // Set Control Value Table Cut-In
opSSWCI = 0x1e // Set Single Width Cut-In
opSSW = 0x1f // Set Single Width
opDUP = 0x20 // DUPlicate top stack element
opPOP = 0x21 // POP top stack element
opCLEAR = 0x22 // CLEAR the stack
opSWAP = 0x23 // SWAP the top two elements on the stack
opDEPTH = 0x24 // DEPTH of the stack
opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
opALIGNPTS = 0x27 // ALIGN PoinTS
op_0x28 = 0x28 // deprecated
opUTP = 0x29 // UnTouch Point
opLOOPCALL = 0x2a // LOOP and CALL function
opCALL = 0x2b // CALL function
opFDEF = 0x2c // Function DEFinition
opENDF = 0x2d // END Function definition
opMDAP0 = 0x2e // Move Direct Absolute Point
opMDAP1 = 0x2f // .
opIUP0 = 0x30 // Interpolate Untouched Points through the outline
opIUP1 = 0x31 // .
opSHP0 = 0x32 // SHift Point using reference point
opSHP1 = 0x33 // .
opSHC0 = 0x34 // SHift Contour using reference point
opSHC1 = 0x35 // .
opSHZ0 = 0x36 // SHift Zone using reference point
opSHZ1 = 0x37 // .
opSHPIX = 0x38 // SHift point by a PIXel amount
opIP = 0x39 // Interpolate Point
opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
opMSIRP1 = 0x3b // .
opALIGNRP = 0x3c // ALIGN to Reference Point
opRTDG = 0x3d // Round To Double Grid
opMIAP0 = 0x3e // Move Indirect Absolute Point
opMIAP1 = 0x3f // .
opNPUSHB = 0x40 // PUSH N Bytes
opNPUSHW = 0x41 // PUSH N Words
opWS = 0x42 // Write Store
opRS = 0x43 // Read Store
opWCVTP = 0x44 // Write Control Value Table in Pixel units
opRCVT = 0x45 // Read Control Value Table entry
opGC0 = 0x46 // Get Coordinate projected onto the projection vector
opGC1 = 0x47 // .
opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
opMD0 = 0x49 // Measure Distance
opMD1 = 0x4a // .
opMPPEM = 0x4b // Measure Pixels Per EM
opMPS = 0x4c // Measure Point Size
opFLIPON = 0x4d // set the auto FLIP Boolean to ON
opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
opDEBUG = 0x4f // DEBUG call
opLT = 0x50 // Less Than
opLTEQ = 0x51 // Less Than or EQual
opGT = 0x52 // Greater Than
opGTEQ = 0x53 // Greater Than or EQual
opEQ = 0x54 // EQual
opNEQ = 0x55 // Not EQual
opODD = 0x56 // ODD
opEVEN = 0x57 // EVEN
opIF = 0x58 // IF test
opEIF = 0x59 // End IF
opAND = 0x5a // logical AND
opOR = 0x5b // logical OR
opNOT = 0x5c // logical NOT
opDELTAP1 = 0x5d // DELTA exception P1
opSDB = 0x5e // Set Delta Base in the graphics state
opSDS = 0x5f // Set Delta Shift in the graphics state
opADD = 0x60 // ADD
opSUB = 0x61 // SUBtract
opDIV = 0x62 // DIVide
opMUL = 0x63 // MULtiply
opABS = 0x64 // ABSolute value
opNEG = 0x65 // NEGate
opFLOOR = 0x66 // FLOOR
opCEILING = 0x67 // CEILING
opROUND00 = 0x68 // ROUND value
opROUND01 = 0x69 // .
opROUND10 = 0x6a // .
opROUND11 = 0x6b // .
opNROUND00 = 0x6c // No ROUNDing of value
opNROUND01 = 0x6d // .
opNROUND10 = 0x6e // .
opNROUND11 = 0x6f // .
opWCVTF = 0x70 // Write Control Value Table in Funits
opDELTAP2 = 0x71 // DELTA exception P2
opDELTAP3 = 0x72 // DELTA exception P3
opDELTAC1 = 0x73 // DELTA exception C1
opDELTAC2 = 0x74 // DELTA exception C2
opDELTAC3 = 0x75 // DELTA exception C3
opSROUND = 0x76 // Super ROUND
opS45ROUND = 0x77 // Super ROUND 45 degrees
opJROT = 0x78 // Jump Relative On True
opJROF = 0x79 // Jump Relative On False
opROFF = 0x7a // Round OFF
op_0x7b = 0x7b // deprecated
opRUTG = 0x7c // Round Up To Grid
opRDTG = 0x7d // Round Down To Grid
opSANGW = 0x7e // Set ANGle Weight
opAA = 0x7f // Adjust Angle
opFLIPPT = 0x80 // FLIP PoinT
opFLIPRGON = 0x81 // FLIP RanGe ON
opFLIPRGOFF = 0x82 // FLIP RanGe OFF
op_0x83 = 0x83 // deprecated
op_0x84 = 0x84 // deprecated
opSCANCTRL = 0x85 // SCAN conversion ConTRoL
opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
opSDPVTL1 = 0x87 // .
opGETINFO = 0x88 // GET INFOrmation
opIDEF = 0x89 // Instruction DEFinition
opROLL = 0x8a // ROLL the top three stack elements
opMAX = 0x8b // MAXimum of top two stack elements
opMIN = 0x8c // MINimum of top two stack elements
opSCANTYPE = 0x8d // SCANTYPE
opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
op_0x8f = 0x8f
op_0x90 = 0x90
op_0x91 = 0x91
op_0x92 = 0x92
op_0x93 = 0x93
op_0x94 = 0x94
op_0x95 = 0x95
op_0x96 = 0x96
op_0x97 = 0x97
op_0x98 = 0x98
op_0x99 = 0x99
op_0x9a = 0x9a
op_0x9b = 0x9b
op_0x9c = 0x9c
op_0x9d = 0x9d
op_0x9e = 0x9e
op_0x9f = 0x9f
op_0xa0 = 0xa0
op_0xa1 = 0xa1
op_0xa2 = 0xa2
op_0xa3 = 0xa3
op_0xa4 = 0xa4
op_0xa5 = 0xa5
op_0xa6 = 0xa6
op_0xa7 = 0xa7
op_0xa8 = 0xa8
op_0xa9 = 0xa9
op_0xaa = 0xaa
op_0xab = 0xab
op_0xac = 0xac
op_0xad = 0xad
op_0xae = 0xae
op_0xaf = 0xaf
opPUSHB000 = 0xb0 // PUSH Bytes
opPUSHB001 = 0xb1 // .
opPUSHB010 = 0xb2 // .
opPUSHB011 = 0xb3 // .
opPUSHB100 = 0xb4 // .
opPUSHB101 = 0xb5 // .
opPUSHB110 = 0xb6 // .
opPUSHB111 = 0xb7 // .
opPUSHW000 = 0xb8 // PUSH Words
opPUSHW001 = 0xb9 // .
opPUSHW010 = 0xba // .
opPUSHW011 = 0xbb // .
opPUSHW100 = 0xbc // .
opPUSHW101 = 0xbd // .
opPUSHW110 = 0xbe // .
opPUSHW111 = 0xbf // .
opMDRP00000 = 0xc0 // Move Direct Relative Point
opMDRP00001 = 0xc1 // .
opMDRP00010 = 0xc2 // .
opMDRP00011 = 0xc3 // .
opMDRP00100 = 0xc4 // .
opMDRP00101 = 0xc5 // .
opMDRP00110 = 0xc6 // .
opMDRP00111 = 0xc7 // .
opMDRP01000 = 0xc8 // .
opMDRP01001 = 0xc9 // .
opMDRP01010 = 0xca // .
opMDRP01011 = 0xcb // .
opMDRP01100 = 0xcc // .
opMDRP01101 = 0xcd // .
opMDRP01110 = 0xce // .
opMDRP01111 = 0xcf // .
opMDRP10000 = 0xd0 // .
opMDRP10001 = 0xd1 // .
opMDRP10010 = 0xd2 // .
opMDRP10011 = 0xd3 // .
opMDRP10100 = 0xd4 // .
opMDRP10101 = 0xd5 // .
opMDRP10110 = 0xd6 // .
opMDRP10111 = 0xd7 // .
opMDRP11000 = 0xd8 // .
opMDRP11001 = 0xd9 // .
opMDRP11010 = 0xda // .
opMDRP11011 = 0xdb // .
opMDRP11100 = 0xdc // .
opMDRP11101 = 0xdd // .
opMDRP11110 = 0xde // .
opMDRP11111 = 0xdf // .
opMIRP00000 = 0xe0 // Move Indirect Relative Point
opMIRP00001 = 0xe1 // .
opMIRP00010 = 0xe2 // .
opMIRP00011 = 0xe3 // .
opMIRP00100 = 0xe4 // .
opMIRP00101 = 0xe5 // .
opMIRP00110 = 0xe6 // .
opMIRP00111 = 0xe7 // .
opMIRP01000 = 0xe8 // .
opMIRP01001 = 0xe9 // .
opMIRP01010 = 0xea // .
opMIRP01011 = 0xeb // .
opMIRP01100 = 0xec // .
opMIRP01101 = 0xed // .
opMIRP01110 = 0xee // .
opMIRP01111 = 0xef // .
opMIRP10000 = 0xf0 // .
opMIRP10001 = 0xf1 // .
opMIRP10010 = 0xf2 // .
opMIRP10011 = 0xf3 // .
opMIRP10100 = 0xf4 // .
opMIRP10101 = 0xf5 // .
opMIRP10110 = 0xf6 // .
opMIRP10111 = 0xf7 // .
opMIRP11000 = 0xf8 // .
opMIRP11001 = 0xf9 // .
opMIRP11010 = 0xfa // .
opMIRP11011 = 0xfb // .
opMIRP11100 = 0xfc // .
opMIRP11101 = 0xfd // .
opMIRP11110 = 0xfe // .
opMIRP11111 = 0xff // .
)
// popCount is the number of stack elements that each opcode pops.
var popCount = [256]uint8{
// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 0x00 - 0x0f
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
1, 1, 0, 2, 0, 1, 1, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
}

View File

@@ -1,554 +0,0 @@
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
// Package truetype provides a parser for the TTF and TTC file formats.
// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
// and http://www.microsoft.com/typography/otspec/
//
// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
// metrics and control points. All these methods take a scale parameter, which
// is the number of device units in 1 em. For example, if 1 em is 10 pixels and
// 1 pixel is 64 units, then scale is 640. If the device space involves pixels,
// 64 units per pixel is recommended, since that is what the bytecode hinter
// uses when snapping point co-ordinates to the pixel grid.
//
// To measure a TrueType font in ideal FUnit space, use scale equal to
// font.FUnitsPerEm().
package truetype
import (
"fmt"
)
// An Index is a Font's index of a rune.
type Index uint16
// A Bounds holds the co-ordinate range of one or more glyphs.
// The endpoints are inclusive.
type Bounds struct {
XMin, YMin, XMax, YMax int32
}
// An HMetric holds the horizontal metrics of a single glyph.
type HMetric struct {
AdvanceWidth, LeftSideBearing int32
}
// A VMetric holds the vertical metrics of a single glyph.
type VMetric struct {
AdvanceHeight, TopSideBearing int32
}
// A FormatError reports that the input is not a valid TrueType font.
type FormatError string
func (e FormatError) Error() string {
return "freetype: invalid TrueType format: " + string(e)
}
// An UnsupportedError reports that the input uses a valid but unimplemented
// TrueType feature.
type UnsupportedError string
func (e UnsupportedError) Error() string {
return "freetype: unsupported TrueType feature: " + string(e)
}
// u32 returns the big-endian uint32 at b[i:].
func u32(b []byte, i int) uint32 {
return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
}
// u16 returns the big-endian uint16 at b[i:].
func u16(b []byte, i int) uint16 {
return uint16(b[i])<<8 | uint16(b[i+1])
}
// readTable returns a slice of the TTF data given by a table's directory entry.
func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
offset := int(u32(offsetLength, 0))
if offset < 0 {
return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
}
length := int(u32(offsetLength, 4))
if length < 0 {
return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
}
end := offset + length
if end < 0 || end > len(ttf) {
return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
}
return ttf[offset:end], nil
}
const (
locaOffsetFormatUnknown int = iota
locaOffsetFormatShort
locaOffsetFormatLong
)
// A cm holds a parsed cmap entry.
type cm struct {
start, end, delta, offset uint32
}
// A Font represents a Truetype font.
type Font struct {
// Tables sliced from the TTF data. The different tables are documented
// at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, os2, prep, vmtx []byte
cmapIndexes []byte
// Cached values derived from the raw ttf data.
cm []cm
locaOffsetFormat int
nGlyph, nHMetric, nKern int
fUnitsPerEm int32
bounds Bounds
// Values from the maxp section.
maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
}
func (f *Font) parseCmap() error {
const (
cmapFormat4 = 4
cmapFormat12 = 12
languageIndependent = 0
// A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
// least-significant 16-bit Platform Specific ID. The magic numbers are
// specified at https://www.microsoft.com/typography/otspec/name.htm
unicodeEncoding = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0)
microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
)
if len(f.cmap) < 4 {
return FormatError("cmap too short")
}
nsubtab := int(u16(f.cmap, 2))
if len(f.cmap) < 8*nsubtab+4 {
return FormatError("cmap too short")
}
offset, found, x := 0, false, 4
for i := 0; i < nsubtab; i++ {
// We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
// All values are big-endian.
pidPsid, o := u32(f.cmap, x), u32(f.cmap, x+4)
x += 8
// We prefer the Unicode cmap encoding. Failing to find that, we fall
// back onto the Microsoft cmap encoding.
if pidPsid == unicodeEncoding {
offset, found = int(o), true
break
} else if pidPsid == microsoftSymbolEncoding ||
pidPsid == microsoftUCS2Encoding ||
pidPsid == microsoftUCS4Encoding {
offset, found = int(o), true
// We don't break out of the for loop, so that Unicode can override Microsoft.
}
}
if !found {
return UnsupportedError("cmap encoding")
}
if offset <= 0 || offset > len(f.cmap) {
return FormatError("bad cmap offset")
}
cmapFormat := u16(f.cmap, offset)
switch cmapFormat {
case cmapFormat4:
language := u16(f.cmap, offset+4)
if language != languageIndependent {
return UnsupportedError(fmt.Sprintf("language: %d", language))
}
segCountX2 := int(u16(f.cmap, offset+6))
if segCountX2%2 == 1 {
return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
}
segCount := segCountX2 / 2
offset += 14
f.cm = make([]cm, segCount)
for i := 0; i < segCount; i++ {
f.cm[i].end = uint32(u16(f.cmap, offset))
offset += 2
}
offset += 2
for i := 0; i < segCount; i++ {
f.cm[i].start = uint32(u16(f.cmap, offset))
offset += 2
}
for i := 0; i < segCount; i++ {
f.cm[i].delta = uint32(u16(f.cmap, offset))
offset += 2
}
for i := 0; i < segCount; i++ {
f.cm[i].offset = uint32(u16(f.cmap, offset))
offset += 2
}
f.cmapIndexes = f.cmap[offset:]
return nil
case cmapFormat12:
if u16(f.cmap, offset+2) != 0 {
return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
}
length := u32(f.cmap, offset+4)
language := u32(f.cmap, offset+8)
if language != languageIndependent {
return UnsupportedError(fmt.Sprintf("language: %d", language))
}
nGroups := u32(f.cmap, offset+12)
if length != 12*nGroups+16 {
return FormatError("inconsistent cmap length")
}
offset += 16
f.cm = make([]cm, nGroups)
for i := uint32(0); i < nGroups; i++ {
f.cm[i].start = u32(f.cmap, offset+0)
f.cm[i].end = u32(f.cmap, offset+4)
f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
offset += 12
}
return nil
}
return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
}
func (f *Font) parseHead() error {
if len(f.head) != 54 {
return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
}
f.fUnitsPerEm = int32(u16(f.head, 18))
f.bounds.XMin = int32(int16(u16(f.head, 36)))
f.bounds.YMin = int32(int16(u16(f.head, 38)))
f.bounds.XMax = int32(int16(u16(f.head, 40)))
f.bounds.YMax = int32(int16(u16(f.head, 42)))
switch i := u16(f.head, 50); i {
case 0:
f.locaOffsetFormat = locaOffsetFormatShort
case 1:
f.locaOffsetFormat = locaOffsetFormatLong
default:
return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
}
return nil
}
func (f *Font) parseHhea() error {
if len(f.hhea) != 36 {
return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
}
f.nHMetric = int(u16(f.hhea, 34))
if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
}
return nil
}
func (f *Font) parseKern() error {
// Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
// "Previous versions of the 'kern' table defined both the version and nTables fields in the header
// as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
// (although AAT can sense an old kerning table and still make correct use of it). Microsoft
// Windows still uses the older format for the 'kern' table and will not recognize the newer one.
// Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
// and Windows should use the old format."
// Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
// just like the C Freetype implementation.
if len(f.kern) == 0 {
if f.nKern != 0 {
return FormatError("bad kern table length")
}
return nil
}
if len(f.kern) < 18 {
return FormatError("kern data too short")
}
version, offset := u16(f.kern, 0), 2
if version != 0 {
return UnsupportedError(fmt.Sprintf("kern version: %d", version))
}
n, offset := u16(f.kern, offset), offset+2
if n != 1 {
return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
}
offset += 2
length, offset := int(u16(f.kern, offset)), offset+2
coverage, offset := u16(f.kern, offset), offset+2
if coverage != 0x0001 {
// We only support horizontal kerning.
return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
}
f.nKern, offset = int(u16(f.kern, offset)), offset+2
if 6*f.nKern != length-14 {
return FormatError("bad kern table length")
}
return nil
}
func (f *Font) parseMaxp() error {
if len(f.maxp) != 32 {
return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
}
f.nGlyph = int(u16(f.maxp, 4))
f.maxTwilightPoints = u16(f.maxp, 16)
f.maxStorage = u16(f.maxp, 18)
f.maxFunctionDefs = u16(f.maxp, 20)
f.maxStackElements = u16(f.maxp, 24)
return nil
}
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
func (f *Font) scale(x int32) int32 {
if x >= 0 {
x += f.fUnitsPerEm / 2
} else {
x -= f.fUnitsPerEm / 2
}
return x / f.fUnitsPerEm
}
// Bounds returns the union of a Font's glyphs' bounds.
func (f *Font) Bounds(scale int32) Bounds {
b := f.bounds
b.XMin = f.scale(scale * b.XMin)
b.YMin = f.scale(scale * b.YMin)
b.XMax = f.scale(scale * b.XMax)
b.YMax = f.scale(scale * b.YMax)
return b
}
// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
func (f *Font) FUnitsPerEm() int32 {
return f.fUnitsPerEm
}
// Index returns a Font's index for the given rune.
func (f *Font) Index(x rune) Index {
c := uint32(x)
for i, j := 0, len(f.cm); i < j; {
h := i + (j-i)/2
cm := &f.cm[h]
if c < cm.start {
j = h
} else if cm.end < c {
i = h + 1
} else if cm.offset == 0 {
return Index(c + cm.delta)
} else {
offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
return Index(u16(f.cmapIndexes, offset))
}
}
return 0
}
// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
// the given index.
func (f *Font) unscaledHMetric(i Index) (h HMetric) {
j := int(i)
if j < 0 || f.nGlyph <= j {
return HMetric{}
}
if j >= f.nHMetric {
p := 4 * (f.nHMetric - 1)
return HMetric{
AdvanceWidth: int32(u16(f.hmtx, p)),
LeftSideBearing: int32(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
}
}
return HMetric{
AdvanceWidth: int32(u16(f.hmtx, 4*j)),
LeftSideBearing: int32(int16(u16(f.hmtx, 4*j+2))),
}
}
// HMetric returns the horizontal metrics for the glyph with the given index.
func (f *Font) HMetric(scale int32, i Index) HMetric {
h := f.unscaledHMetric(i)
h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
return h
}
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
// the given index. yMax is the top of the glyph's bounding box.
func (f *Font) unscaledVMetric(i Index, yMax int32) (v VMetric) {
j := int(i)
if j < 0 || f.nGlyph <= j {
return VMetric{}
}
if 4*j+4 <= len(f.vmtx) {
return VMetric{
AdvanceHeight: int32(u16(f.vmtx, 4*j)),
TopSideBearing: int32(int16(u16(f.vmtx, 4*j+2))),
}
}
// The OS/2 table has grown over time.
// https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
// says that it was originally 68 bytes. Optional fields, including
// the ascender and descender, are described at
// http://www.microsoft.com/typography/otspec/os2.htm
if len(f.os2) >= 72 {
sTypoAscender := int32(int16(u16(f.os2, 68)))
sTypoDescender := int32(int16(u16(f.os2, 70)))
return VMetric{
AdvanceHeight: sTypoAscender - sTypoDescender,
TopSideBearing: sTypoAscender - yMax,
}
}
return VMetric{
AdvanceHeight: f.fUnitsPerEm,
TopSideBearing: 0,
}
}
// VMetric returns the vertical metrics for the glyph with the given index.
func (f *Font) VMetric(scale int32, i Index) VMetric {
// TODO: should 0 be bounds.YMax?
v := f.unscaledVMetric(i, 0)
v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
v.TopSideBearing = f.scale(scale * v.TopSideBearing)
return v
}
// Kerning returns the kerning for the given glyph pair.
func (f *Font) Kerning(scale int32, i0, i1 Index) int32 {
if f.nKern == 0 {
return 0
}
g := uint32(i0)<<16 | uint32(i1)
lo, hi := 0, f.nKern
for lo < hi {
i := (lo + hi) / 2
ig := u32(f.kern, 18+6*i)
if ig < g {
lo = i + 1
} else if ig > g {
hi = i
} else {
return f.scale(scale * int32(int16(u16(f.kern, 22+6*i))))
}
}
return 0
}
// Parse returns a new Font for the given TTF or TTC data.
//
// For TrueType Collections, the first font in the collection is parsed.
func Parse(ttf []byte) (font *Font, err error) {
return parse(ttf, 0)
}
func parse(ttf []byte, offset int) (font *Font, err error) {
if len(ttf)-offset < 12 {
err = FormatError("TTF data is too short")
return
}
originalOffset := offset
magic, offset := u32(ttf, offset), offset+4
switch magic {
case 0x00010000:
// No-op.
case 0x74746366: // "ttcf" as a big-endian uint32.
if originalOffset != 0 {
err = FormatError("recursive TTC")
return
}
ttcVersion, offset := u32(ttf, offset), offset+4
if ttcVersion != 0x00010000 {
// TODO: support TTC version 2.0, once I have such a .ttc file to test with.
err = FormatError("bad TTC version")
return
}
numFonts, offset := int(u32(ttf, offset)), offset+4
if numFonts <= 0 {
err = FormatError("bad number of TTC fonts")
return
}
if len(ttf[offset:])/4 < numFonts {
err = FormatError("TTC offset table is too short")
return
}
// TODO: provide an API to select which font in a TrueType collection to return,
// not just the first one. This may require an API to parse a TTC's name tables,
// so users of this package can select the font in a TTC by name.
offset = int(u32(ttf, offset))
if offset <= 0 || offset > len(ttf) {
err = FormatError("bad TTC offset")
return
}
return parse(ttf, offset)
default:
err = FormatError("bad TTF version")
return
}
n, offset := int(u16(ttf, offset)), offset+2
if len(ttf) < 16*n+12 {
err = FormatError("TTF data is too short")
return
}
f := new(Font)
// Assign the table slices.
for i := 0; i < n; i++ {
x := 16*i + 12
switch string(ttf[x : x+4]) {
case "cmap":
f.cmap, err = readTable(ttf, ttf[x+8:x+16])
case "cvt ":
f.cvt, err = readTable(ttf, ttf[x+8:x+16])
case "fpgm":
f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
case "glyf":
f.glyf, err = readTable(ttf, ttf[x+8:x+16])
case "hdmx":
f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
case "head":
f.head, err = readTable(ttf, ttf[x+8:x+16])
case "hhea":
f.hhea, err = readTable(ttf, ttf[x+8:x+16])
case "hmtx":
f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
case "kern":
f.kern, err = readTable(ttf, ttf[x+8:x+16])
case "loca":
f.loca, err = readTable(ttf, ttf[x+8:x+16])
case "maxp":
f.maxp, err = readTable(ttf, ttf[x+8:x+16])
case "OS/2":
f.os2, err = readTable(ttf, ttf[x+8:x+16])
case "prep":
f.prep, err = readTable(ttf, ttf[x+8:x+16])
case "vmtx":
f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
}
if err != nil {
return
}
}
// Parse and sanity-check the TTF data.
if err = f.parseHead(); err != nil {
return
}
if err = f.parseMaxp(); err != nil {
return
}
if err = f.parseCmap(); err != nil {
return
}
if err = f.parseKern(); err != nil {
return
}
if err = f.parseHhea(); err != nil {
return
}
font = f
return
}

View File

@@ -1,366 +0,0 @@
// Copyright 2012 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
package truetype
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
"strconv"
"strings"
"testing"
)
func parseTestdataFont(name string) (font *Font, testdataIsOptional bool, err error) {
b, err := ioutil.ReadFile(fmt.Sprintf("../../testdata/%s.ttf", name))
if err != nil {
// The "x-foo" fonts are optional tests, as they are not checked
// in for copyright or file size reasons.
return nil, strings.HasPrefix(name, "x-"), fmt.Errorf("%s: ReadFile: %v", name, err)
}
font, err = Parse(b)
if err != nil {
return nil, true, fmt.Errorf("%s: Parse: %v", name, err)
}
return font, false, nil
}
// TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly.
// The numerical values can be manually verified by examining luxisr.ttx.
func TestParse(t *testing.T) {
font, _, err := parseTestdataFont("luxisr")
if err != nil {
t.Fatal(err)
}
if got, want := font.FUnitsPerEm(), int32(2048); got != want {
t.Errorf("FUnitsPerEm: got %v, want %v", got, want)
}
fupe := font.FUnitsPerEm()
if got, want := font.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want {
t.Errorf("Bounds: got %v, want %v", got, want)
}
i0 := font.Index('A')
i1 := font.Index('V')
if i0 != 36 || i1 != 57 {
t.Fatalf("Index: i0, i1 = %d, %d, want 36, 57", i0, i1)
}
if got, want := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
t.Errorf("HMetric: got %v, want %v", got, want)
}
if got, want := font.VMetric(fupe, i0), (VMetric{2465, 553}); got != want {
t.Errorf("VMetric: got %v, want %v", got, want)
}
if got, want := font.Kerning(fupe, i0, i1), int32(-144); got != want {
t.Errorf("Kerning: got %v, want %v", got, want)
}
g := NewGlyphBuf()
err = g.Load(font, fupe, i0, NoHinting)
if err != nil {
t.Fatalf("Load: %v", err)
}
g0 := &GlyphBuf{
B: g.B,
Point: g.Point,
End: g.End,
}
g1 := &GlyphBuf{
B: Bounds{19, 0, 1342, 1480},
Point: []Point{
{19, 0, 51},
{581, 1480, 1},
{789, 1480, 51},
{1342, 0, 1},
{1116, 0, 35},
{962, 410, 3},
{368, 410, 33},
{214, 0, 3},
{428, 566, 19},
{904, 566, 33},
{667, 1200, 3},
},
End: []int{8, 11},
}
if got, want := fmt.Sprint(g0), fmt.Sprint(g1); got != want {
t.Errorf("GlyphBuf:\ngot %v\nwant %v", got, want)
}
}
func TestIndex(t *testing.T) {
testCases := map[string]map[rune]Index{
"luxisr": {
' ': 3,
'!': 4,
'A': 36,
'V': 57,
'É': 101,
'fl': 193,
'\u22c5': 385,
'中': 0,
},
// The x-etc test cases use those versions of the .ttf files provided
// by Ubuntu 14.04. See testdata/make-other-hinting-txts.sh for details.
"x-arial-bold": {
' ': 3,
'+': 14,
'0': 19,
'_': 66,
'w': 90,
'~': 97,
'Ä': 98,
'fl': 192,
'½': 242,
'σ': 305,
'λ': 540,
'ỹ': 1275,
'\u04e9': 1319,
'中': 0,
},
"x-deja-vu-sans-oblique": {
' ': 3,
'*': 13,
'Œ': 276,
'ω': 861,
'‡': 2571,
'⊕': 3110,
'fl': 4728,
'\ufb03': 4729,
'\ufffd': 4813,
// TODO: '\U0001f640': ???,
'中': 0,
},
"x-droid-sans-japanese": {
' ': 0,
'\u3000': 3,
'\u3041': 25,
'\u30fe': 201,
'\uff61': 202,
'\uff67': 208,
'\uff9e': 263,
'\uff9f': 264,
'\u4e00': 265,
'\u557e': 1000,
'\u61b6': 2024,
'\u6ede': 3177,
'\u7505': 3555,
'\u81e3': 4602,
'\u81e5': 4603,
'\u81e7': 4604,
'\u81e8': 4605,
'\u81ea': 4606,
'\u81ed': 4607,
'\u81f3': 4608,
'\u81f4': 4609,
'\u91c7': 5796,
'\u9fa0': 6620,
'\u203e': 12584,
},
"x-times-new-roman": {
' ': 3,
':': 29,
'fl': 192,
'Ŀ': 273,
'♠': 388,
'Ŗ': 451,
'Σ': 520,
'\u200D': 745,
'Ẽ': 1216,
'\u04e9': 1319,
'中': 0,
},
}
for name, wants := range testCases {
font, testdataIsOptional, err := parseTestdataFont(name)
if err != nil {
if testdataIsOptional {
t.Log(err)
} else {
t.Fatal(err)
}
continue
}
for r, want := range wants {
if got := font.Index(r); got != want {
t.Errorf("%s: Index of %q, aka %U: got %d, want %d", name, r, r, got, want)
}
}
}
}
type scalingTestData struct {
advanceWidth int32
bounds Bounds
points []Point
}
// scalingTestParse parses a line of points like
// 213 -22 -111 236 555;-22 -111 1, 178 555 1, 236 555 1, 36 -111 1
// The line will not have a trailing "\n".
func scalingTestParse(line string) (ret scalingTestData) {
next := func(s string) (string, int32) {
t, i := "", strings.Index(s, " ")
if i != -1 {
s, t = s[:i], s[i+1:]
}
x, _ := strconv.Atoi(s)
return t, int32(x)
}
i := strings.Index(line, ";")
prefix, line := line[:i], line[i+1:]
prefix, ret.advanceWidth = next(prefix)
prefix, ret.bounds.XMin = next(prefix)
prefix, ret.bounds.YMin = next(prefix)
prefix, ret.bounds.XMax = next(prefix)
prefix, ret.bounds.YMax = next(prefix)
ret.points = make([]Point, 0, 1+strings.Count(line, ","))
for len(line) > 0 {
s := line
if i := strings.Index(line, ","); i != -1 {
s, line = line[:i], line[i+1:]
for len(line) > 0 && line[0] == ' ' {
line = line[1:]
}
} else {
line = ""
}
s, x := next(s)
s, y := next(s)
s, f := next(s)
ret.points = append(ret.points, Point{X: x, Y: y, Flags: uint32(f)})
}
return ret
}
// scalingTestEquals is equivalent to, but faster than, calling
// reflect.DeepEquals(a, b), and also returns the index of the first non-equal
// element. It also treats a nil []Point and an empty non-nil []Point as equal.
// a and b must have equal length.
func scalingTestEquals(a, b []Point) (index int, equals bool) {
for i, p := range a {
if p != b[i] {
return i, false
}
}
return 0, true
}
var scalingTestCases = []struct {
name string
size int32
}{
{"luxisr", 12},
{"x-arial-bold", 11},
{"x-deja-vu-sans-oblique", 17},
{"x-droid-sans-japanese", 9},
{"x-times-new-roman", 13},
}
func testScaling(t *testing.T, h Hinting) {
for _, tc := range scalingTestCases {
font, testdataIsOptional, err := parseTestdataFont(tc.name)
if err != nil {
if testdataIsOptional {
t.Log(err)
} else {
t.Error(err)
}
continue
}
hintingStr := "sans"
if h != NoHinting {
hintingStr = "with"
}
f, err := os.Open(fmt.Sprintf(
"../../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hintingStr))
if err != nil {
t.Errorf("%s: Open: %v", tc.name, err)
continue
}
defer f.Close()
wants := []scalingTestData{}
scanner := bufio.NewScanner(f)
if scanner.Scan() {
major, minor, patch := 0, 0, 0
_, err := fmt.Sscanf(scanner.Text(), "freetype version %d.%d.%d", &major, &minor, &patch)
if err != nil {
t.Errorf("%s: version information: %v", tc.name, err)
}
if (major < 2) || (major == 2 && minor < 5) || (major == 2 && minor == 5 && patch < 1) {
t.Errorf("%s: need freetype version >= 2.5.1.\n"+
"Try setting LD_LIBRARY_PATH=/path/to/freetype_built_from_src/objs/.libs/\n"+
"and re-running testdata/make-other-hinting-txts.sh",
tc.name)
continue
}
} else {
t.Errorf("%s: no version information", tc.name)
continue
}
for scanner.Scan() {
wants = append(wants, scalingTestParse(scanner.Text()))
}
if err := scanner.Err(); err != nil && err != io.EOF {
t.Errorf("%s: Scanner: %v", tc.name, err)
continue
}
glyphBuf := NewGlyphBuf()
for i, want := range wants {
if err = glyphBuf.Load(font, tc.size*64, Index(i), h); err != nil {
t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err)
continue
}
got := scalingTestData{
advanceWidth: glyphBuf.AdvanceWidth,
bounds: glyphBuf.B,
points: glyphBuf.Point,
}
if got.advanceWidth != want.advanceWidth {
t.Errorf("%s: glyph #%d advance width:\ngot %v\nwant %v",
tc.name, i, got.advanceWidth, want.advanceWidth)
continue
}
if got.bounds != want.bounds {
t.Errorf("%s: glyph #%d bounds:\ngot %v\nwant %v",
tc.name, i, got.bounds, want.bounds)
continue
}
for i := range got.points {
got.points[i].Flags &= 0x01
}
if len(got.points) != len(want.points) {
t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\ndifferent slice lengths: %d versus %d",
tc.name, i, got.points, want.points, len(got.points), len(want.points))
continue
}
if j, equals := scalingTestEquals(got.points, want.points); !equals {
t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\nat index %d: %v versus %v",
tc.name, i, got.points, want.points, j, got.points[j], want.points[j])
continue
}
}
}
}
func TestScalingSansHinting(t *testing.T) {
testScaling(t, NoHinting)
}
func TestScalingWithHinting(t *testing.T) {
testScaling(t, FullHinting)
}

View File

@@ -5,7 +5,6 @@ package api
import (
"bytes"
"code.google.com/p/draw2d/draw2d"
l4g "code.google.com/p/log4go"
"fmt"
"github.com/goamz/goamz/aws"
@@ -19,6 +18,7 @@ import (
"hash/fnv"
"image"
"image/color"
"image/draw"
_ "image/gif"
_ "image/jpeg"
"image/png"
@@ -602,42 +602,13 @@ func createProfileImage(username string, userId string) ([]byte, *model.AppError
h.Write([]byte(userId))
seed := h.Sum32()
initials := ""
parts := strings.Split(username, " ")
for _, v := range parts {
if len(v) > 0 {
initials += string(strings.ToUpper(v)[0])
}
}
if len(initials) == 0 {
initials = "^"
}
if len(initials) > 2 {
initials = initials[0:2]
}
draw2d.SetFontFolder(utils.FindDir("web/static/fonts"))
i := image.NewRGBA(image.Rect(0, 0, 128, 128))
gc := draw2d.NewGraphicContext(i)
draw2d.Rect(gc, 0, 0, 128, 128)
gc.SetFillColor(colors[int(seed)%len(colors)])
gc.Fill()
gc.SetFontSize(50)
gc.SetFontData(draw2d.FontData{"luxi", draw2d.FontFamilyMono, draw2d.FontStyleBold | draw2d.FontStyleItalic})
left, top, right, bottom := gc.GetStringBounds("CH")
width := (128 - (right - left + 10)) / 2
height := (128 - (top - bottom + 6)) / 2
gc.Translate(width, height)
gc.SetFillColor(image.White)
gc.FillString(initials)
color := colors[int(seed)%len(colors)]
img := image.NewRGBA(image.Rect(0, 0, int(utils.Cfg.ImageSettings.ProfileWidth), int(utils.Cfg.ImageSettings.ProfileHeight)))
draw.Draw(img, img.Bounds(), &image.Uniform{color}, image.ZP, draw.Src)
buf := new(bytes.Buffer)
if imgErr := png.Encode(buf, i); imgErr != nil {
if imgErr := png.Encode(buf, img); imgErr != nil {
return nil, model.NewAppError("getProfileImage", "Could not encode default profile image", imgErr.Error())
} else {
return buf.Bytes(), nil