first commit
Этот коммит содержится в:
42
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go
сгенерированный
поставляемый
Обычный файл
42
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/advanced_path.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
67
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/arc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,67 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
11
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/Makefile
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,11 @@
|
||||
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
сгенерированный
поставляемый
Обычный файл
36
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/arc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,36 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
67
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,67 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
696
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/cubic_float64_others.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,696 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
262
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/curve_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,262 @@
|
||||
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
сгенерированный
поставляемый
Обычный файл
51
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curve/quad_float64.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,51 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
336
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/curves.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,336 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
90
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/dasher.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,90 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
23
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/demux_converter.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,23 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
5
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/doc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,5 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
97
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/font.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,97 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
55
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/gc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,55 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
359
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/image.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,359 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
52
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/math.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,52 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
92
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/paint.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,92 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
27
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,27 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
70
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_adder.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,70 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
173
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_converter.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,173 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
190
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/path_storage.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,190 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
203
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/coverage_table.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,203 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
320
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerAA.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,320 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
303
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV1/fillerAA.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,303 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
320
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fillerV2/fillerAA.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,320 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
17
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/fixed_point.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,17 @@
|
||||
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
сгенерированный
поставляемый
Обычный файл
55
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/line.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,55 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
581
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/polygon.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,581 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
200
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/raster/raster_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,200 @@
|
||||
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
сгенерированный
поставляемый
Обычный файл
150
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/rgba_interpolation.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,150 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
208
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stack_gc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,208 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
135
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/stroker.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,135 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
306
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/transform.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,306 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
19
Godeps/_workspace/src/code.google.com/p/draw2d/draw2d/vertex2d.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
280
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,280 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
292
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,292 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
579
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,579 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
466
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,466 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
530
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,530 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
1764
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
673
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go
сгенерированный
поставляемый
Обычный файл
673
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,673 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
289
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,289 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
554
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,554 @@
|
||||
// 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
сгенерированный
поставляемый
Обычный файл
366
Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,366 @@
|
||||
// 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)
|
||||
}
|
||||
27
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/LICENSE
сгенерированный
поставляемый
Обычный файл
27
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009,2014 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
84
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/dce.go
сгенерированный
поставляемый
Обычный файл
84
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/dce.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,84 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A Domain represents a Version 2 domain
|
||||
type Domain byte
|
||||
|
||||
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||
const (
|
||||
Person = Domain(0)
|
||||
Group = Domain(1)
|
||||
Org = Domain(2)
|
||||
)
|
||||
|
||||
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||
//
|
||||
// The domain should be one of Person, Group or Org.
|
||||
// On a POSIX system the id should be the users UID for the Person
|
||||
// domain and the users GID for the Group. The meaning of id for
|
||||
// the domain Org or on non-POSIX systems is site defined.
|
||||
//
|
||||
// For a given domain/id pair the same token may be returned for up to
|
||||
// 7 minutes and 10 seconds.
|
||||
func NewDCESecurity(domain Domain, id uint32) UUID {
|
||||
uuid := NewUUID()
|
||||
if uuid != nil {
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||
uuid[9] = byte(domain)
|
||||
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||
// domain with the id returned by os.Getuid.
|
||||
//
|
||||
// NewDCEPerson(Person, uint32(os.Getuid()))
|
||||
func NewDCEPerson() UUID {
|
||||
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
}
|
||||
|
||||
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||
// domain with the id returned by os.Getgid.
|
||||
//
|
||||
// NewDCEGroup(Group, uint32(os.Getgid()))
|
||||
func NewDCEGroup() UUID {
|
||||
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
// Domain returns the domain for a Version 2 UUID or false.
|
||||
func (uuid UUID) Domain() (Domain, bool) {
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
return 0, false
|
||||
}
|
||||
return Domain(uuid[9]), true
|
||||
}
|
||||
|
||||
// Id returns the id for a Version 2 UUID or false.
|
||||
func (uuid UUID) Id() (uint32, bool) {
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
return 0, false
|
||||
}
|
||||
return binary.BigEndian.Uint32(uuid[0:4]), true
|
||||
}
|
||||
|
||||
func (d Domain) String() string {
|
||||
switch d {
|
||||
case Person:
|
||||
return "Person"
|
||||
case Group:
|
||||
return "Group"
|
||||
case Org:
|
||||
return "Org"
|
||||
}
|
||||
return fmt.Sprintf("Domain%d", int(d))
|
||||
}
|
||||
8
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/doc.go
сгенерированный
поставляемый
Обычный файл
8
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/doc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,8 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The uuid package generates and inspects UUIDs.
|
||||
//
|
||||
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
|
||||
package uuid
|
||||
53
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/hash.go
сгенерированный
поставляемый
Обычный файл
53
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/hash.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,53 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Well known Name Space IDs and UUIDs
|
||||
var (
|
||||
NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||||
NIL = Parse("00000000-0000-0000-0000-000000000000")
|
||||
)
|
||||
|
||||
// NewHash returns a new UUID dervied from the hash of space concatenated with
|
||||
// data generated by h. The hash should be at least 16 byte in length. The
|
||||
// first 16 bytes of the hash are used to form the UUID. The version of the
|
||||
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
||||
// NewMD5 and NewSHA1.
|
||||
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||
h.Reset()
|
||||
h.Write(space)
|
||||
h.Write([]byte(data))
|
||||
s := h.Sum(nil)
|
||||
uuid := make([]byte, 16)
|
||||
copy(uuid, s)
|
||||
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
||||
// supplied name space and data.
|
||||
//
|
||||
// NewHash(md5.New(), space, data, 3)
|
||||
func NewMD5(space UUID, data []byte) UUID {
|
||||
return NewHash(md5.New(), space, data, 3)
|
||||
}
|
||||
|
||||
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
||||
// supplied name space and data.
|
||||
//
|
||||
// NewHash(sha1.New(), space, data, 5)
|
||||
func NewSHA1(space UUID, data []byte) UUID {
|
||||
return NewHash(sha1.New(), space, data, 5)
|
||||
}
|
||||
30
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/json.go
сгенерированный
поставляемый
Обычный файл
30
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/json.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,30 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "errors"
|
||||
|
||||
func (u UUID) MarshalJSON() ([]byte, error) {
|
||||
if len(u) == 0 {
|
||||
return []byte(`""`), nil
|
||||
}
|
||||
return []byte(`"` + u.String() + `"`), nil
|
||||
}
|
||||
|
||||
func (u *UUID) UnmarshalJSON(data []byte) error {
|
||||
if len(data) == 0 || string(data) == `""` {
|
||||
return nil
|
||||
}
|
||||
if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
|
||||
return errors.New("invalid UUID format")
|
||||
}
|
||||
data = data[1 : len(data)-1]
|
||||
uu := Parse(string(data))
|
||||
if uu == nil {
|
||||
return errors.New("invalid UUID format")
|
||||
}
|
||||
*u = uu
|
||||
return nil
|
||||
}
|
||||
32
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/json_test.go
сгенерированный
поставляемый
Обычный файл
32
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/json_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,32 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testUUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
|
||||
func TestJSON(t *testing.T) {
|
||||
type S struct {
|
||||
ID1 UUID
|
||||
ID2 UUID
|
||||
}
|
||||
s1 := S{ID1: testUUID}
|
||||
data, err := json.Marshal(&s1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var s2 S
|
||||
if err := json.Unmarshal(data, &s2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(&s1, &s2) {
|
||||
t.Errorf("got %#v, want %#v", s2, s1)
|
||||
}
|
||||
}
|
||||
101
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/node.go
сгенерированный
поставляемый
Обычный файл
101
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/node.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,101 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "net"
|
||||
|
||||
var (
|
||||
interfaces []net.Interface // cached list of interfaces
|
||||
ifname string // name of interface being used
|
||||
nodeID []byte // hardware for version 1 UUIDs
|
||||
)
|
||||
|
||||
// NodeInterface returns the name of the interface from which the NodeID was
|
||||
// derived. The interface "user" is returned if the NodeID was set by
|
||||
// SetNodeID.
|
||||
func NodeInterface() string {
|
||||
return ifname
|
||||
}
|
||||
|
||||
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
||||
// If name is "" then the first usable interface found will be used or a random
|
||||
// Node ID will be generated. If a named interface cannot be found then false
|
||||
// is returned.
|
||||
//
|
||||
// SetNodeInterface never fails when name is "".
|
||||
func SetNodeInterface(name string) bool {
|
||||
if interfaces == nil {
|
||||
var err error
|
||||
interfaces, err = net.Interfaces()
|
||||
if err != nil && name != "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, ifs := range interfaces {
|
||||
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
||||
if setNodeID(ifs.HardwareAddr) {
|
||||
ifname = ifs.Name
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We found no interfaces with a valid hardware address. If name
|
||||
// does not specify a specific interface generate a random Node ID
|
||||
// (section 4.1.6)
|
||||
if name == "" {
|
||||
if nodeID == nil {
|
||||
nodeID = make([]byte, 6)
|
||||
}
|
||||
randomBits(nodeID)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
||||
// if not already set.
|
||||
func NodeID() []byte {
|
||||
if nodeID == nil {
|
||||
SetNodeInterface("")
|
||||
}
|
||||
nid := make([]byte, 6)
|
||||
copy(nid, nodeID)
|
||||
return nid
|
||||
}
|
||||
|
||||
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
||||
// of id are used. If id is less than 6 bytes then false is returned and the
|
||||
// Node ID is not set.
|
||||
func SetNodeID(id []byte) bool {
|
||||
if setNodeID(id) {
|
||||
ifname = "user"
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func setNodeID(id []byte) bool {
|
||||
if len(id) < 6 {
|
||||
return false
|
||||
}
|
||||
if nodeID == nil {
|
||||
nodeID = make([]byte, 6)
|
||||
}
|
||||
copy(nodeID, id)
|
||||
return true
|
||||
}
|
||||
|
||||
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
||||
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) NodeID() []byte {
|
||||
if len(uuid) != 16 {
|
||||
return nil
|
||||
}
|
||||
node := make([]byte, 6)
|
||||
copy(node, uuid[10:])
|
||||
return node
|
||||
}
|
||||
66
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/seq_test.go
сгенерированный
поставляемый
Обычный файл
66
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/seq_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,66 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This test is only run when --regressions is passed on the go test line.
|
||||
var regressions = flag.Bool("regressions", false, "run uuid regression tests")
|
||||
|
||||
// TestClockSeqRace tests for a particular race condition of returning two
|
||||
// identical Version1 UUIDs. The duration of 1 minute was chosen as the race
|
||||
// condition, before being fixed, nearly always occured in under 30 seconds.
|
||||
func TestClockSeqRace(t *testing.T) {
|
||||
if !*regressions {
|
||||
t.Skip("skipping regression tests")
|
||||
}
|
||||
duration := time.Minute
|
||||
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
|
||||
ch := make(chan UUID, 10000)
|
||||
ncpu := runtime.NumCPU()
|
||||
switch ncpu {
|
||||
case 0, 1:
|
||||
// We can't run the test effectively.
|
||||
t.Skip("skipping race test, only one CPU detected")
|
||||
return
|
||||
default:
|
||||
runtime.GOMAXPROCS(ncpu)
|
||||
}
|
||||
for i := 0; i < ncpu; i++ {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case ch <- NewUUID():
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
uuids := make(map[string]bool)
|
||||
cnt := 0
|
||||
start := time.Now()
|
||||
for u := range ch {
|
||||
s := u.String()
|
||||
if uuids[s] {
|
||||
t.Errorf("duplicate uuid after %d in %v: %s", cnt, time.Since(start), s)
|
||||
return
|
||||
}
|
||||
uuids[s] = true
|
||||
if time.Since(start) > duration {
|
||||
return
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
132
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/time.go
сгенерированный
поставляемый
Обычный файл
132
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/time.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,132 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
||||
// 1582.
|
||||
type Time int64
|
||||
|
||||
const (
|
||||
lillian = 2299160 // Julian day of 15 Oct 1582
|
||||
unix = 2440587 // Julian day of 1 Jan 1970
|
||||
epoch = unix - lillian // Days between epochs
|
||||
g1582 = epoch * 86400 // seconds between epochs
|
||||
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
lasttime uint64 // last time we returned
|
||||
clock_seq uint16 // clock sequence for this run
|
||||
|
||||
timeNow = time.Now // for testing
|
||||
)
|
||||
|
||||
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
||||
// epoch of 1 Jan 1970.
|
||||
func (t Time) UnixTime() (sec, nsec int64) {
|
||||
sec = int64(t - g1582ns100)
|
||||
nsec = (sec % 10000000) * 100
|
||||
sec /= 10000000
|
||||
return sec, nsec
|
||||
}
|
||||
|
||||
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
|
||||
// clock sequence as well as adjusting the clock sequence as needed. An error
|
||||
// is returned if the current time cannot be determined.
|
||||
func GetTime() (Time, uint16, error) {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
return getTime()
|
||||
}
|
||||
|
||||
func getTime() (Time, uint16, error) {
|
||||
t := timeNow()
|
||||
|
||||
// If we don't have a clock sequence already, set one.
|
||||
if clock_seq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
now := uint64(t.UnixNano()/100) + g1582ns100
|
||||
|
||||
// If time has gone backwards with this clock sequence then we
|
||||
// increment the clock sequence
|
||||
if now <= lasttime {
|
||||
clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
|
||||
}
|
||||
lasttime = now
|
||||
return Time(now), clock_seq, nil
|
||||
}
|
||||
|
||||
// ClockSequence returns the current clock sequence, generating one if not
|
||||
// already set. The clock sequence is only used for Version 1 UUIDs.
|
||||
//
|
||||
// The uuid package does not use global static storage for the clock sequence or
|
||||
// the last time a UUID was generated. Unless SetClockSequence a new random
|
||||
// clock sequence is generated the first time a clock sequence is requested by
|
||||
// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated
|
||||
// for
|
||||
func ClockSequence() int {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
return clockSequence()
|
||||
}
|
||||
|
||||
func clockSequence() int {
|
||||
if clock_seq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
return int(clock_seq & 0x3fff)
|
||||
}
|
||||
|
||||
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
|
||||
// -1 causes a new sequence to be generated.
|
||||
func SetClockSequence(seq int) {
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
setClockSequence(seq)
|
||||
}
|
||||
|
||||
func setClockSequence(seq int) {
|
||||
if seq == -1 {
|
||||
var b [2]byte
|
||||
randomBits(b[:]) // clock sequence
|
||||
seq = int(b[0])<<8 | int(b[1])
|
||||
}
|
||||
old_seq := clock_seq
|
||||
clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
||||
if old_seq != clock_seq {
|
||||
lasttime = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||
// uuid. It returns false if uuid is not valid. The time is only well defined
|
||||
// for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) Time() (Time, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
return Time(time), true
|
||||
}
|
||||
|
||||
// ClockSequence returns the clock sequence encoded in uuid. It returns false
|
||||
// if uuid is not valid. The clock sequence is only well defined for version 1
|
||||
// and 2 UUIDs.
|
||||
func (uuid UUID) ClockSequence() (int, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true
|
||||
}
|
||||
43
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/util.go
сгенерированный
поставляемый
Обычный файл
43
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/util.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,43 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// randomBits completely fills slice b with random data.
|
||||
func randomBits(b []byte) {
|
||||
if _, err := io.ReadFull(rander, b); err != nil {
|
||||
panic(err.Error()) // rand should never fail
|
||||
}
|
||||
}
|
||||
|
||||
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
||||
var xvalues = []byte{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
}
|
||||
|
||||
// xtob converts the the first two hex bytes of x into a byte.
|
||||
func xtob(x string) (byte, bool) {
|
||||
b1 := xvalues[x[0]]
|
||||
b2 := xvalues[x[1]]
|
||||
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
||||
}
|
||||
163
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid.go
сгенерированный
поставляемый
Обычный файл
163
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,163 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
||||
// 4122.
|
||||
type UUID []byte
|
||||
|
||||
// A Version represents a UUIDs version.
|
||||
type Version byte
|
||||
|
||||
// A Variant represents a UUIDs variant.
|
||||
type Variant byte
|
||||
|
||||
// Constants returned by Variant.
|
||||
const (
|
||||
Invalid = Variant(iota) // Invalid UUID
|
||||
RFC4122 // The variant specified in RFC4122
|
||||
Reserved // Reserved, NCS backward compatibility.
|
||||
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
||||
Future // Reserved for future definition.
|
||||
)
|
||||
|
||||
var rander = rand.Reader // random function
|
||||
|
||||
// New returns a new random (version 4) UUID as a string. It is a convenience
|
||||
// function for NewRandom().String().
|
||||
func New() string {
|
||||
return NewRandom().String()
|
||||
}
|
||||
|
||||
// Parse decodes s into a UUID or returns nil. Both the UUID form of
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
|
||||
func Parse(s string) UUID {
|
||||
if len(s) == 36+9 {
|
||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||
return nil
|
||||
}
|
||||
s = s[9:]
|
||||
} else if len(s) != 36 {
|
||||
return nil
|
||||
}
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return nil
|
||||
}
|
||||
uuid := make([]byte, 16)
|
||||
for i, x := range []int{
|
||||
0, 2, 4, 6,
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
if v, ok := xtob(s[x:]); !ok {
|
||||
return nil
|
||||
} else {
|
||||
uuid[i] = v
|
||||
}
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// Equal returns true if uuid1 and uuid2 are equal.
|
||||
func Equal(uuid1, uuid2 UUID) bool {
|
||||
return bytes.Equal(uuid1, uuid2)
|
||||
}
|
||||
|
||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// , or "" if uuid is invalid.
|
||||
func (uuid UUID) String() string {
|
||||
if uuid == nil || len(uuid) != 16 {
|
||||
return ""
|
||||
}
|
||||
b := []byte(uuid)
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
|
||||
b[:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
}
|
||||
|
||||
// URN returns the RFC 2141 URN form of uuid,
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
||||
func (uuid UUID) URN() string {
|
||||
if uuid == nil || len(uuid) != 16 {
|
||||
return ""
|
||||
}
|
||||
b := []byte(uuid)
|
||||
return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x",
|
||||
b[:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||
}
|
||||
|
||||
// Variant returns the variant encoded in uuid. It returns Invalid if
|
||||
// uuid is invalid.
|
||||
func (uuid UUID) Variant() Variant {
|
||||
if len(uuid) != 16 {
|
||||
return Invalid
|
||||
}
|
||||
switch {
|
||||
case (uuid[8] & 0xc0) == 0x80:
|
||||
return RFC4122
|
||||
case (uuid[8] & 0xe0) == 0xc0:
|
||||
return Microsoft
|
||||
case (uuid[8] & 0xe0) == 0xe0:
|
||||
return Future
|
||||
default:
|
||||
return Reserved
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Version returns the verison of uuid. It returns false if uuid is not
|
||||
// valid.
|
||||
func (uuid UUID) Version() (Version, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
return Version(uuid[6] >> 4), true
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
if v > 15 {
|
||||
return fmt.Sprintf("BAD_VERSION_%d", v)
|
||||
}
|
||||
return fmt.Sprintf("VERSION_%d", v)
|
||||
}
|
||||
|
||||
func (v Variant) String() string {
|
||||
switch v {
|
||||
case RFC4122:
|
||||
return "RFC4122"
|
||||
case Reserved:
|
||||
return "Reserved"
|
||||
case Microsoft:
|
||||
return "Microsoft"
|
||||
case Future:
|
||||
return "Future"
|
||||
case Invalid:
|
||||
return "Invalid"
|
||||
}
|
||||
return fmt.Sprintf("BadVariant%d", int(v))
|
||||
}
|
||||
|
||||
// SetRand sets the random number generator to r, which implents io.Reader.
|
||||
// If r.Read returns an error when the package requests random data then
|
||||
// a panic will be issued.
|
||||
//
|
||||
// Calling SetRand with nil sets the random number generator to the default
|
||||
// generator.
|
||||
func SetRand(r io.Reader) {
|
||||
if r == nil {
|
||||
rander = rand.Reader
|
||||
return
|
||||
}
|
||||
rander = r
|
||||
}
|
||||
390
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid_test.go
сгенерированный
поставляемый
Обычный файл
390
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/uuid_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,390 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
in string
|
||||
version Version
|
||||
variant Variant
|
||||
isuuid bool
|
||||
}
|
||||
|
||||
var tests = []test{
|
||||
{"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true},
|
||||
{"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true},
|
||||
{"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true},
|
||||
{"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true},
|
||||
{"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true},
|
||||
{"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true},
|
||||
{"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true},
|
||||
{"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true},
|
||||
{"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true},
|
||||
{"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true},
|
||||
{"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true},
|
||||
{"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true},
|
||||
{"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true},
|
||||
{"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true},
|
||||
|
||||
{"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true},
|
||||
{"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true},
|
||||
{"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true},
|
||||
{"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true},
|
||||
|
||||
{"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false},
|
||||
{"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
}
|
||||
|
||||
var constants = []struct {
|
||||
c interface{}
|
||||
name string
|
||||
}{
|
||||
{Person, "Person"},
|
||||
{Group, "Group"},
|
||||
{Org, "Org"},
|
||||
{Invalid, "Invalid"},
|
||||
{RFC4122, "RFC4122"},
|
||||
{Reserved, "Reserved"},
|
||||
{Microsoft, "Microsoft"},
|
||||
{Future, "Future"},
|
||||
{Domain(17), "Domain17"},
|
||||
{Variant(42), "BadVariant42"},
|
||||
}
|
||||
|
||||
func testTest(t *testing.T, in string, tt test) {
|
||||
uuid := Parse(in)
|
||||
if ok := (uuid != nil); ok != tt.isuuid {
|
||||
t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid)
|
||||
}
|
||||
if uuid == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v := uuid.Variant(); v != tt.variant {
|
||||
t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant)
|
||||
}
|
||||
if v, _ := uuid.Version(); v != tt.version {
|
||||
t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUUID(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
testTest(t, tt.in, tt)
|
||||
testTest(t, strings.ToUpper(tt.in), tt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConstants(t *testing.T) {
|
||||
for x, tt := range constants {
|
||||
v, ok := tt.c.(fmt.Stringer)
|
||||
if !ok {
|
||||
t.Errorf("%x: %v: not a stringer", x, v)
|
||||
} else if s := v.String(); s != tt.name {
|
||||
v, _ := tt.c.(int)
|
||||
t.Errorf("%x: Constant %T:%d gives %q, expected %q\n", x, tt.c, v, s, tt.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomUUID(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
for x := 1; x < 32; x++ {
|
||||
uuid := NewRandom()
|
||||
s := uuid.String()
|
||||
if m[s] {
|
||||
t.Errorf("NewRandom returned duplicated UUID %s\n", s)
|
||||
}
|
||||
m[s] = true
|
||||
if v, _ := uuid.Version(); v != 4 {
|
||||
t.Errorf("Random UUID of version %s\n", v)
|
||||
}
|
||||
if uuid.Variant() != RFC4122 {
|
||||
t.Errorf("Random UUID is variant %d\n", uuid.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
for x := 1; x < 32; x++ {
|
||||
s := New()
|
||||
if m[s] {
|
||||
t.Errorf("New returned duplicated UUID %s\n", s)
|
||||
}
|
||||
m[s] = true
|
||||
uuid := Parse(s)
|
||||
if uuid == nil {
|
||||
t.Errorf("New returned %q which does not decode\n", s)
|
||||
continue
|
||||
}
|
||||
if v, _ := uuid.Version(); v != 4 {
|
||||
t.Errorf("Random UUID of version %s\n", v)
|
||||
}
|
||||
if uuid.Variant() != RFC4122 {
|
||||
t.Errorf("Random UUID is variant %d\n", uuid.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clockSeq(t *testing.T, uuid UUID) int {
|
||||
seq, ok := uuid.ClockSequence()
|
||||
if !ok {
|
||||
t.Fatalf("%s: invalid clock sequence\n", uuid)
|
||||
}
|
||||
return seq
|
||||
}
|
||||
|
||||
func TestClockSeq(t *testing.T) {
|
||||
// Fake time.Now for this test to return a monotonically advancing time; restore it at end.
|
||||
defer func(orig func() time.Time) { timeNow = orig }(timeNow)
|
||||
monTime := time.Now()
|
||||
timeNow = func() time.Time {
|
||||
monTime = monTime.Add(1 * time.Second)
|
||||
return monTime
|
||||
}
|
||||
|
||||
SetClockSequence(-1)
|
||||
uuid1 := NewUUID()
|
||||
uuid2 := NewUUID()
|
||||
|
||||
if clockSeq(t, uuid1) != clockSeq(t, uuid2) {
|
||||
t.Errorf("clock sequence %d != %d\n", clockSeq(t, uuid1), clockSeq(t, uuid2))
|
||||
}
|
||||
|
||||
SetClockSequence(-1)
|
||||
uuid2 = NewUUID()
|
||||
|
||||
// Just on the very off chance we generated the same sequence
|
||||
// two times we try again.
|
||||
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||
SetClockSequence(-1)
|
||||
uuid2 = NewUUID()
|
||||
}
|
||||
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||
t.Errorf("Duplicate clock sequence %d\n", clockSeq(t, uuid1))
|
||||
}
|
||||
|
||||
SetClockSequence(0x1234)
|
||||
uuid1 = NewUUID()
|
||||
if seq := clockSeq(t, uuid1); seq != 0x1234 {
|
||||
t.Errorf("%s: expected seq 0x1234 got 0x%04x\n", uuid1, seq)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoding(t *testing.T) {
|
||||
text := "7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||
urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||
data := UUID{
|
||||
0x7d, 0x44, 0x48, 0x40,
|
||||
0x9d, 0xc0,
|
||||
0x11, 0xd1,
|
||||
0xb2, 0x45,
|
||||
0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2,
|
||||
}
|
||||
if v := data.String(); v != text {
|
||||
t.Errorf("%x: encoded to %s, expected %s\n", data, v, text)
|
||||
}
|
||||
if v := data.URN(); v != urn {
|
||||
t.Errorf("%x: urn is %s, expected %s\n", data, v, urn)
|
||||
}
|
||||
|
||||
uuid := Parse(text)
|
||||
if !Equal(uuid, data) {
|
||||
t.Errorf("%s: decoded to %s, expected %s\n", text, uuid, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion1(t *testing.T) {
|
||||
uuid1 := NewUUID()
|
||||
uuid2 := NewUUID()
|
||||
|
||||
if Equal(uuid1, uuid2) {
|
||||
t.Errorf("%s:duplicate uuid\n", uuid1)
|
||||
}
|
||||
if v, _ := uuid1.Version(); v != 1 {
|
||||
t.Errorf("%s: version %s expected 1\n", uuid1, v)
|
||||
}
|
||||
if v, _ := uuid2.Version(); v != 1 {
|
||||
t.Errorf("%s: version %s expected 1\n", uuid2, v)
|
||||
}
|
||||
n1 := uuid1.NodeID()
|
||||
n2 := uuid2.NodeID()
|
||||
if !bytes.Equal(n1, n2) {
|
||||
t.Errorf("Different nodes %x != %x\n", n1, n2)
|
||||
}
|
||||
t1, ok := uuid1.Time()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid time\n", uuid1)
|
||||
}
|
||||
t2, ok := uuid2.Time()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid time\n", uuid2)
|
||||
}
|
||||
q1, ok := uuid1.ClockSequence()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid clock sequence\n", uuid1)
|
||||
}
|
||||
q2, ok := uuid2.ClockSequence()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid clock sequence", uuid2)
|
||||
}
|
||||
|
||||
switch {
|
||||
case t1 == t2 && q1 == q2:
|
||||
t.Errorf("time stopped\n")
|
||||
case t1 > t2 && q1 == q2:
|
||||
t.Errorf("time reversed\n")
|
||||
case t1 < t2 && q1 != q2:
|
||||
t.Errorf("clock sequence chaned unexpectedly\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeAndTime(t *testing.T) {
|
||||
// Time is February 5, 1998 12:30:23.136364800 AM GMT
|
||||
|
||||
uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2")
|
||||
node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2}
|
||||
|
||||
ts, ok := uuid.Time()
|
||||
if ok {
|
||||
c := time.Unix(ts.UnixTime())
|
||||
want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC)
|
||||
if !c.Equal(want) {
|
||||
t.Errorf("Got time %v, want %v", c, want)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("%s: bad time\n", uuid)
|
||||
}
|
||||
if !bytes.Equal(node, uuid.NodeID()) {
|
||||
t.Errorf("Expected node %v got %v\n", node, uuid.NodeID())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMD5(t *testing.T) {
|
||||
uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String()
|
||||
want := "6fa459ea-ee8a-3ca4-894e-db77e160355e"
|
||||
if uuid != want {
|
||||
t.Errorf("MD5: got %q expected %q\n", uuid, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSHA1(t *testing.T) {
|
||||
uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String()
|
||||
want := "886313e1-3b8a-5372-9b90-0c9aee199e5d"
|
||||
if uuid != want {
|
||||
t.Errorf("SHA1: got %q expected %q\n", uuid, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID(t *testing.T) {
|
||||
nid := []byte{1, 2, 3, 4, 5, 6}
|
||||
SetNodeInterface("")
|
||||
s := NodeInterface()
|
||||
if s == "" || s == "user" {
|
||||
t.Errorf("NodeInterface %q after SetInteface\n", s)
|
||||
}
|
||||
node1 := NodeID()
|
||||
if node1 == nil {
|
||||
t.Errorf("NodeID nil after SetNodeInterface\n", s)
|
||||
}
|
||||
SetNodeID(nid)
|
||||
s = NodeInterface()
|
||||
if s != "user" {
|
||||
t.Errorf("Expected NodeInterface %q got %q\n", "user", s)
|
||||
}
|
||||
node2 := NodeID()
|
||||
if node2 == nil {
|
||||
t.Errorf("NodeID nil after SetNodeID\n", s)
|
||||
}
|
||||
if bytes.Equal(node1, node2) {
|
||||
t.Errorf("NodeID not changed after SetNodeID\n", s)
|
||||
} else if !bytes.Equal(nid, node2) {
|
||||
t.Errorf("NodeID is %x, expected %x\n", node2, nid)
|
||||
}
|
||||
}
|
||||
|
||||
func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) {
|
||||
if uuid == nil {
|
||||
t.Errorf("%s failed\n", name)
|
||||
return
|
||||
}
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
t.Errorf("%s: %s: expected version 2, got %s\n", name, uuid, v)
|
||||
return
|
||||
}
|
||||
if v, ok := uuid.Domain(); !ok || v != domain {
|
||||
if !ok {
|
||||
t.Errorf("%s: %d: Domain failed\n", name, uuid)
|
||||
} else {
|
||||
t.Errorf("%s: %s: expected domain %d, got %d\n", name, uuid, domain, v)
|
||||
}
|
||||
}
|
||||
if v, ok := uuid.Id(); !ok || v != id {
|
||||
if !ok {
|
||||
t.Errorf("%s: %d: Id failed\n", name, uuid)
|
||||
} else {
|
||||
t.Errorf("%s: %s: expected id %d, got %d\n", name, uuid, id, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDCE(t *testing.T) {
|
||||
testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678)
|
||||
testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid()))
|
||||
testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
type badRand struct{}
|
||||
|
||||
func (r badRand) Read(buf []byte) (int, error) {
|
||||
for i, _ := range buf {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func TestBadRand(t *testing.T) {
|
||||
SetRand(badRand{})
|
||||
uuid1 := New()
|
||||
uuid2 := New()
|
||||
if uuid1 != uuid2 {
|
||||
t.Errorf("execpted duplicates, got %q and %q\n", uuid1, uuid2)
|
||||
}
|
||||
SetRand(nil)
|
||||
uuid1 = New()
|
||||
uuid2 = New()
|
||||
if uuid1 == uuid2 {
|
||||
t.Errorf("unexecpted duplicates, got %q\n", uuid1)
|
||||
}
|
||||
}
|
||||
41
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version1.go
сгенерированный
поставляемый
Обычный файл
41
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version1.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,41 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||
// be set NewUUID returns nil. If clock sequence has not been set by
|
||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||
// return the current NewUUID returns nil.
|
||||
func NewUUID() UUID {
|
||||
if nodeID == nil {
|
||||
SetNodeInterface("")
|
||||
}
|
||||
|
||||
now, seq, err := GetTime()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uuid := make([]byte, 16)
|
||||
|
||||
time_low := uint32(now & 0xffffffff)
|
||||
time_mid := uint16((now >> 32) & 0xffff)
|
||||
time_hi := uint16((now >> 48) & 0x0fff)
|
||||
time_hi |= 0x1000 // Version 1
|
||||
|
||||
binary.BigEndian.PutUint32(uuid[0:], time_low)
|
||||
binary.BigEndian.PutUint16(uuid[4:], time_mid)
|
||||
binary.BigEndian.PutUint16(uuid[6:], time_hi)
|
||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||
copy(uuid[10:], nodeID)
|
||||
|
||||
return uuid
|
||||
}
|
||||
25
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version4.go
сгенерированный
поставляемый
Обычный файл
25
Godeps/_workspace/src/code.google.com/p/go-uuid/uuid/version4.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,25 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
// Random returns a Random (Version 4) UUID or panics.
|
||||
//
|
||||
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||
// package.
|
||||
//
|
||||
// A note about uniqueness derived from from the UUID Wikipedia entry:
|
||||
//
|
||||
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
||||
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
||||
// means the probability is about 0.00000000006 (6 × 10−11),
|
||||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||
// year and having one duplicate.
|
||||
func NewRandom() UUID {
|
||||
uuid := make([]byte, 16)
|
||||
randomBits([]byte(uuid))
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||
return uuid
|
||||
}
|
||||
35
Godeps/_workspace/src/code.google.com/p/go.crypto/bcrypt/base64.go
сгенерированный
поставляемый
Обычный файл
35
Godeps/_workspace/src/code.google.com/p/go.crypto/bcrypt/base64.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,35 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bcrypt
|
||||
|
||||
import "encoding/base64"
|
||||
|
||||
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
var bcEncoding = base64.NewEncoding(alphabet)
|
||||
|
||||
func base64Encode(src []byte) []byte {
|
||||
n := bcEncoding.EncodedLen(len(src))
|
||||
dst := make([]byte, n)
|
||||
bcEncoding.Encode(dst, src)
|
||||
for dst[n-1] == '=' {
|
||||
n--
|
||||
}
|
||||
return dst[:n]
|
||||
}
|
||||
|
||||
func base64Decode(src []byte) ([]byte, error) {
|
||||
numOfEquals := 4 - (len(src) % 4)
|
||||
for i := 0; i < numOfEquals; i++ {
|
||||
src = append(src, '=')
|
||||
}
|
||||
|
||||
dst := make([]byte, bcEncoding.DecodedLen(len(src)))
|
||||
n, err := bcEncoding.Decode(dst, src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dst[:n], nil
|
||||
}
|
||||
294
Godeps/_workspace/src/code.google.com/p/go.crypto/bcrypt/bcrypt.go
сгенерированный
поставляемый
Обычный файл
294
Godeps/_workspace/src/code.google.com/p/go.crypto/bcrypt/bcrypt.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,294 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
|
||||
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
|
||||
package bcrypt
|
||||
|
||||
// The code is a port of Provos and Mazières's C implementation.
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/blowfish"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
|
||||
MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
|
||||
DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
|
||||
)
|
||||
|
||||
// The error returned from CompareHashAndPassword when a password and hash do
|
||||
// not match.
|
||||
var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
|
||||
|
||||
// The error returned from CompareHashAndPassword when a hash is too short to
|
||||
// be a bcrypt hash.
|
||||
var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
|
||||
|
||||
// The error returned from CompareHashAndPassword when a hash was created with
|
||||
// a bcrypt algorithm newer than this implementation.
|
||||
type HashVersionTooNewError byte
|
||||
|
||||
func (hv HashVersionTooNewError) Error() string {
|
||||
return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
|
||||
}
|
||||
|
||||
// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
|
||||
type InvalidHashPrefixError byte
|
||||
|
||||
func (ih InvalidHashPrefixError) Error() string {
|
||||
return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
|
||||
}
|
||||
|
||||
type InvalidCostError int
|
||||
|
||||
func (ic InvalidCostError) Error() string {
|
||||
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
|
||||
}
|
||||
|
||||
const (
|
||||
majorVersion = '2'
|
||||
minorVersion = 'a'
|
||||
maxSaltSize = 16
|
||||
maxCryptedHashSize = 23
|
||||
encodedSaltSize = 22
|
||||
encodedHashSize = 31
|
||||
minHashSize = 59
|
||||
)
|
||||
|
||||
// magicCipherData is an IV for the 64 Blowfish encryption calls in
|
||||
// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
|
||||
var magicCipherData = []byte{
|
||||
0x4f, 0x72, 0x70, 0x68,
|
||||
0x65, 0x61, 0x6e, 0x42,
|
||||
0x65, 0x68, 0x6f, 0x6c,
|
||||
0x64, 0x65, 0x72, 0x53,
|
||||
0x63, 0x72, 0x79, 0x44,
|
||||
0x6f, 0x75, 0x62, 0x74,
|
||||
}
|
||||
|
||||
type hashed struct {
|
||||
hash []byte
|
||||
salt []byte
|
||||
cost int // allowed range is MinCost to MaxCost
|
||||
major byte
|
||||
minor byte
|
||||
}
|
||||
|
||||
// GenerateFromPassword returns the bcrypt hash of the password at the given
|
||||
// cost. If the cost given is less than MinCost, the cost will be set to
|
||||
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
|
||||
// to compare the returned hashed password with its cleartext version.
|
||||
func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
|
||||
p, err := newFromPassword(password, cost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Hash(), nil
|
||||
}
|
||||
|
||||
// CompareHashAndPassword compares a bcrypt hashed password with its possible
|
||||
// plaintext equivalent. Returns nil on success, or an error on failure.
|
||||
func CompareHashAndPassword(hashedPassword, password []byte) error {
|
||||
p, err := newFromHash(hashedPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
otherHash, err := bcrypt(password, p.cost, p.salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
|
||||
if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrMismatchedHashAndPassword
|
||||
}
|
||||
|
||||
// Cost returns the hashing cost used to create the given hashed
|
||||
// password. When, in the future, the hashing cost of a password system needs
|
||||
// to be increased in order to adjust for greater computational power, this
|
||||
// function allows one to establish which passwords need to be updated.
|
||||
func Cost(hashedPassword []byte) (int, error) {
|
||||
p, err := newFromHash(hashedPassword)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return p.cost, nil
|
||||
}
|
||||
|
||||
func newFromPassword(password []byte, cost int) (*hashed, error) {
|
||||
if cost < MinCost {
|
||||
cost = DefaultCost
|
||||
}
|
||||
p := new(hashed)
|
||||
p.major = majorVersion
|
||||
p.minor = minorVersion
|
||||
|
||||
err := checkCost(cost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.cost = cost
|
||||
|
||||
unencodedSalt := make([]byte, maxSaltSize)
|
||||
_, err = io.ReadFull(rand.Reader, unencodedSalt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.salt = base64Encode(unencodedSalt)
|
||||
hash, err := bcrypt(password, p.cost, p.salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.hash = hash
|
||||
return p, err
|
||||
}
|
||||
|
||||
func newFromHash(hashedSecret []byte) (*hashed, error) {
|
||||
if len(hashedSecret) < minHashSize {
|
||||
return nil, ErrHashTooShort
|
||||
}
|
||||
p := new(hashed)
|
||||
n, err := p.decodeVersion(hashedSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashedSecret = hashedSecret[n:]
|
||||
n, err = p.decodeCost(hashedSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashedSecret = hashedSecret[n:]
|
||||
|
||||
// The "+2" is here because we'll have to append at most 2 '=' to the salt
|
||||
// when base64 decoding it in expensiveBlowfishSetup().
|
||||
p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
|
||||
copy(p.salt, hashedSecret[:encodedSaltSize])
|
||||
|
||||
hashedSecret = hashedSecret[encodedSaltSize:]
|
||||
p.hash = make([]byte, len(hashedSecret))
|
||||
copy(p.hash, hashedSecret)
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
|
||||
cipherData := make([]byte, len(magicCipherData))
|
||||
copy(cipherData, magicCipherData)
|
||||
|
||||
c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < 24; i += 8 {
|
||||
for j := 0; j < 64; j++ {
|
||||
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
|
||||
}
|
||||
}
|
||||
|
||||
// Bug compatibility with C bcrypt implementations. We only encode 23 of
|
||||
// the 24 bytes encrypted.
|
||||
hsh := base64Encode(cipherData[:maxCryptedHashSize])
|
||||
return hsh, nil
|
||||
}
|
||||
|
||||
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
|
||||
|
||||
csalt, err := base64Decode(salt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Bug compatibility with C bcrypt implementations. They use the trailing
|
||||
// NULL in the key string during expansion.
|
||||
ckey := append(key, 0)
|
||||
|
||||
c, err := blowfish.NewSaltedCipher(ckey, csalt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var i, rounds uint64
|
||||
rounds = 1 << cost
|
||||
for i = 0; i < rounds; i++ {
|
||||
blowfish.ExpandKey(ckey, c)
|
||||
blowfish.ExpandKey(csalt, c)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (p *hashed) Hash() []byte {
|
||||
arr := make([]byte, 60)
|
||||
arr[0] = '$'
|
||||
arr[1] = p.major
|
||||
n := 2
|
||||
if p.minor != 0 {
|
||||
arr[2] = p.minor
|
||||
n = 3
|
||||
}
|
||||
arr[n] = '$'
|
||||
n += 1
|
||||
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
|
||||
n += 2
|
||||
arr[n] = '$'
|
||||
n += 1
|
||||
copy(arr[n:], p.salt)
|
||||
n += encodedSaltSize
|
||||
copy(arr[n:], p.hash)
|
||||
n += encodedHashSize
|
||||
return arr[:n]
|
||||
}
|
||||
|
||||
func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
|
||||
if sbytes[0] != '$' {
|
||||
return -1, InvalidHashPrefixError(sbytes[0])
|
||||
}
|
||||
if sbytes[1] > majorVersion {
|
||||
return -1, HashVersionTooNewError(sbytes[1])
|
||||
}
|
||||
p.major = sbytes[1]
|
||||
n := 3
|
||||
if sbytes[2] != '$' {
|
||||
p.minor = sbytes[2]
|
||||
n++
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// sbytes should begin where decodeVersion left off.
|
||||
func (p *hashed) decodeCost(sbytes []byte) (int, error) {
|
||||
cost, err := strconv.Atoi(string(sbytes[0:2]))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
err = checkCost(cost)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
p.cost = cost
|
||||
return 3, nil
|
||||
}
|
||||
|
||||
func (p *hashed) String() string {
|
||||
return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
|
||||
}
|
||||
|
||||
func checkCost(cost int) error {
|
||||
if cost < MinCost || cost > MaxCost {
|
||||
return InvalidCostError(cost)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
226
Godeps/_workspace/src/code.google.com/p/go.crypto/bcrypt/bcrypt_test.go
сгенерированный
поставляемый
Обычный файл
226
Godeps/_workspace/src/code.google.com/p/go.crypto/bcrypt/bcrypt_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,226 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bcrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBcryptingIsEasy(t *testing.T) {
|
||||
pass := []byte("mypassword")
|
||||
hp, err := GenerateFromPassword(pass, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateFromPassword error: %s", err)
|
||||
}
|
||||
|
||||
if CompareHashAndPassword(hp, pass) != nil {
|
||||
t.Errorf("%v should hash %s correctly", hp, pass)
|
||||
}
|
||||
|
||||
notPass := "notthepass"
|
||||
err = CompareHashAndPassword(hp, []byte(notPass))
|
||||
if err != ErrMismatchedHashAndPassword {
|
||||
t.Errorf("%v and %s should be mismatched", hp, notPass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcryptingIsCorrect(t *testing.T) {
|
||||
pass := []byte("allmine")
|
||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||
expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
|
||||
|
||||
hash, err := bcrypt(pass, 10, salt)
|
||||
if err != nil {
|
||||
t.Fatalf("bcrypt blew up: %v", err)
|
||||
}
|
||||
if !bytes.HasSuffix(expectedHash, hash) {
|
||||
t.Errorf("%v should be the suffix of %v", hash, expectedHash)
|
||||
}
|
||||
|
||||
h, err := newFromHash(expectedHash)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
|
||||
}
|
||||
|
||||
// This is not the safe way to compare these hashes. We do this only for
|
||||
// testing clarity. Use bcrypt.CompareHashAndPassword()
|
||||
if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
|
||||
t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVeryShortPasswords(t *testing.T) {
|
||||
key := []byte("k")
|
||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||
_, err := bcrypt(key, 10, salt)
|
||||
if err != nil {
|
||||
t.Errorf("One byte key resulted in error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTooLongPasswordsWork(t *testing.T) {
|
||||
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||
// One byte over the usual 56 byte limit that blowfish has
|
||||
tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
|
||||
tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
|
||||
hash, err := bcrypt(tooLongPass, 10, salt)
|
||||
if err != nil {
|
||||
t.Fatalf("bcrypt blew up on long password: %v", err)
|
||||
}
|
||||
if !bytes.HasSuffix(tooLongExpected, hash) {
|
||||
t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
|
||||
}
|
||||
}
|
||||
|
||||
type InvalidHashTest struct {
|
||||
err error
|
||||
hash []byte
|
||||
}
|
||||
|
||||
var invalidTests = []InvalidHashTest{
|
||||
{ErrHashTooShort, []byte("$2a$10$fooo")},
|
||||
{ErrHashTooShort, []byte("$2a")},
|
||||
{HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||
{InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||
{InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
|
||||
}
|
||||
|
||||
func TestInvalidHashErrors(t *testing.T) {
|
||||
check := func(name string, expected, err error) {
|
||||
if err == nil {
|
||||
t.Errorf("%s: Should have returned an error", name)
|
||||
}
|
||||
if err != nil && err != expected {
|
||||
t.Errorf("%s gave err %v but should have given %v", name, err, expected)
|
||||
}
|
||||
}
|
||||
for _, iht := range invalidTests {
|
||||
_, err := newFromHash(iht.hash)
|
||||
check("newFromHash", iht.err, err)
|
||||
err = CompareHashAndPassword(iht.hash, []byte("anything"))
|
||||
check("CompareHashAndPassword", iht.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnpaddedBase64Encoding(t *testing.T) {
|
||||
original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
|
||||
encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
|
||||
|
||||
encoded := base64Encode(original)
|
||||
|
||||
if !bytes.Equal(encodedOriginal, encoded) {
|
||||
t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
|
||||
}
|
||||
|
||||
decoded, err := base64Decode(encodedOriginal)
|
||||
if err != nil {
|
||||
t.Fatalf("base64Decode blew up: %s", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(decoded, original) {
|
||||
t.Errorf("Decoded %v should have equaled %v", decoded, original)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCost(t *testing.T) {
|
||||
suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
|
||||
for _, vers := range []string{"2a", "2"} {
|
||||
for _, cost := range []int{4, 10} {
|
||||
s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
|
||||
h := []byte(s)
|
||||
actual, err := Cost(h)
|
||||
if err != nil {
|
||||
t.Errorf("Cost, error: %s", err)
|
||||
continue
|
||||
}
|
||||
if actual != cost {
|
||||
t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := Cost([]byte("$a$a$" + suffix))
|
||||
if err == nil {
|
||||
t.Errorf("Cost, malformed but no error returned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCostValidationInHash(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
pass := []byte("mypassword")
|
||||
|
||||
for c := 0; c < MinCost; c++ {
|
||||
p, _ := newFromPassword(pass, c)
|
||||
if p.cost != DefaultCost {
|
||||
t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
|
||||
}
|
||||
}
|
||||
|
||||
p, _ := newFromPassword(pass, 14)
|
||||
if p.cost != 14 {
|
||||
t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
|
||||
}
|
||||
|
||||
hp, _ := newFromHash(p.Hash())
|
||||
if p.cost != hp.cost {
|
||||
t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
|
||||
}
|
||||
|
||||
_, err := newFromPassword(pass, 32)
|
||||
if err == nil {
|
||||
t.Fatalf("newFromPassword: should return a cost error")
|
||||
}
|
||||
if err != InvalidCostError(32) {
|
||||
t.Errorf("newFromPassword: should return cost error, got %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCostReturnsWithLeadingZeroes(t *testing.T) {
|
||||
hp, _ := newFromPassword([]byte("abcdefgh"), 7)
|
||||
cost := hp.Hash()[4:7]
|
||||
expected := []byte("07$")
|
||||
|
||||
if !bytes.Equal(expected, cost) {
|
||||
t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinorNotRequired(t *testing.T) {
|
||||
noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
|
||||
h, err := newFromHash(noMinorHash)
|
||||
if err != nil {
|
||||
t.Fatalf("No minor hash blew up: %s", err)
|
||||
}
|
||||
if h.minor != 0 {
|
||||
t.Errorf("Should leave minor version at 0, but was %d", h.minor)
|
||||
}
|
||||
|
||||
if !bytes.Equal(noMinorHash, h.Hash()) {
|
||||
t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEqual(b *testing.B) {
|
||||
b.StopTimer()
|
||||
passwd := []byte("somepasswordyoulike")
|
||||
hash, _ := GenerateFromPassword(passwd, 10)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
CompareHashAndPassword(hash, passwd)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGeneration(b *testing.B) {
|
||||
b.StopTimer()
|
||||
passwd := []byte("mylongpassword1234")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
GenerateFromPassword(passwd, 10)
|
||||
}
|
||||
}
|
||||
4
Godeps/_workspace/src/code.google.com/p/log4go/.hgtags
сгенерированный
поставляемый
Обычный файл
4
Godeps/_workspace/src/code.google.com/p/log4go/.hgtags
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,4 @@
|
||||
4fbe6aadba231e838a449d340e43bdaab0bf85bd go.weekly.2012-02-07
|
||||
56168fd53249d639c25c74ced881fffb20d27be9 go.weekly.2012-02-22
|
||||
56168fd53249d639c25c74ced881fffb20d27be9 go.weekly.2012-02-22
|
||||
5c22fbd77d91f54d76cdbdee05318699754c44cc go.weekly.2012-02-22
|
||||
13
Godeps/_workspace/src/code.google.com/p/log4go/LICENSE
сгенерированный
поставляемый
Обычный файл
13
Godeps/_workspace/src/code.google.com/p/log4go/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,13 @@
|
||||
Copyright (c) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
12
Godeps/_workspace/src/code.google.com/p/log4go/README
сгенерированный
поставляемый
Обычный файл
12
Godeps/_workspace/src/code.google.com/p/log4go/README
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,12 @@
|
||||
Please see http://log4go.googlecode.com/
|
||||
|
||||
Installation:
|
||||
- Run `goinstall log4go.googlecode.com/hg`
|
||||
|
||||
Usage:
|
||||
- Add the following import:
|
||||
import l4g "log4go.googlecode.com/hg"
|
||||
|
||||
Acknowledgements:
|
||||
- pomack
|
||||
For providing awesome patches to bring log4go up to the latest Go spec
|
||||
288
Godeps/_workspace/src/code.google.com/p/log4go/config.go
сгенерированный
поставляемый
Обычный файл
288
Godeps/_workspace/src/code.google.com/p/log4go/config.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,288 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type xmlProperty struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type xmlFilter struct {
|
||||
Enabled string `xml:"enabled,attr"`
|
||||
Tag string `xml:"tag"`
|
||||
Level string `xml:"level"`
|
||||
Type string `xml:"type"`
|
||||
Property []xmlProperty `xml:"property"`
|
||||
}
|
||||
|
||||
type xmlLoggerConfig struct {
|
||||
Filter []xmlFilter `xml:"filter"`
|
||||
}
|
||||
|
||||
// Load XML configuration; see examples/example.xml for documentation
|
||||
func (log Logger) LoadConfiguration(filename string) {
|
||||
log.Close()
|
||||
|
||||
// Open the configuration file
|
||||
fd, err := os.Open(filename)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadAll(fd)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
xc := new(xmlLoggerConfig)
|
||||
if err := xml.Unmarshal(contents, xc); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, xmlfilt := range xc.Filter {
|
||||
var filt LogWriter
|
||||
var lvl level
|
||||
bad, good, enabled := false, true, false
|
||||
|
||||
// Check required children
|
||||
if len(xmlfilt.Enabled) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
|
||||
bad = true
|
||||
} else {
|
||||
enabled = xmlfilt.Enabled != "false"
|
||||
}
|
||||
if len(xmlfilt.Tag) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
|
||||
bad = true
|
||||
}
|
||||
if len(xmlfilt.Type) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
|
||||
bad = true
|
||||
}
|
||||
if len(xmlfilt.Level) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
|
||||
bad = true
|
||||
}
|
||||
|
||||
switch xmlfilt.Level {
|
||||
case "FINEST":
|
||||
lvl = FINEST
|
||||
case "FINE":
|
||||
lvl = FINE
|
||||
case "DEBUG":
|
||||
lvl = DEBUG
|
||||
case "TRACE":
|
||||
lvl = TRACE
|
||||
case "INFO":
|
||||
lvl = INFO
|
||||
case "WARNING":
|
||||
lvl = WARNING
|
||||
case "ERROR":
|
||||
lvl = ERROR
|
||||
case "CRITICAL":
|
||||
lvl = CRITICAL
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
|
||||
bad = true
|
||||
}
|
||||
|
||||
// Just so all of the required attributes are errored at the same time if missing
|
||||
if bad {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch xmlfilt.Type {
|
||||
case "console":
|
||||
filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "file":
|
||||
filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "xml":
|
||||
filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "socket":
|
||||
filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Just so all of the required params are errored at the same time if wrong
|
||||
if !good {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// If we're disabled (syntax and correctness checks only), don't add to logger
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
log[xmlfilt.Tag] = &Filter{lvl, filt}
|
||||
}
|
||||
}
|
||||
|
||||
func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (ConsoleLogWriter, bool) {
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
return NewConsoleLogWriter(), true
|
||||
}
|
||||
|
||||
// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
|
||||
func strToNumSuffix(str string, mult int) int {
|
||||
num := 1
|
||||
if len(str) > 1 {
|
||||
switch str[len(str)-1] {
|
||||
case 'G', 'g':
|
||||
num *= mult
|
||||
fallthrough
|
||||
case 'M', 'm':
|
||||
num *= mult
|
||||
fallthrough
|
||||
case 'K', 'k':
|
||||
num *= mult
|
||||
str = str[0 : len(str)-1]
|
||||
}
|
||||
}
|
||||
parsed, _ := strconv.Atoi(str)
|
||||
return parsed * num
|
||||
}
|
||||
func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
|
||||
file := ""
|
||||
format := "[%D %T] [%L] (%S) %M"
|
||||
maxlines := 0
|
||||
maxsize := 0
|
||||
daily := false
|
||||
rotate := false
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "filename":
|
||||
file = strings.Trim(prop.Value, " \r\n")
|
||||
case "format":
|
||||
format = strings.Trim(prop.Value, " \r\n")
|
||||
case "maxlines":
|
||||
maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
|
||||
case "maxsize":
|
||||
maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
|
||||
case "daily":
|
||||
daily = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
case "rotate":
|
||||
rotate = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(file) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
flw := NewFileLogWriter(file, rotate)
|
||||
flw.SetFormat(format)
|
||||
flw.SetRotateLines(maxlines)
|
||||
flw.SetRotateSize(maxsize)
|
||||
flw.SetRotateDaily(daily)
|
||||
return flw, true
|
||||
}
|
||||
|
||||
func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
|
||||
file := ""
|
||||
maxrecords := 0
|
||||
maxsize := 0
|
||||
daily := false
|
||||
rotate := false
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "filename":
|
||||
file = strings.Trim(prop.Value, " \r\n")
|
||||
case "maxrecords":
|
||||
maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
|
||||
case "maxsize":
|
||||
maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
|
||||
case "daily":
|
||||
daily = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
case "rotate":
|
||||
rotate = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(file) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
xlw := NewXMLLogWriter(file, rotate)
|
||||
xlw.SetRotateLines(maxrecords)
|
||||
xlw.SetRotateSize(maxsize)
|
||||
xlw.SetRotateDaily(daily)
|
||||
return xlw, true
|
||||
}
|
||||
|
||||
func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
|
||||
endpoint := ""
|
||||
protocol := "udp"
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "endpoint":
|
||||
endpoint = strings.Trim(prop.Value, " \r\n")
|
||||
case "protocol":
|
||||
protocol = strings.Trim(prop.Value, " \r\n")
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(endpoint) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
return NewSocketLogWriter(protocol, endpoint), true
|
||||
}
|
||||
13
Godeps/_workspace/src/code.google.com/p/log4go/examples/ConsoleLogWriter_Manual.go
сгенерированный
поставляемый
Обычный файл
13
Godeps/_workspace/src/code.google.com/p/log4go/examples/ConsoleLogWriter_Manual.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
import l4g "code.google.com/p/log4go"
|
||||
|
||||
func main() {
|
||||
log := l4g.NewLogger()
|
||||
log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter())
|
||||
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||
}
|
||||
57
Godeps/_workspace/src/code.google.com/p/log4go/examples/FileLogWriter_Manual.go
сгенерированный
поставляемый
Обычный файл
57
Godeps/_workspace/src/code.google.com/p/log4go/examples/FileLogWriter_Manual.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
import l4g "code.google.com/p/log4go"
|
||||
|
||||
const (
|
||||
filename = "flw.log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get a new logger instance
|
||||
log := l4g.NewLogger()
|
||||
|
||||
// Create a default logger that is logging messages of FINE or higher
|
||||
log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false))
|
||||
log.Close()
|
||||
|
||||
/* Can also specify manually via the following: (these are the defaults) */
|
||||
flw := l4g.NewFileLogWriter(filename, false)
|
||||
flw.SetFormat("[%D %T] [%L] (%S) %M")
|
||||
flw.SetRotate(false)
|
||||
flw.SetRotateSize(0)
|
||||
flw.SetRotateLines(0)
|
||||
flw.SetRotateDaily(false)
|
||||
log.AddFilter("file", l4g.FINE, flw)
|
||||
|
||||
// Log some experimental messages
|
||||
log.Finest("Everything is created now (notice that I will not be printing to the file)")
|
||||
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||
log.Critical("Time to close out!")
|
||||
|
||||
// Close the log
|
||||
log.Close()
|
||||
|
||||
// Print what was logged to the file (yes, I know I'm skipping error checking)
|
||||
fd, _ := os.Open(filename)
|
||||
in := bufio.NewReader(fd)
|
||||
fmt.Print("Messages logged to file were: (line numbers not included)\n")
|
||||
for lineno := 1; ; lineno++ {
|
||||
line, err := in.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Printf("%3d:\t%s", lineno, line)
|
||||
}
|
||||
fd.Close()
|
||||
|
||||
// Remove the file so it's not lying around
|
||||
os.Remove(filename)
|
||||
}
|
||||
42
Godeps/_workspace/src/code.google.com/p/log4go/examples/SimpleNetLogServer.go
сгенерированный
поставляемый
Обычный файл
42
Godeps/_workspace/src/code.google.com/p/log4go/examples/SimpleNetLogServer.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
port = flag.String("p", "12124", "Port number to listen on")
|
||||
)
|
||||
|
||||
func e(err error) {
|
||||
if err != nil {
|
||||
fmt.Printf("Erroring out: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// Bind to the port
|
||||
bind, err := net.ResolveUDPAddr("0.0.0.0:" + *port)
|
||||
e(err)
|
||||
|
||||
// Create listener
|
||||
listener, err := net.ListenUDP("udp", bind)
|
||||
e(err)
|
||||
|
||||
fmt.Printf("Listening to port %s...\n", *port)
|
||||
for {
|
||||
// read into a new buffer
|
||||
buffer := make([]byte, 1024)
|
||||
_, _, err := listener.ReadFrom(buffer)
|
||||
e(err)
|
||||
|
||||
// log to standard output
|
||||
fmt.Println(string(buffer))
|
||||
}
|
||||
}
|
||||
18
Godeps/_workspace/src/code.google.com/p/log4go/examples/SocketLogWriter_Manual.go
сгенерированный
поставляемый
Обычный файл
18
Godeps/_workspace/src/code.google.com/p/log4go/examples/SocketLogWriter_Manual.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
import l4g "code.google.com/p/log4go"
|
||||
|
||||
func main() {
|
||||
log := l4g.NewLogger()
|
||||
log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124"))
|
||||
|
||||
// Run `nc -u -l -p 12124` or similar before you run this to see the following message
|
||||
log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02"))
|
||||
|
||||
// This makes sure the output stream buffer is written
|
||||
log.Close()
|
||||
}
|
||||
13
Godeps/_workspace/src/code.google.com/p/log4go/examples/XMLConfigurationExample.go
сгенерированный
поставляемый
Обычный файл
13
Godeps/_workspace/src/code.google.com/p/log4go/examples/XMLConfigurationExample.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import l4g "code.google.com/p/log4go"
|
||||
|
||||
func main() {
|
||||
// Load the configuration (isn't this easy?)
|
||||
l4g.LoadConfiguration("example.xml")
|
||||
|
||||
// And now we're ready!
|
||||
l4g.Finest("This will only go to those of you really cool UDP kids! If you change enabled=true.")
|
||||
l4g.Debug("Oh no! %d + %d = %d!", 2, 2, 2+2)
|
||||
l4g.Info("About that time, eh chaps?")
|
||||
}
|
||||
47
Godeps/_workspace/src/code.google.com/p/log4go/examples/example.xml
сгенерированный
поставляемый
Обычный файл
47
Godeps/_workspace/src/code.google.com/p/log4go/examples/example.xml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,47 @@
|
||||
<logging>
|
||||
<filter enabled="true">
|
||||
<tag>stdout</tag>
|
||||
<type>console</type>
|
||||
<!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
|
||||
<level>DEBUG</level>
|
||||
</filter>
|
||||
<filter enabled="true">
|
||||
<tag>file</tag>
|
||||
<type>file</type>
|
||||
<level>FINEST</level>
|
||||
<property name="filename">test.log</property>
|
||||
<!--
|
||||
%T - Time (15:04:05 MST)
|
||||
%t - Time (15:04)
|
||||
%D - Date (2006/01/02)
|
||||
%d - Date (01/02/06)
|
||||
%L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
|
||||
%S - Source
|
||||
%M - Message
|
||||
It ignores unknown format strings (and removes them)
|
||||
Recommended: "[%D %T] [%L] (%S) %M"
|
||||
-->
|
||||
<property name="format">[%D %T] [%L] (%S) %M</property>
|
||||
<property name="rotate">false</property> <!-- true enables log rotation, otherwise append -->
|
||||
<property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
|
||||
<property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
|
||||
<property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
|
||||
</filter>
|
||||
<filter enabled="true">
|
||||
<tag>xmllog</tag>
|
||||
<type>xml</type>
|
||||
<level>TRACE</level>
|
||||
<property name="filename">trace.xml</property>
|
||||
<property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
|
||||
<property name="maxsize">100M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
|
||||
<property name="maxrecords">6K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
|
||||
<property name="daily">false</property> <!-- Automatically rotates when a log message is written after midnight -->
|
||||
</filter>
|
||||
<filter enabled="false"><!-- enabled=false means this logger won't actually be created -->
|
||||
<tag>donotopen</tag>
|
||||
<type>socket</type>
|
||||
<level>FINEST</level>
|
||||
<property name="endpoint">192.168.1.255:12124</property> <!-- recommend UDP broadcast -->
|
||||
<property name="protocol">udp</property> <!-- tcp or udp -->
|
||||
</filter>
|
||||
</logging>
|
||||
239
Godeps/_workspace/src/code.google.com/p/log4go/filelog.go
сгенерированный
поставляемый
Обычный файл
239
Godeps/_workspace/src/code.google.com/p/log4go/filelog.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,239 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This log writer sends output to a file
|
||||
type FileLogWriter struct {
|
||||
rec chan *LogRecord
|
||||
rot chan bool
|
||||
|
||||
// The opened file
|
||||
filename string
|
||||
file *os.File
|
||||
|
||||
// The logging format
|
||||
format string
|
||||
|
||||
// File header/trailer
|
||||
header, trailer string
|
||||
|
||||
// Rotate at linecount
|
||||
maxlines int
|
||||
maxlines_curlines int
|
||||
|
||||
// Rotate at size
|
||||
maxsize int
|
||||
maxsize_cursize int
|
||||
|
||||
// Rotate daily
|
||||
daily bool
|
||||
daily_opendate int
|
||||
|
||||
// Keep old logfiles (.001, .002, etc)
|
||||
rotate bool
|
||||
}
|
||||
|
||||
// This is the FileLogWriter's output method
|
||||
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
|
||||
w.rec <- rec
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) Close() {
|
||||
close(w.rec)
|
||||
}
|
||||
|
||||
// NewFileLogWriter creates a new LogWriter which writes to the given file and
|
||||
// has rotation enabled if rotate is true.
|
||||
//
|
||||
// If rotate is true, any time a new log file is opened, the old one is renamed
|
||||
// with a .### extension to preserve it. The various Set* methods can be used
|
||||
// to configure log rotation based on lines, size, and daily.
|
||||
//
|
||||
// The standard log-line format is:
|
||||
// [%D %T] [%L] (%S) %M
|
||||
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
|
||||
w := &FileLogWriter{
|
||||
rec: make(chan *LogRecord, LogBufferLength),
|
||||
rot: make(chan bool),
|
||||
filename: fname,
|
||||
format: "[%D %T] [%L] (%S) %M",
|
||||
rotate: rotate,
|
||||
}
|
||||
|
||||
// open the file for the first time
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if w.file != nil {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
|
||||
w.file.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-w.rot:
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
case rec, ok := <-w.rec:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
|
||||
(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
|
||||
(w.daily && now.Day() != w.daily_opendate) {
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the write
|
||||
n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the counts
|
||||
w.maxlines_curlines++
|
||||
w.maxsize_cursize += n
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
// Request that the logs rotate
|
||||
func (w *FileLogWriter) Rotate() {
|
||||
w.rot <- true
|
||||
}
|
||||
|
||||
// If this is called in a threaded context, it MUST be synchronized
|
||||
func (w *FileLogWriter) intRotate() error {
|
||||
// Close any log file that may be open
|
||||
if w.file != nil {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
|
||||
w.file.Close()
|
||||
}
|
||||
|
||||
// If we are keeping log files, move it to the next available number
|
||||
if w.rotate {
|
||||
_, err := os.Lstat(w.filename)
|
||||
if err == nil { // file exists
|
||||
// Find the next available number
|
||||
num := 1
|
||||
fname := ""
|
||||
for ; err == nil && num <= 999; num++ {
|
||||
fname = w.filename + fmt.Sprintf(".%03d", num)
|
||||
_, err = os.Lstat(fname)
|
||||
}
|
||||
// return error if the last file checked still existed
|
||||
if err == nil {
|
||||
return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
|
||||
}
|
||||
|
||||
// Rename the file to its newfound home
|
||||
err = os.Rename(w.filename, fname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Rotate: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the log file
|
||||
fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.file = fd
|
||||
|
||||
now := time.Now()
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))
|
||||
|
||||
// Set the daily open date to the current date
|
||||
w.daily_opendate = now.Day()
|
||||
|
||||
// initialize rotation values
|
||||
w.maxlines_curlines = 0
|
||||
w.maxsize_cursize = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the logging format (chainable). Must be called before the first log
|
||||
// message is written.
|
||||
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
|
||||
w.format = format
|
||||
return w
|
||||
}
|
||||
|
||||
// Set the logfile header and footer (chainable). Must be called before the first log
|
||||
// message is written. These are formatted similar to the FormatLogRecord (e.g.
|
||||
// you can use %D and %T in your header/footer for date and time).
|
||||
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
|
||||
w.header, w.trailer = head, foot
|
||||
if w.maxlines_curlines == 0 {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate at linecount (chainable). Must be called before the first log
|
||||
// message is written.
|
||||
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
|
||||
w.maxlines = maxlines
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate at size (chainable). Must be called before the first log message
|
||||
// is written.
|
||||
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
|
||||
w.maxsize = maxsize
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate daily (chainable). Must be called before the first log message is
|
||||
// written.
|
||||
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
|
||||
w.daily = daily
|
||||
return w
|
||||
}
|
||||
|
||||
// SetRotate changes whether or not the old logs are kept. (chainable) Must be
|
||||
// called before the first log message is written. If rotate is false, the
|
||||
// files are overwritten; otherwise, they are rotated to another file before the
|
||||
// new log is opened.
|
||||
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
|
||||
w.rotate = rotate
|
||||
return w
|
||||
}
|
||||
|
||||
// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
|
||||
// output XML record log messages instead of line-based ones.
|
||||
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
|
||||
return NewFileLogWriter(fname, rotate).SetFormat(
|
||||
` <record level="%L">
|
||||
<timestamp>%D %T</timestamp>
|
||||
<source>%S</source>
|
||||
<message>%M</message>
|
||||
</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
|
||||
}
|
||||
484
Godeps/_workspace/src/code.google.com/p/log4go/log4go.go
сгенерированный
поставляемый
Обычный файл
484
Godeps/_workspace/src/code.google.com/p/log4go/log4go.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,484 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
// Package log4go provides level-based and highly configurable logging.
|
||||
//
|
||||
// Enhanced Logging
|
||||
//
|
||||
// This is inspired by the logging functionality in Java. Essentially, you create a Logger
|
||||
// object and create output filters for it. You can send whatever you want to the Logger,
|
||||
// and it will filter that based on your settings and send it to the outputs. This way, you
|
||||
// can put as much debug code in your program as you want, and when you're done you can filter
|
||||
// out the mundane messages so only the important ones show up.
|
||||
//
|
||||
// Utility functions are provided to make life easier. Here is some example code to get started:
|
||||
//
|
||||
// log := log4go.NewLogger()
|
||||
// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter())
|
||||
// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
|
||||
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
|
||||
//
|
||||
// The first two lines can be combined with the utility NewDefaultLogger:
|
||||
//
|
||||
// log := log4go.NewDefaultLogger(log4go.DEBUG)
|
||||
// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
|
||||
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
|
||||
//
|
||||
// Usage notes:
|
||||
// - The ConsoleLogWriter does not display the source of the message to standard
|
||||
// output, but the FileLogWriter does.
|
||||
// - The utility functions (Info, Debug, Warn, etc) derive their source from the
|
||||
// calling function, and this incurs extra overhead.
|
||||
//
|
||||
// Changes from 2.0:
|
||||
// - The external interface has remained mostly stable, but a lot of the
|
||||
// internals have been changed, so if you depended on any of this or created
|
||||
// your own LogWriter, then you will probably have to update your code. In
|
||||
// particular, Logger is now a map and ConsoleLogWriter is now a channel
|
||||
// behind-the-scenes, and the LogWrite method no longer has return values.
|
||||
//
|
||||
// Future work: (please let me know if you think I should work on any of these particularly)
|
||||
// - Log file rotation
|
||||
// - Logging configuration files ala log4j
|
||||
// - Have the ability to remove filters?
|
||||
// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows
|
||||
// for another method of logging
|
||||
// - Add an XML filter type
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"fmt"
|
||||
"time"
|
||||
"strings"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Version information
|
||||
const (
|
||||
L4G_VERSION = "log4go-v3.0.1"
|
||||
L4G_MAJOR = 3
|
||||
L4G_MINOR = 0
|
||||
L4G_BUILD = 1
|
||||
)
|
||||
|
||||
/****** Constants ******/
|
||||
|
||||
// These are the integer logging levels used by the logger
|
||||
type level int
|
||||
|
||||
const (
|
||||
FINEST level = iota
|
||||
FINE
|
||||
DEBUG
|
||||
TRACE
|
||||
INFO
|
||||
WARNING
|
||||
ERROR
|
||||
CRITICAL
|
||||
)
|
||||
|
||||
// Logging level strings
|
||||
var (
|
||||
levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
|
||||
)
|
||||
|
||||
func (l level) String() string {
|
||||
if l < 0 || int(l) > len(levelStrings) {
|
||||
return "UNKNOWN"
|
||||
}
|
||||
return levelStrings[int(l)]
|
||||
}
|
||||
|
||||
/****** Variables ******/
|
||||
var (
|
||||
// LogBufferLength specifies how many log messages a particular log4go
|
||||
// logger can buffer at a time before writing them.
|
||||
LogBufferLength = 32
|
||||
)
|
||||
|
||||
/****** LogRecord ******/
|
||||
|
||||
// A LogRecord contains all of the pertinent information for each message
|
||||
type LogRecord struct {
|
||||
Level level // The log level
|
||||
Created time.Time // The time at which the log message was created (nanoseconds)
|
||||
Source string // The message source
|
||||
Message string // The log message
|
||||
}
|
||||
|
||||
/****** LogWriter ******/
|
||||
|
||||
// This is an interface for anything that should be able to write logs
|
||||
type LogWriter interface {
|
||||
// This will be called to log a LogRecord message.
|
||||
LogWrite(rec *LogRecord)
|
||||
|
||||
// This should clean up anything lingering about the LogWriter, as it is called before
|
||||
// the LogWriter is removed. LogWrite should not be called after Close.
|
||||
Close()
|
||||
}
|
||||
|
||||
/****** Logger ******/
|
||||
|
||||
// A Filter represents the log level below which no log records are written to
|
||||
// the associated LogWriter.
|
||||
type Filter struct {
|
||||
Level level
|
||||
LogWriter
|
||||
}
|
||||
|
||||
// A Logger represents a collection of Filters through which log messages are
|
||||
// written.
|
||||
type Logger map[string]*Filter
|
||||
|
||||
// Create a new logger.
|
||||
//
|
||||
// DEPRECATED: Use make(Logger) instead.
|
||||
func NewLogger() Logger {
|
||||
os.Stderr.WriteString("warning: use of deprecated NewLogger\n")
|
||||
return make(Logger)
|
||||
}
|
||||
|
||||
// Create a new logger with a "stdout" filter configured to send log messages at
|
||||
// or above lvl to standard output.
|
||||
//
|
||||
// DEPRECATED: use NewDefaultLogger instead.
|
||||
func NewConsoleLogger(lvl level) Logger {
|
||||
os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n")
|
||||
return Logger{
|
||||
"stdout": &Filter{lvl, NewConsoleLogWriter()},
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new logger with a "stdout" filter configured to send log messages at
|
||||
// or above lvl to standard output.
|
||||
func NewDefaultLogger(lvl level) Logger {
|
||||
return Logger{
|
||||
"stdout": &Filter{lvl, NewConsoleLogWriter()},
|
||||
}
|
||||
}
|
||||
|
||||
// Closes all log writers in preparation for exiting the program or a
|
||||
// reconfiguration of logging. Calling this is not really imperative, unless
|
||||
// you want to guarantee that all log messages are written. Close removes
|
||||
// all filters (and thus all LogWriters) from the logger.
|
||||
func (log Logger) Close() {
|
||||
// Close all open loggers
|
||||
for name, filt := range log {
|
||||
filt.Close()
|
||||
delete(log, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new LogWriter to the Logger which will only log messages at lvl or
|
||||
// higher. This function should not be called from multiple goroutines.
|
||||
// Returns the logger for chaining.
|
||||
func (log Logger) AddFilter(name string, lvl level, writer LogWriter) Logger {
|
||||
log[name] = &Filter{lvl, writer}
|
||||
return log
|
||||
}
|
||||
|
||||
/******* Logging *******/
|
||||
// Send a formatted log message internally
|
||||
func (log Logger) intLogf(lvl level, format string, args ...interface{}) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine caller func
|
||||
pc, _, lineno, ok := runtime.Caller(2)
|
||||
src := ""
|
||||
if ok {
|
||||
src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
|
||||
}
|
||||
|
||||
msg := format
|
||||
if len(args) > 0 {
|
||||
msg = fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: src,
|
||||
Message: msg,
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Send a closure log message internally
|
||||
func (log Logger) intLogc(lvl level, closure func() string) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine caller func
|
||||
pc, _, lineno, ok := runtime.Caller(2)
|
||||
src := ""
|
||||
if ok {
|
||||
src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: src,
|
||||
Message: closure(),
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Send a log message with manual level, source, and message.
|
||||
func (log Logger) Log(lvl level, source, message string) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: source,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Logf logs a formatted log message at the given log level, using the caller as
|
||||
// its source.
|
||||
func (log Logger) Logf(lvl level, format string, args ...interface{}) {
|
||||
log.intLogf(lvl, format, args...)
|
||||
}
|
||||
|
||||
// Logc logs a string returned by the closure at the given log level, using the caller as
|
||||
// its source. If no log message would be written, the closure is never called.
|
||||
func (log Logger) Logc(lvl level, closure func() string) {
|
||||
log.intLogc(lvl, closure)
|
||||
}
|
||||
|
||||
// Finest logs a message at the finest log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Finest(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINEST
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fine logs a message at the fine log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Fine(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug is a utility method for debug log messages.
|
||||
// The behavior of Debug depends on the first argument:
|
||||
// - arg0 is a string
|
||||
// When given a string as the first argument, this behaves like Logf but with
|
||||
// the DEBUG log level: the first argument is interpreted as a format for the
|
||||
// latter arguments.
|
||||
// - arg0 is a func()string
|
||||
// When given a closure of type func()string, this logs the string returned by
|
||||
// the closure iff it will be logged. The closure runs at most one time.
|
||||
// - arg0 is interface{}
|
||||
// When given anything else, the log message will be each of the arguments
|
||||
// formatted with %v and separated by spaces (ala Sprint).
|
||||
func (log Logger) Debug(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = DEBUG
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace logs a message at the trace log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Trace(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = TRACE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info logs a message at the info log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Info(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = INFO
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn logs a message at the warning log level and returns the formatted error.
|
||||
// At the warning level and higher, there is no performance benefit if the
|
||||
// message is not actually logged, because all formats are processed and all
|
||||
// closures are executed to format the error message.
|
||||
// See Debug for further explanation of the arguments.
|
||||
func (log Logger) Warn(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = WARNING
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
// Error logs a message at the error log level and returns the formatted error,
|
||||
// See Warn for an explanation of the performance and Debug for an explanation
|
||||
// of the parameters.
|
||||
func (log Logger) Error(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = ERROR
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
// Critical logs a message at the critical log level and returns the formatted error,
|
||||
// See Warn for an explanation of the performance and Debug for an explanation
|
||||
// of the parameters.
|
||||
func (log Logger) Critical(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = CRITICAL
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
534
Godeps/_workspace/src/code.google.com/p/log4go/log4go_test.go
сгенерированный
поставляемый
Обычный файл
534
Godeps/_workspace/src/code.google.com/p/log4go/log4go_test.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,534 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const testLogFile = "_logtest.log"
|
||||
|
||||
var now time.Time = time.Unix(0, 1234567890123456789).In(time.UTC)
|
||||
|
||||
func newLogRecord(lvl level, src string, msg string) *LogRecord {
|
||||
return &LogRecord{
|
||||
Level: lvl,
|
||||
Source: src,
|
||||
Created: now,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func TestELog(t *testing.T) {
|
||||
fmt.Printf("Testing %s\n", L4G_VERSION)
|
||||
lr := newLogRecord(CRITICAL, "source", "message")
|
||||
if lr.Level != CRITICAL {
|
||||
t.Errorf("Incorrect level: %d should be %d", lr.Level, CRITICAL)
|
||||
}
|
||||
if lr.Source != "source" {
|
||||
t.Errorf("Incorrect source: %s should be %s", lr.Source, "source")
|
||||
}
|
||||
if lr.Message != "message" {
|
||||
t.Errorf("Incorrect message: %s should be %s", lr.Source, "message")
|
||||
}
|
||||
}
|
||||
|
||||
var formatTests = []struct {
|
||||
Test string
|
||||
Record *LogRecord
|
||||
Formats map[string]string
|
||||
}{
|
||||
{
|
||||
Test: "Standard formats",
|
||||
Record: &LogRecord{
|
||||
Level: ERROR,
|
||||
Source: "source",
|
||||
Message: "message",
|
||||
Created: now,
|
||||
},
|
||||
Formats: map[string]string{
|
||||
// TODO(kevlar): How can I do this so it'll work outside of PST?
|
||||
FORMAT_DEFAULT: "[2009/02/13 23:31:30 UTC] [EROR] (source) message\n",
|
||||
FORMAT_SHORT: "[23:31 02/13/09] [EROR] message\n",
|
||||
FORMAT_ABBREV: "[EROR] message\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestFormatLogRecord(t *testing.T) {
|
||||
for _, test := range formatTests {
|
||||
name := test.Test
|
||||
for fmt, want := range test.Formats {
|
||||
if got := FormatLogRecord(fmt, test.Record); got != want {
|
||||
t.Errorf("%s - %s:", name, fmt)
|
||||
t.Errorf(" got %q", got)
|
||||
t.Errorf(" want %q", want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var logRecordWriteTests = []struct {
|
||||
Test string
|
||||
Record *LogRecord
|
||||
Console string
|
||||
}{
|
||||
{
|
||||
Test: "Normal message",
|
||||
Record: &LogRecord{
|
||||
Level: CRITICAL,
|
||||
Source: "source",
|
||||
Message: "message",
|
||||
Created: now,
|
||||
},
|
||||
Console: "[02/13/09 23:31:30] [CRIT] message\n",
|
||||
},
|
||||
}
|
||||
|
||||
func TestConsoleLogWriter(t *testing.T) {
|
||||
console := make(ConsoleLogWriter)
|
||||
|
||||
r, w := io.Pipe()
|
||||
go console.run(w)
|
||||
defer console.Close()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
|
||||
for _, test := range logRecordWriteTests {
|
||||
name := test.Test
|
||||
|
||||
console.LogWrite(test.Record)
|
||||
n, _ := r.Read(buf)
|
||||
|
||||
if got, want := string(buf[:n]), test.Console; got != want {
|
||||
t.Errorf("%s: got %q", name, got)
|
||||
t.Errorf("%s: want %q", name, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileLogWriter(t *testing.T) {
|
||||
defer func(buflen int) {
|
||||
LogBufferLength = buflen
|
||||
}(LogBufferLength)
|
||||
LogBufferLength = 0
|
||||
|
||||
w := NewFileLogWriter(testLogFile, false)
|
||||
if w == nil {
|
||||
t.Fatalf("Invalid return: w should not be nil")
|
||||
}
|
||||
defer os.Remove(testLogFile)
|
||||
|
||||
w.LogWrite(newLogRecord(CRITICAL, "source", "message"))
|
||||
w.Close()
|
||||
runtime.Gosched()
|
||||
|
||||
if contents, err := ioutil.ReadFile(testLogFile); err != nil {
|
||||
t.Errorf("read(%q): %s", testLogFile, err)
|
||||
} else if len(contents) != 50 {
|
||||
t.Errorf("malformed filelog: %q (%d bytes)", string(contents), len(contents))
|
||||
}
|
||||
}
|
||||
|
||||
func TestXMLLogWriter(t *testing.T) {
|
||||
defer func(buflen int) {
|
||||
LogBufferLength = buflen
|
||||
}(LogBufferLength)
|
||||
LogBufferLength = 0
|
||||
|
||||
w := NewXMLLogWriter(testLogFile, false)
|
||||
if w == nil {
|
||||
t.Fatalf("Invalid return: w should not be nil")
|
||||
}
|
||||
defer os.Remove(testLogFile)
|
||||
|
||||
w.LogWrite(newLogRecord(CRITICAL, "source", "message"))
|
||||
w.Close()
|
||||
runtime.Gosched()
|
||||
|
||||
if contents, err := ioutil.ReadFile(testLogFile); err != nil {
|
||||
t.Errorf("read(%q): %s", testLogFile, err)
|
||||
} else if len(contents) != 185 {
|
||||
t.Errorf("malformed xmllog: %q (%d bytes)", string(contents), len(contents))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
sl := NewDefaultLogger(WARNING)
|
||||
if sl == nil {
|
||||
t.Fatalf("NewDefaultLogger should never return nil")
|
||||
}
|
||||
if lw, exist := sl["stdout"]; lw == nil || exist != true {
|
||||
t.Fatalf("NewDefaultLogger produced invalid logger (DNE or nil)")
|
||||
}
|
||||
if sl["stdout"].Level != WARNING {
|
||||
t.Fatalf("NewDefaultLogger produced invalid logger (incorrect level)")
|
||||
}
|
||||
if len(sl) != 1 {
|
||||
t.Fatalf("NewDefaultLogger produced invalid logger (incorrect map count)")
|
||||
}
|
||||
|
||||
//func (l *Logger) AddFilter(name string, level int, writer LogWriter) {}
|
||||
l := make(Logger)
|
||||
l.AddFilter("stdout", DEBUG, NewConsoleLogWriter())
|
||||
if lw, exist := l["stdout"]; lw == nil || exist != true {
|
||||
t.Fatalf("AddFilter produced invalid logger (DNE or nil)")
|
||||
}
|
||||
if l["stdout"].Level != DEBUG {
|
||||
t.Fatalf("AddFilter produced invalid logger (incorrect level)")
|
||||
}
|
||||
if len(l) != 1 {
|
||||
t.Fatalf("AddFilter produced invalid logger (incorrect map count)")
|
||||
}
|
||||
|
||||
//func (l *Logger) Warn(format string, args ...interface{}) error {}
|
||||
if err := l.Warn("%s %d %#v", "Warning:", 1, []int{}); err.Error() != "Warning: 1 []int{}" {
|
||||
t.Errorf("Warn returned invalid error: %s", err)
|
||||
}
|
||||
|
||||
//func (l *Logger) Error(format string, args ...interface{}) error {}
|
||||
if err := l.Error("%s %d %#v", "Error:", 10, []string{}); err.Error() != "Error: 10 []string{}" {
|
||||
t.Errorf("Error returned invalid error: %s", err)
|
||||
}
|
||||
|
||||
//func (l *Logger) Critical(format string, args ...interface{}) error {}
|
||||
if err := l.Critical("%s %d %#v", "Critical:", 100, []int64{}); err.Error() != "Critical: 100 []int64{}" {
|
||||
t.Errorf("Critical returned invalid error: %s", err)
|
||||
}
|
||||
|
||||
// Already tested or basically untestable
|
||||
//func (l *Logger) Log(level int, source, message string) {}
|
||||
//func (l *Logger) Logf(level int, format string, args ...interface{}) {}
|
||||
//func (l *Logger) intLogf(level int, format string, args ...interface{}) string {}
|
||||
//func (l *Logger) Finest(format string, args ...interface{}) {}
|
||||
//func (l *Logger) Fine(format string, args ...interface{}) {}
|
||||
//func (l *Logger) Debug(format string, args ...interface{}) {}
|
||||
//func (l *Logger) Trace(format string, args ...interface{}) {}
|
||||
//func (l *Logger) Info(format string, args ...interface{}) {}
|
||||
}
|
||||
|
||||
func TestLogOutput(t *testing.T) {
|
||||
const (
|
||||
expected = "fdf3e51e444da56b4cb400f30bc47424"
|
||||
)
|
||||
|
||||
// Unbuffered output
|
||||
defer func(buflen int) {
|
||||
LogBufferLength = buflen
|
||||
}(LogBufferLength)
|
||||
LogBufferLength = 0
|
||||
|
||||
l := make(Logger)
|
||||
|
||||
// Delete and open the output log without a timestamp (for a constant md5sum)
|
||||
l.AddFilter("file", FINEST, NewFileLogWriter(testLogFile, false).SetFormat("[%L] %M"))
|
||||
defer os.Remove(testLogFile)
|
||||
|
||||
// Send some log messages
|
||||
l.Log(CRITICAL, "testsrc1", fmt.Sprintf("This message is level %d", int(CRITICAL)))
|
||||
l.Logf(ERROR, "This message is level %v", ERROR)
|
||||
l.Logf(WARNING, "This message is level %s", WARNING)
|
||||
l.Logc(INFO, func() string { return "This message is level INFO" })
|
||||
l.Trace("This message is level %d", int(TRACE))
|
||||
l.Debug("This message is level %s", DEBUG)
|
||||
l.Fine(func() string { return fmt.Sprintf("This message is level %v", FINE) })
|
||||
l.Finest("This message is level %v", FINEST)
|
||||
l.Finest(FINEST, "is also this message's level")
|
||||
|
||||
l.Close()
|
||||
|
||||
contents, err := ioutil.ReadFile(testLogFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not read output log: %s", err)
|
||||
}
|
||||
|
||||
sum := md5.New()
|
||||
sum.Write(contents)
|
||||
if sumstr := hex.EncodeToString(sum.Sum(nil)); sumstr != expected {
|
||||
t.Errorf("--- Log Contents:\n%s---", string(contents))
|
||||
t.Fatalf("Checksum does not match: %s (expecting %s)", sumstr, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountMallocs(t *testing.T) {
|
||||
const N = 1
|
||||
var m runtime.MemStats
|
||||
getMallocs := func() uint64 {
|
||||
runtime.ReadMemStats(&m)
|
||||
return m.Mallocs
|
||||
}
|
||||
|
||||
// Console logger
|
||||
sl := NewDefaultLogger(INFO)
|
||||
mallocs := 0 - getMallocs()
|
||||
for i := 0; i < N; i++ {
|
||||
sl.Log(WARNING, "here", "This is a WARNING message")
|
||||
}
|
||||
mallocs += getMallocs()
|
||||
fmt.Printf("mallocs per sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N)
|
||||
|
||||
// Console logger formatted
|
||||
mallocs = 0 - getMallocs()
|
||||
for i := 0; i < N; i++ {
|
||||
sl.Logf(WARNING, "%s is a log message with level %d", "This", WARNING)
|
||||
}
|
||||
mallocs += getMallocs()
|
||||
fmt.Printf("mallocs per sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N)
|
||||
|
||||
// Console logger (not logged)
|
||||
sl = NewDefaultLogger(INFO)
|
||||
mallocs = 0 - getMallocs()
|
||||
for i := 0; i < N; i++ {
|
||||
sl.Log(DEBUG, "here", "This is a DEBUG log message")
|
||||
}
|
||||
mallocs += getMallocs()
|
||||
fmt.Printf("mallocs per unlogged sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N)
|
||||
|
||||
// Console logger formatted (not logged)
|
||||
mallocs = 0 - getMallocs()
|
||||
for i := 0; i < N; i++ {
|
||||
sl.Logf(DEBUG, "%s is a log message with level %d", "This", DEBUG)
|
||||
}
|
||||
mallocs += getMallocs()
|
||||
fmt.Printf("mallocs per unlogged sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N)
|
||||
}
|
||||
|
||||
func TestXMLConfig(t *testing.T) {
|
||||
const (
|
||||
configfile = "example.xml"
|
||||
)
|
||||
|
||||
fd, err := os.Create(configfile)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not open %s for writing: %s", configfile, err)
|
||||
}
|
||||
|
||||
fmt.Fprintln(fd, "<logging>")
|
||||
fmt.Fprintln(fd, " <filter enabled=\"true\">")
|
||||
fmt.Fprintln(fd, " <tag>stdout</tag>")
|
||||
fmt.Fprintln(fd, " <type>console</type>")
|
||||
fmt.Fprintln(fd, " <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->")
|
||||
fmt.Fprintln(fd, " <level>DEBUG</level>")
|
||||
fmt.Fprintln(fd, " </filter>")
|
||||
fmt.Fprintln(fd, " <filter enabled=\"true\">")
|
||||
fmt.Fprintln(fd, " <tag>file</tag>")
|
||||
fmt.Fprintln(fd, " <type>file</type>")
|
||||
fmt.Fprintln(fd, " <level>FINEST</level>")
|
||||
fmt.Fprintln(fd, " <property name=\"filename\">test.log</property>")
|
||||
fmt.Fprintln(fd, " <!--")
|
||||
fmt.Fprintln(fd, " %T - Time (15:04:05 MST)")
|
||||
fmt.Fprintln(fd, " %t - Time (15:04)")
|
||||
fmt.Fprintln(fd, " %D - Date (2006/01/02)")
|
||||
fmt.Fprintln(fd, " %d - Date (01/02/06)")
|
||||
fmt.Fprintln(fd, " %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)")
|
||||
fmt.Fprintln(fd, " %S - Source")
|
||||
fmt.Fprintln(fd, " %M - Message")
|
||||
fmt.Fprintln(fd, " It ignores unknown format strings (and removes them)")
|
||||
fmt.Fprintln(fd, " Recommended: \"[%D %T] [%L] (%S) %M\"")
|
||||
fmt.Fprintln(fd, " -->")
|
||||
fmt.Fprintln(fd, " <property name=\"format\">[%D %T] [%L] (%S) %M</property>")
|
||||
fmt.Fprintln(fd, " <property name=\"rotate\">false</property> <!-- true enables log rotation, otherwise append -->")
|
||||
fmt.Fprintln(fd, " <property name=\"maxsize\">0M</property> <!-- \\d+[KMG]? Suffixes are in terms of 2**10 -->")
|
||||
fmt.Fprintln(fd, " <property name=\"maxlines\">0K</property> <!-- \\d+[KMG]? Suffixes are in terms of thousands -->")
|
||||
fmt.Fprintln(fd, " <property name=\"daily\">true</property> <!-- Automatically rotates when a log message is written after midnight -->")
|
||||
fmt.Fprintln(fd, " </filter>")
|
||||
fmt.Fprintln(fd, " <filter enabled=\"true\">")
|
||||
fmt.Fprintln(fd, " <tag>xmllog</tag>")
|
||||
fmt.Fprintln(fd, " <type>xml</type>")
|
||||
fmt.Fprintln(fd, " <level>TRACE</level>")
|
||||
fmt.Fprintln(fd, " <property name=\"filename\">trace.xml</property>")
|
||||
fmt.Fprintln(fd, " <property name=\"rotate\">true</property> <!-- true enables log rotation, otherwise append -->")
|
||||
fmt.Fprintln(fd, " <property name=\"maxsize\">100M</property> <!-- \\d+[KMG]? Suffixes are in terms of 2**10 -->")
|
||||
fmt.Fprintln(fd, " <property name=\"maxrecords\">6K</property> <!-- \\d+[KMG]? Suffixes are in terms of thousands -->")
|
||||
fmt.Fprintln(fd, " <property name=\"daily\">false</property> <!-- Automatically rotates when a log message is written after midnight -->")
|
||||
fmt.Fprintln(fd, " </filter>")
|
||||
fmt.Fprintln(fd, " <filter enabled=\"false\"><!-- enabled=false means this logger won't actually be created -->")
|
||||
fmt.Fprintln(fd, " <tag>donotopen</tag>")
|
||||
fmt.Fprintln(fd, " <type>socket</type>")
|
||||
fmt.Fprintln(fd, " <level>FINEST</level>")
|
||||
fmt.Fprintln(fd, " <property name=\"endpoint\">192.168.1.255:12124</property> <!-- recommend UDP broadcast -->")
|
||||
fmt.Fprintln(fd, " <property name=\"protocol\">udp</property> <!-- tcp or udp -->")
|
||||
fmt.Fprintln(fd, " </filter>")
|
||||
fmt.Fprintln(fd, "</logging>")
|
||||
fd.Close()
|
||||
|
||||
log := make(Logger)
|
||||
log.LoadConfiguration(configfile)
|
||||
defer os.Remove("trace.xml")
|
||||
defer os.Remove("test.log")
|
||||
defer log.Close()
|
||||
|
||||
// Make sure we got all loggers
|
||||
if len(log) != 3 {
|
||||
t.Fatalf("XMLConfig: Expected 3 filters, found %d", len(log))
|
||||
}
|
||||
|
||||
// Make sure they're the right keys
|
||||
if _, ok := log["stdout"]; !ok {
|
||||
t.Errorf("XMLConfig: Expected stdout logger")
|
||||
}
|
||||
if _, ok := log["file"]; !ok {
|
||||
t.Fatalf("XMLConfig: Expected file logger")
|
||||
}
|
||||
if _, ok := log["xmllog"]; !ok {
|
||||
t.Fatalf("XMLConfig: Expected xmllog logger")
|
||||
}
|
||||
|
||||
// Make sure they're the right type
|
||||
if _, ok := log["stdout"].LogWriter.(ConsoleLogWriter); !ok {
|
||||
t.Fatalf("XMLConfig: Expected stdout to be ConsoleLogWriter, found %T", log["stdout"].LogWriter)
|
||||
}
|
||||
if _, ok := log["file"].LogWriter.(*FileLogWriter); !ok {
|
||||
t.Fatalf("XMLConfig: Expected file to be *FileLogWriter, found %T", log["file"].LogWriter)
|
||||
}
|
||||
if _, ok := log["xmllog"].LogWriter.(*FileLogWriter); !ok {
|
||||
t.Fatalf("XMLConfig: Expected xmllog to be *FileLogWriter, found %T", log["xmllog"].LogWriter)
|
||||
}
|
||||
|
||||
// Make sure levels are set
|
||||
if lvl := log["stdout"].Level; lvl != DEBUG {
|
||||
t.Errorf("XMLConfig: Expected stdout to be set to level %d, found %d", DEBUG, lvl)
|
||||
}
|
||||
if lvl := log["file"].Level; lvl != FINEST {
|
||||
t.Errorf("XMLConfig: Expected file to be set to level %d, found %d", FINEST, lvl)
|
||||
}
|
||||
if lvl := log["xmllog"].Level; lvl != TRACE {
|
||||
t.Errorf("XMLConfig: Expected xmllog to be set to level %d, found %d", TRACE, lvl)
|
||||
}
|
||||
|
||||
// Make sure the w is open and points to the right file
|
||||
if fname := log["file"].LogWriter.(*FileLogWriter).file.Name(); fname != "test.log" {
|
||||
t.Errorf("XMLConfig: Expected file to have opened %s, found %s", "test.log", fname)
|
||||
}
|
||||
|
||||
// Make sure the XLW is open and points to the right file
|
||||
if fname := log["xmllog"].LogWriter.(*FileLogWriter).file.Name(); fname != "trace.xml" {
|
||||
t.Errorf("XMLConfig: Expected xmllog to have opened %s, found %s", "trace.xml", fname)
|
||||
}
|
||||
|
||||
// Move XML log file
|
||||
os.Rename(configfile, "examples/"+configfile) // Keep this so that an example with the documentation is available
|
||||
}
|
||||
|
||||
func BenchmarkFormatLogRecord(b *testing.B) {
|
||||
const updateEvery = 1
|
||||
rec := &LogRecord{
|
||||
Level: CRITICAL,
|
||||
Created: now,
|
||||
Source: "source",
|
||||
Message: "message",
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
rec.Created = rec.Created.Add(1 * time.Second / updateEvery)
|
||||
if i%2 == 0 {
|
||||
FormatLogRecord(FORMAT_DEFAULT, rec)
|
||||
} else {
|
||||
FormatLogRecord(FORMAT_SHORT, rec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConsoleLog(b *testing.B) {
|
||||
/* This doesn't seem to work on OS X
|
||||
sink, err := os.Open(os.DevNull)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := syscall.Dup2(int(sink.Fd()), syscall.Stdout); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*/
|
||||
|
||||
stdout = ioutil.Discard
|
||||
sl := NewDefaultLogger(INFO)
|
||||
for i := 0; i < b.N; i++ {
|
||||
sl.Log(WARNING, "here", "This is a log message")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConsoleNotLogged(b *testing.B) {
|
||||
sl := NewDefaultLogger(INFO)
|
||||
for i := 0; i < b.N; i++ {
|
||||
sl.Log(DEBUG, "here", "This is a log message")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConsoleUtilLog(b *testing.B) {
|
||||
sl := NewDefaultLogger(INFO)
|
||||
for i := 0; i < b.N; i++ {
|
||||
sl.Info("%s is a log message", "This")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConsoleUtilNotLog(b *testing.B) {
|
||||
sl := NewDefaultLogger(INFO)
|
||||
for i := 0; i < b.N; i++ {
|
||||
sl.Debug("%s is a log message", "This")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFileLog(b *testing.B) {
|
||||
sl := make(Logger)
|
||||
b.StopTimer()
|
||||
sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false))
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sl.Log(WARNING, "here", "This is a log message")
|
||||
}
|
||||
b.StopTimer()
|
||||
os.Remove("benchlog.log")
|
||||
}
|
||||
|
||||
func BenchmarkFileNotLogged(b *testing.B) {
|
||||
sl := make(Logger)
|
||||
b.StopTimer()
|
||||
sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false))
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sl.Log(DEBUG, "here", "This is a log message")
|
||||
}
|
||||
b.StopTimer()
|
||||
os.Remove("benchlog.log")
|
||||
}
|
||||
|
||||
func BenchmarkFileUtilLog(b *testing.B) {
|
||||
sl := make(Logger)
|
||||
b.StopTimer()
|
||||
sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false))
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sl.Info("%s is a log message", "This")
|
||||
}
|
||||
b.StopTimer()
|
||||
os.Remove("benchlog.log")
|
||||
}
|
||||
|
||||
func BenchmarkFileUtilNotLog(b *testing.B) {
|
||||
sl := make(Logger)
|
||||
b.StopTimer()
|
||||
sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false))
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sl.Debug("%s is a log message", "This")
|
||||
}
|
||||
b.StopTimer()
|
||||
os.Remove("benchlog.log")
|
||||
}
|
||||
|
||||
// Benchmark results (darwin amd64 6g)
|
||||
//elog.BenchmarkConsoleLog 100000 22819 ns/op
|
||||
//elog.BenchmarkConsoleNotLogged 2000000 879 ns/op
|
||||
//elog.BenchmarkConsoleUtilLog 50000 34380 ns/op
|
||||
//elog.BenchmarkConsoleUtilNotLog 1000000 1339 ns/op
|
||||
//elog.BenchmarkFileLog 100000 26497 ns/op
|
||||
//elog.BenchmarkFileNotLogged 2000000 821 ns/op
|
||||
//elog.BenchmarkFileUtilLog 50000 33945 ns/op
|
||||
//elog.BenchmarkFileUtilNotLog 1000000 1258 ns/op
|
||||
122
Godeps/_workspace/src/code.google.com/p/log4go/pattlog.go
сгенерированный
поставляемый
Обычный файл
122
Godeps/_workspace/src/code.google.com/p/log4go/pattlog.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,122 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M"
|
||||
FORMAT_SHORT = "[%t %d] [%L] %M"
|
||||
FORMAT_ABBREV = "[%L] %M"
|
||||
)
|
||||
|
||||
type formatCacheType struct {
|
||||
LastUpdateSeconds int64
|
||||
shortTime, shortDate string
|
||||
longTime, longDate string
|
||||
}
|
||||
|
||||
var formatCache = &formatCacheType{}
|
||||
|
||||
// Known format codes:
|
||||
// %T - Time (15:04:05 MST)
|
||||
// %t - Time (15:04)
|
||||
// %D - Date (2006/01/02)
|
||||
// %d - Date (01/02/06)
|
||||
// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
|
||||
// %S - Source
|
||||
// %M - Message
|
||||
// Ignores unknown formats
|
||||
// Recommended: "[%D %T] [%L] (%S) %M"
|
||||
func FormatLogRecord(format string, rec *LogRecord) string {
|
||||
if rec == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
if len(format) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(make([]byte, 0, 64))
|
||||
secs := rec.Created.UnixNano() / 1e9
|
||||
|
||||
cache := *formatCache
|
||||
if cache.LastUpdateSeconds != secs {
|
||||
month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year()
|
||||
hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second()
|
||||
zone, _ := rec.Created.Zone()
|
||||
updated := &formatCacheType{
|
||||
LastUpdateSeconds: secs,
|
||||
shortTime: fmt.Sprintf("%02d:%02d", hour, minute),
|
||||
shortDate: fmt.Sprintf("%02d/%02d/%02d", month, day, year%100),
|
||||
longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone),
|
||||
longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day),
|
||||
}
|
||||
cache = *updated
|
||||
formatCache = updated
|
||||
}
|
||||
|
||||
// Split the string into pieces by % signs
|
||||
pieces := bytes.Split([]byte(format), []byte{'%'})
|
||||
|
||||
// Iterate over the pieces, replacing known formats
|
||||
for i, piece := range pieces {
|
||||
if i > 0 && len(piece) > 0 {
|
||||
switch piece[0] {
|
||||
case 'T':
|
||||
out.WriteString(cache.longTime)
|
||||
case 't':
|
||||
out.WriteString(cache.shortTime)
|
||||
case 'D':
|
||||
out.WriteString(cache.longDate)
|
||||
case 'd':
|
||||
out.WriteString(cache.shortDate)
|
||||
case 'L':
|
||||
out.WriteString(levelStrings[rec.Level])
|
||||
case 'S':
|
||||
out.WriteString(rec.Source)
|
||||
case 'M':
|
||||
out.WriteString(rec.Message)
|
||||
}
|
||||
if len(piece) > 1 {
|
||||
out.Write(piece[1:])
|
||||
}
|
||||
} else if len(piece) > 0 {
|
||||
out.Write(piece)
|
||||
}
|
||||
}
|
||||
out.WriteByte('\n')
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// This is the standard writer that prints to standard output.
|
||||
type FormatLogWriter chan *LogRecord
|
||||
|
||||
// This creates a new FormatLogWriter
|
||||
func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter {
|
||||
records := make(FormatLogWriter, LogBufferLength)
|
||||
go records.run(out, format)
|
||||
return records
|
||||
}
|
||||
|
||||
func (w FormatLogWriter) run(out io.Writer, format string) {
|
||||
for rec := range w {
|
||||
fmt.Fprint(out, FormatLogRecord(format, rec))
|
||||
}
|
||||
}
|
||||
|
||||
// This is the FormatLogWriter's output method. This will block if the output
|
||||
// buffer is full.
|
||||
func (w FormatLogWriter) LogWrite(rec *LogRecord) {
|
||||
w <- rec
|
||||
}
|
||||
|
||||
// Close stops the logger from sending messages to standard output. Attempts to
|
||||
// send log messages to this logger after a Close have undefined behavior.
|
||||
func (w FormatLogWriter) Close() {
|
||||
close(w)
|
||||
}
|
||||
57
Godeps/_workspace/src/code.google.com/p/log4go/socklog.go
сгенерированный
поставляемый
Обычный файл
57
Godeps/_workspace/src/code.google.com/p/log4go/socklog.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,57 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
// This log writer sends output to a socket
|
||||
type SocketLogWriter chan *LogRecord
|
||||
|
||||
// This is the SocketLogWriter's output method
|
||||
func (w SocketLogWriter) LogWrite(rec *LogRecord) {
|
||||
w <- rec
|
||||
}
|
||||
|
||||
func (w SocketLogWriter) Close() {
|
||||
close(w)
|
||||
}
|
||||
|
||||
func NewSocketLogWriter(proto, hostport string) SocketLogWriter {
|
||||
sock, err := net.Dial(proto, hostport)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
w := SocketLogWriter(make(chan *LogRecord, LogBufferLength))
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if sock != nil && proto == "tcp" {
|
||||
sock.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for rec := range w {
|
||||
// Marshall into JSON
|
||||
js, err := json.Marshal(rec)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = sock.Write(js)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return w
|
||||
}
|
||||
45
Godeps/_workspace/src/code.google.com/p/log4go/termlog.go
сгенерированный
поставляемый
Обычный файл
45
Godeps/_workspace/src/code.google.com/p/log4go/termlog.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var stdout io.Writer = os.Stdout
|
||||
|
||||
// This is the standard writer that prints to standard output.
|
||||
type ConsoleLogWriter chan *LogRecord
|
||||
|
||||
// This creates a new ConsoleLogWriter
|
||||
func NewConsoleLogWriter() ConsoleLogWriter {
|
||||
records := make(ConsoleLogWriter, LogBufferLength)
|
||||
go records.run(stdout)
|
||||
return records
|
||||
}
|
||||
|
||||
func (w ConsoleLogWriter) run(out io.Writer) {
|
||||
var timestr string
|
||||
var timestrAt int64
|
||||
|
||||
for rec := range w {
|
||||
if at := rec.Created.UnixNano() / 1e9; at != timestrAt {
|
||||
timestr, timestrAt = rec.Created.Format("01/02/06 15:04:05"), at
|
||||
}
|
||||
fmt.Fprint(out, "[", timestr, "] [", levelStrings[rec.Level], "] ", rec.Message, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
// This is the ConsoleLogWriter's output method. This will block if the output
|
||||
// buffer is full.
|
||||
func (w ConsoleLogWriter) LogWrite(rec *LogRecord) {
|
||||
w <- rec
|
||||
}
|
||||
|
||||
// Close stops the logger from sending messages to standard output. Attempts to
|
||||
// send log messages to this logger after a Close have undefined behavior.
|
||||
func (w ConsoleLogWriter) Close() {
|
||||
close(w)
|
||||
}
|
||||
278
Godeps/_workspace/src/code.google.com/p/log4go/wrapper.go
сгенерированный
поставляемый
Обычный файл
278
Godeps/_workspace/src/code.google.com/p/log4go/wrapper.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,278 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
Global Logger
|
||||
)
|
||||
|
||||
func init() {
|
||||
Global = NewDefaultLogger(DEBUG)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).LoadConfiguration
|
||||
func LoadConfiguration(filename string) {
|
||||
Global.LoadConfiguration(filename)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).AddFilter
|
||||
func AddFilter(name string, lvl level, writer LogWriter) {
|
||||
Global.AddFilter(name, lvl, writer)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).Close (closes and removes all logwriters)
|
||||
func Close() {
|
||||
Global.Close()
|
||||
}
|
||||
|
||||
func Crash(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
panic(args)
|
||||
}
|
||||
|
||||
// Logs the given message and crashes the program
|
||||
func Crashf(format string, args ...interface{}) {
|
||||
Global.intLogf(CRITICAL, format, args...)
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
panic(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Exit(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Exitf(format string, args ...interface{}) {
|
||||
Global.intLogf(ERROR, format, args...)
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stderr(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stderrf(format string, args ...interface{}) {
|
||||
Global.intLogf(ERROR, format, args...)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stdout(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stdoutf(format string, args ...interface{}) {
|
||||
Global.intLogf(INFO, format, args...)
|
||||
}
|
||||
|
||||
// Send a log message manually
|
||||
// Wrapper for (*Logger).Log
|
||||
func Log(lvl level, source, message string) {
|
||||
Global.Log(lvl, source, message)
|
||||
}
|
||||
|
||||
// Send a formatted log message easily
|
||||
// Wrapper for (*Logger).Logf
|
||||
func Logf(lvl level, format string, args ...interface{}) {
|
||||
Global.intLogf(lvl, format, args...)
|
||||
}
|
||||
|
||||
// Send a closure log message
|
||||
// Wrapper for (*Logger).Logc
|
||||
func Logc(lvl level, closure func() string) {
|
||||
Global.intLogc(lvl, closure)
|
||||
}
|
||||
|
||||
// Utility for finest log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Finest
|
||||
func Finest(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINEST
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for fine log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Fine
|
||||
func Fine(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for debug log messages
|
||||
// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
|
||||
// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time.
|
||||
// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
|
||||
// Wrapper for (*Logger).Debug
|
||||
func Debug(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = DEBUG
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for trace log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Trace
|
||||
func Trace(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = TRACE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for info log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Info
|
||||
func Info(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = INFO
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Warn
|
||||
func Warn(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = WARNING
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Error
|
||||
func Error(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = ERROR
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Critical
|
||||
func Critical(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = CRITICAL
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Ссылка в новой задаче
Block a user