mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
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:
15
Godeps/Godeps.json
generated
15
Godeps/Godeps.json
generated
@@ -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",
|
||||
|
||||
42
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go
generated
vendored
42
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go
generated
vendored
@@ -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()
|
||||
}
|
||||
67
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go
generated
vendored
67
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go
generated
vendored
@@ -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)}
|
||||
}
|
||||
11
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile
generated
vendored
11
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile
generated
vendored
@@ -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
|
||||
36
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go
generated
vendored
36
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go
generated
vendored
@@ -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)
|
||||
}
|
||||
67
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go
generated
vendored
67
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go
generated
vendored
@@ -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++
|
||||
}
|
||||
}
|
||||
}
|
||||
696
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go
generated
vendored
696
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go
generated
vendored
@@ -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
|
||||
}
|
||||
262
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go
generated
vendored
262
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go
generated
vendored
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go
generated
vendored
51
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go
generated
vendored
@@ -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++
|
||||
}
|
||||
}
|
||||
}
|
||||
336
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go
generated
vendored
336
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go
generated
vendored
@@ -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)
|
||||
|
||||
}
|
||||
90
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go
generated
vendored
90
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go
generated
vendored
@@ -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
|
||||
}
|
||||
23
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go
generated
vendored
23
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go
generated
vendored
@@ -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)
|
||||
}
|
||||
}
|
||||
5
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go
generated
vendored
5
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go
generated
vendored
@@ -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
|
||||
97
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go
generated
vendored
97
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go
generated
vendored
@@ -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
|
||||
}
|
||||
55
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go
generated
vendored
55
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go
generated
vendored
@@ -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)
|
||||
}
|
||||
359
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go
generated
vendored
359
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go
generated
vendored
@@ -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
|
||||
}
|
||||
52
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go
generated
vendored
52
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go
generated
vendored
@@ -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
|
||||
}
|
||||
92
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go
generated
vendored
92
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go
generated
vendored
@@ -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}
|
||||
}
|
||||
*/
|
||||
27
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go
generated
vendored
27
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go
generated
vendored
@@ -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()
|
||||
}
|
||||
70
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go
generated
vendored
70
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go
generated
vendored
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
173
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go
generated
vendored
173
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go
generated
vendored
@@ -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
|
||||
}
|
||||
190
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go
generated
vendored
190
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go
generated
vendored
@@ -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
|
||||
}
|
||||
203
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go
generated
vendored
203
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go
generated
vendored
@@ -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
|
||||
}
|
||||
320
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go
generated
vendored
320
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go
generated
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
303
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go
generated
vendored
303
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go
generated
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
320
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go
generated
vendored
320
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go
generated
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go
generated
vendored
17
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go
generated
vendored
@@ -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
|
||||
)
|
||||
55
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go
generated
vendored
55
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go
generated
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
581
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go
generated
vendored
581
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go
generated
vendored
@@ -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
|
||||
}
|
||||
200
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go
generated
vendored
200
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go
generated
vendored
@@ -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)
|
||||
}
|
||||
}
|
||||
150
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go
generated
vendored
150
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go
generated
vendored
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
208
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go
generated
vendored
208
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go
generated
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
135
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go
generated
vendored
135
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go
generated
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
306
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go
generated
vendored
306
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go
generated
vendored
@@ -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)
|
||||
}
|
||||
19
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go
generated
vendored
19
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go
generated
vendored
@@ -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)
|
||||
}
|
||||
280
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go
generated
vendored
280
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go
generated
vendored
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
292
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go
generated
vendored
292
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go
generated
vendored
@@ -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
|
||||
}
|
||||
579
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go
generated
vendored
579
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go
generated
vendored
@@ -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
|
||||
}
|
||||
466
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go
generated
vendored
466
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go
generated
vendored
@@ -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
|
||||
// |x′y″-y′x″| / (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:])
|
||||
}
|
||||
530
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go
generated
vendored
530
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go
generated
vendored
@@ -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),
|
||||
}
|
||||
}
|
||||
1764
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go
generated
vendored
1764
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go
generated
vendored
File diff suppressed because it is too large
Load Diff
673
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go
generated
vendored
673
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go
generated
vendored
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
289
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go
generated
vendored
289
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go
generated
vendored
@@ -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
|
||||
}
|
||||
554
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go
generated
vendored
554
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go
generated
vendored
@@ -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
|
||||
}
|
||||
366
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go
generated
vendored
366
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go
generated
vendored
@@ -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)
|
||||
}
|
||||
39
api/user.go
39
api/user.go
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user