diff --git a/draw2d/src/cmd/test_gopher.go b/draw2d/src/cmd/test_gopher.go new file mode 100644 index 0000000..4039222 --- /dev/null +++ b/draw2d/src/cmd/test_gopher.go @@ -0,0 +1,136 @@ +package main + + +import ( + "fmt" + "log" + "os" + "bufio" + "time" + "math" + + "image" + "image/png" + //"draw2d" + "draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d" +) + +const ( + width, height = 500, 300 +) + +var ( + lastTime int64 + folder = "../../../../wiki/test_results/" +) + +func initGc(w, h int) (image.Image, *draw2d.GraphicContext) { + i := image.NewRGBA(w, h) + gc := draw2d.NewGraphicContext(i) + lastTime = time.Nanoseconds() + + gc.SetStrokeColor(image.Black) + gc.SetFillColor(image.White) + // fill the background + //gc.Clear() + + return i, gc +} + +func saveToPngFile(TestName string, m image.Image) { + dt := time.Nanoseconds() - lastTime + fmt.Printf("%s during: %f ms\n", TestName, float(dt)*10e-6) + filePath := folder + TestName + ".png" + f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600) + 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) + } + fmt.Printf("Wrote %s OK.\n", filePath) +} + +func gordon(gc *draw2d.GraphicContext, x, y, w, h float) { + h23 := (h * 2) / 3 + + blf := image.RGBAColor{0, 0, 0, 0xff} + wf := image.RGBAColor{0xff, 0xff, 0xff, 0xff} + nf := image.RGBAColor{0x8B, 0x45, 0x13, 0xff} + brf := image.RGBAColor{0x8B, 0x45, 0x13, 0x99} + brb := image.RGBAColor{0x8B, 0x45, 0x13, 0xBB} + + gc.MoveTo(x, y+h) + gc.CubicCurveTo(x, y+h, x+w/2, y-h, x+w, y+h) + gc.Close() + gc.SetFillColor(brb) + gc.Fill() + gc.RoundRect(x, y+h, x+ w, y+h+h, 10, 10) + gc.Fill() + gc.Circle(x, y+h, w/12) // left ear + gc.SetFillColor(brf) + gc.Fill() + gc.Circle(x, y+h, w/12-10) + gc.SetFillColor(nf) + gc.Fill() + + gc.Circle(x+w, y+h, w/12) // right ear + gc.SetFillColor(brf) + gc.Fill() + gc.Circle(x+w, y+h, w/12-10) + gc.SetFillColor(nf) + gc.Fill() + + gc.Circle(x+w/3, y+h23, w/9) // left eye + gc.SetFillColor(wf) + gc.Fill() + gc.Circle(x+w/3+10, y+h23, w / 10 - 10) + gc.SetFillColor(blf) + gc.Fill() + gc.Circle(x+w/3+15, y+h23, 5) + gc.SetFillColor(wf) + gc.Fill() + + gc.Circle(x+w-w/3, y+h23, w/9) // right eye + gc.Fill() + gc.Circle(x+w-w/3+10, y+h23, w / 10 - 10) + gc.SetFillColor(blf) + gc.Fill() + gc.Circle(x+w-(w/3)+15, y+h23, 5) + gc.SetFillColor(wf) + gc.Fill() + + gc.SetFillColor(wf) + gc.RoundRect(x+w/2-w/8, y+h+30, x+w/2-w/8 + w/8, y+h+30 + w/6, 5, 5) // left tooth + gc.Fill() + gc.RoundRect(x+w/2, y+h+30, x+w/2+w/8, y+h+30+w/6, 5, 5) // right tooth + gc.Fill() + + + gc.Ellipse(x+(w/2), y+h+30, w/6, w/12) // snout + gc.SetFillColor(nf) + gc.Fill() + gc.Ellipse(x+(w/2), y+h+10, w/10, w/12) // nose + gc.SetFillColor(blf) + gc.Fill() + +} + +func main() { + i, gc := initGc(width, height) + gc.Clear() + gc.Translate(100, 100) + gc.Rotate(-30 * (math.Pi / 180.0)) + gordon(gc, 48, 48, 240, 72) + saveToPngFile("TestGopher", i) +} \ No newline at end of file diff --git a/draw2d/src/cmd/testandroid.go b/draw2d/src/cmd/testandroid.go new file mode 100644 index 0000000..025389b --- /dev/null +++ b/draw2d/src/cmd/testandroid.go @@ -0,0 +1,98 @@ +package main + + +import ( + "fmt" + "log" + "os" + "bufio" + "time" + + "math" + "image" + "image/png" + //"draw2d" + "draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d" +) + +const ( + width, height = 500, 500 +) + +var ( + lastTime int64 + folder = "../../../../wiki/test_results/" +) + +func initGc(w, h int) (image.Image, *draw2d.GraphicContext) { + i := image.NewRGBA(w, h) + gc := draw2d.NewGraphicContext(i) + lastTime = time.Nanoseconds() + + gc.SetStrokeColor(image.Black) + gc.SetFillColor(image.White) + // fill the background + //gc.Clear() + + return i, gc +} + +func saveToPngFile(TestName string, m image.Image) { + dt := time.Nanoseconds() - lastTime + fmt.Printf("%s during: %f ms\n", TestName, float(dt)*10e-6) + filePath := folder + TestName + ".png" + f, err := os.Open(filePath, os.O_CREAT|os.O_WRONLY, 0600) + 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) + } + fmt.Printf("Wrote %s OK.\n", filePath) +} + +func android(gc *draw2d.GraphicContext, x, y float) { + gc.SetLineCap(draw2d.RoundCap) + gc.SetLineWidth(5) + gc.ArcTo(x+80, y+70, 50, 50, 180 * (math.Pi/180), 360 * (math.Pi/180)) // head + gc.FillStroke() + gc.MoveTo(x+60, y+25) + gc.LineTo(x+50, y+10) + gc.MoveTo(x+100, y+25) + gc.LineTo( x+110, y+10) + gc.Stroke() + gc.Circle(x+60, y+45, 5) // left eye + gc.FillStroke() + gc.Circle(x+100, y+45, 5) // right eye + gc.FillStroke() + gc.RoundRect(x+30, y+75, x+30+100, y+75+90, 10, 10) // body + gc.FillStroke() + gc.Rect(x+30, y+75, x+30+100, y+75+80) + gc.FillStroke() + gc.RoundRect(x+5, y+80, x+5+20, y+80+70, 10, 10) // left arm + gc.FillStroke() + gc.RoundRect(x+135, y+80, x+135+20, y+80+70, 10, 10) // right arm + gc.FillStroke() + gc.RoundRect(x+50, y+150, x+50+20, y+150+50, 10, 10) // left leg + gc.FillStroke() + gc.RoundRect(x+90, y+150, x+90+20, y+150+50, 10, 10) // right leg + gc.FillStroke() +} + + + +func main() { + i, gc := initGc(width, height) + android(gc, 100, 100) + saveToPngFile("TestAndroid", i) +} diff --git a/draw2d/src/cmd/testdraw2d.go b/draw2d/src/cmd/testdraw2d.go index be718e5..627a84e 100644 --- a/draw2d/src/cmd/testdraw2d.go +++ b/draw2d/src/cmd/testdraw2d.go @@ -193,7 +193,7 @@ func TestCurveRectangle() { gc.CubicCurveTo(x0, y1, x0, y1, x0, y1-radius) } } - gc.ClosePath() + gc.Close() gc.SetFillColor(image.RGBAColor{0x80, 0x80, 0xFF, 0xFF}) gc.SetStrokeColor(image.RGBAColor{0x80, 0, 0, 0x80}) @@ -261,13 +261,13 @@ func TestFillStroke() { gc.LineTo(230.4, 230.4) gc.RLineTo(-102.4, 0.0) gc.CubicCurveTo(51.2, 230.4, 51.2, 128.0, 128.0, 128.0) - gc.ClosePath() + gc.Close() gc.MoveTo(64.0, 25.6) gc.RLineTo(51.2, 51.2) gc.RLineTo(-51.2, 51.2) gc.RLineTo(-51.2, -51.2) - gc.ClosePath() + gc.Close() gc.SetLineWidth(10.0) gc.SetFillColor(image.RGBAColor{0, 0, 0xFF, 0xFF}) @@ -341,7 +341,7 @@ func TestRoundRectangle() { gc.ArcTo(x+width-radius, y+height-radius, radius, radius, 0*degrees, 90*degrees) gc.ArcTo(x+radius, y+height-radius, radius, radius, 90*degrees, 90*degrees) gc.ArcTo(x+radius, y+radius, radius, radius, 180*degrees, 90*degrees) - gc.ClosePath() + gc.Close() gc.SetFillColor(image.RGBAColor{0x80, 0x80, 0xFF, 0xFF}) gc.SetStrokeColor(image.RGBAColor{0x80, 0, 0, 0x80}) @@ -419,13 +419,14 @@ func TestBubble() { func TestStar() { i, gc := initGc(w, h) for i := 0.0 ; i < 360; i = i + 10 {// Go from 0 to 360 degrees in 10 degree steps - gc.BeginPath() // Start a new path - gc.Save() // Keep rotations temporary - gc.MoveTo(144, 144) + gc.Save() + gc.SetLineWidth(5) // Keep rotations temporary + gc.Translate(144, 144) gc.Rotate(i * (math.Pi / 180.0)) // Rotate by degrees on stack from 'for' - gc.RLineTo(72, 0) + gc.MoveTo(0, 0) + gc.LineTo(72, 0) gc.Stroke() - gc.Restore() // Get back the unrotated state + gc.Restore() } saveToPngFile("TestStar", i) } @@ -440,7 +441,7 @@ func TestTransform() { gc.RLineTo(72,0) gc.RLineTo(0, 72) gc.RLineTo(-72,0) - gc.ClosePath() + gc.Close() gc.Stroke() gc.Restore() @@ -452,7 +453,7 @@ func TestTransform() { gc.RLineTo(72,0) gc.RLineTo(0, 72) gc.RLineTo(-72,0) - gc.ClosePath() // Draw box... + gc.Close() // Draw box... gc.Stroke() gc.Restore() @@ -464,7 +465,7 @@ func TestTransform() { gc.RLineTo(72,0) gc.RLineTo(0, 72) gc.RLineTo(-72,0) - gc.ClosePath() // Draw box... + gc.Close() // Draw box... gc.Stroke() gc.Restore() @@ -477,13 +478,22 @@ func TestTransform() { gc.RLineTo(72,0) gc.RLineTo(0, 72) gc.RLineTo(-72,0) - gc.ClosePath() // Draw box + gc.Close() // Draw box gc.Stroke() gc.Restore() saveToPngFile("TestTransform", i) } +func TestPathTransform() { + i, gc := initGc(800, 600) + gc.SetLineWidth(20) + gc.Scale(1,5) + gc.ArcTo(200, 50, 50, 50, 0, math.Pi * 2) + gc.Stroke() + saveToPngFile("TestPathTransform", i) +} + func main() { TestPath() TestDrawArc() @@ -500,4 +510,5 @@ func main() { TestBubble() TestStar() TestTransform() + TestPathTransform() } diff --git a/draw2d/src/pkg/draw2d/draw2d.go b/draw2d/src/pkg/draw2d/draw2d.go index fa37501..10ce90c 100644 --- a/draw2d/src/pkg/draw2d/draw2d.go +++ b/draw2d/src/pkg/draw2d/draw2d.go @@ -6,7 +6,7 @@ package draw2d import ( "exp/draw" "image" - //"math" + "math" "freetype-go.googlecode.com/hg/freetype/raster" ) @@ -163,68 +163,83 @@ func (gc *GraphicContext) BeginPath() { } func (gc *GraphicContext) MoveTo(x, y float) { - gc.current.tr.Transform(&x, &y) gc.current.path.MoveTo(x, y) } func (gc *GraphicContext) RMoveTo(dx, dy float) { - gc.current.tr.VectorTransform(&dx, &dy) gc.current.path.RMoveTo(dx, dy) } func (gc *GraphicContext) LineTo(x, y float) { - gc.current.tr.Transform(&x, &y) gc.current.path.LineTo(x, y) } func (gc *GraphicContext) RLineTo(dx, dy float) { - gc.current.tr.VectorTransform(&dx, &dy) gc.current.path.RLineTo(dx, dy) } -func (gc *GraphicContext) Rect(x1, y1, x2, y2 float) { - gc.current.tr.Transform(&x1, &y1, &x2, &y2) - gc.current.path.Rect(x1, y1, x2, y2) -} - -func (gc *GraphicContext) RRect(dx1, dy1, dx2, dy2 float) { - gc.current.tr.VectorTransform(&dx1, &dy1, &dx2, &dy2) - gc.current.path.RRect(dx1, dy1, dx2, dy2) -} - func (gc *GraphicContext) QuadCurveTo(cx, cy, x, y float) { - gc.current.tr.Transform(&cx, &cy, &x, &y) gc.current.path.QuadCurveTo(cx, cy, x, y) } func (gc *GraphicContext) RQuadCurveTo(dcx, dcy, dx, dy float) { - gc.current.tr.VectorTransform(&dcx, &dcy, &dx, &dy) gc.current.path.RQuadCurveTo(dcx, dcy, dx, dy) } func (gc *GraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float) { - gc.current.tr.Transform(&cx1, &cy1, &cx2, &cy2, &x, &y) gc.current.path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y) } func (gc *GraphicContext) RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy float) { - gc.current.tr.VectorTransform(&dcx1, &dcy1, &dcx2, &dcy2, &dx, &dy) gc.current.path.RCubicCurveTo(dcx1, dcy1, dcx2, dcy2, dx, dy) } func (gc *GraphicContext) ArcTo(cx, cy, rx, ry, startAngle, angle float) { - gc.current.tr.Transform(&cx, &cy) - gc.current.tr.VectorTransform(&rx, &ry) gc.current.path.ArcTo(cx, cy, rx, ry, startAngle, angle) } func (gc *GraphicContext) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) { - gc.current.tr.VectorTransform(&dcx, &dcy) - gc.current.tr.VectorTransform(&rx, &ry) gc.current.path.RArcTo(dcx, dcy, rx, ry, startAngle, angle) } -func (gc *GraphicContext) ClosePath() { +//high level path creation +func (gc *GraphicContext) Rect(x1, y1, x2, y2 float) { + if gc.current.path.isEmpty() { + gc.current.path.MoveTo(x1, y1) + } else { + gc.current.path.LineTo(x1, y1) + } + gc.current.path.LineTo(x2, y1) + gc.current.path.LineTo(x2, y2) + gc.current.path.LineTo(x1, y2) + gc.current.path.Close() +} + +func (gc *GraphicContext) RoundRect(x1, y1, x2, y2, arcWidth, arcHeight float) { + arcWidth = arcWidth/2; + arcHeight = arcHeight/2; + gc.MoveTo(x1, y1+ arcHeight); + gc.QuadCurveTo(x1, y1, x1 + arcWidth, y1); + gc.LineTo(x2-arcWidth, y1); + gc.QuadCurveTo(x2, y1, x2, y1 + arcHeight); + gc.LineTo(x2, y2-arcHeight); + gc.QuadCurveTo(x2, y2, x2 - arcWidth, y2); + gc.LineTo(x1 + arcWidth, y2); + gc.QuadCurveTo(x1, y2, x1, y2 - arcHeight); + gc.Close() +} + +func (gc *GraphicContext) Ellipse(cx, cy, rx, ry float) { + gc.current.path.ArcTo(cx, cy, rx, ry, 0, -math.Pi * 2) + gc.current.path.Close() +} + +func (gc *GraphicContext) Circle(cx, cy, radius float) { + gc.current.path.ArcTo(cx, cy, radius, radius, 0, -math.Pi * 2) + gc.current.path.Close() +} + +func (gc *GraphicContext) Close() { gc.current.path.Close() } @@ -236,35 +251,44 @@ func (gc *GraphicContext) paint(color image.Color) { gc.current.path = new(Path) } -func (gc *GraphicContext) Stroke(paths ...*Path) { +func (gc *GraphicContext) Stroke(paths ...*Path) { paths = append(paths, gc.current.path) - rasterPath := tracePath(gc.current.dash, gc.current.dashOffset, paths...) gc.rasterizer.UseNonZeroWinding = true - gc.rasterizer.AddStroke(*rasterPath, raster.Fix32(gc.current.lineWidth*256), gc.current.cap.capper(), gc.current.join.joiner()) + rasterPath := new(raster.Path) + if(gc.current.dash == nil) { + tracePath(gc.current.tr.GetMaxAbsScaling(), rasterPath, paths...) + } else { + traceDashPath(gc.current.dash, gc.current.dashOffset, gc.current.tr.GetMaxAbsScaling(), rasterPath, paths...) + } + mta := NewMatrixTransformAdder(gc.current.tr, gc.rasterizer) + raster.Stroke(mta, *rasterPath, raster.Fix32(gc.current.lineWidth*256), gc.current.cap.capper(), gc.current.join.joiner()) gc.paint(gc.current.strokeColor) } func (gc *GraphicContext) Fill(paths ...*Path) { paths = append(paths, gc.current.path) - rasterPath := tracePath(nil, 0, paths...) - gc.rasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule() - gc.rasterizer.AddPath(*rasterPath) + mta := NewMatrixTransformAdder(gc.current.tr, gc.rasterizer) + tracePath(gc.current.tr.GetMaxAbsScaling(), mta, paths...) gc.paint(gc.current.fillColor) } func (gc *GraphicContext) FillStroke(paths ...*Path) { paths = append(paths, gc.current.path) - rasterPath := tracePath(nil, 0, paths...) + mta := NewMatrixTransformAdder(gc.current.tr, gc.rasterizer) + tracePath(gc.current.tr.GetMaxAbsScaling(), mta, paths...) gc.rasterizer.UseNonZeroWinding = gc.current.fillRule.fillRule() - gc.rasterizer.AddPath(*rasterPath) gc.paint(gc.current.fillColor) - if gc.current.dash != nil { - rasterPath = tracePath(gc.current.dash, gc.current.dashOffset, paths...) - } + gc.rasterizer.UseNonZeroWinding = true - gc.rasterizer.AddStroke(*rasterPath, raster.Fix32(gc.current.lineWidth*256), gc.current.cap.capper(), gc.current.join.joiner()) + rasterPath := new(raster.Path) + if(gc.current.dash == nil) { + tracePath(gc.current.tr.GetMaxAbsScaling(), rasterPath, paths...) + } else { + traceDashPath(gc.current.dash, gc.current.dashOffset, gc.current.tr.GetMaxAbsScaling(), rasterPath, paths...) + } + raster.Stroke(mta, *rasterPath, raster.Fix32(gc.current.lineWidth*256), gc.current.cap.capper(), gc.current.join.joiner()) gc.paint(gc.current.strokeColor) } @@ -300,79 +324,3 @@ func (j Join) joiner() raster.Joiner { return raster.RoundJoiner } - -type PathAdapter struct { - path *raster.Path - x, y, distance float - dash []float - currentDash int - dashOffset float -} - -func tracePath(dash []float, dashOffset float, paths ...*Path) *raster.Path { - var adapter PathAdapter - if dash != nil && len(dash) > 0 { - adapter.dash = dash - } else { - adapter.dash = nil - } - adapter.currentDash = 0 - adapter.dashOffset = dashOffset - adapter.path = new(raster.Path) - for _, path := range paths { - path.TraceLine(&adapter) - } - return adapter.path -} - -func floatToPoint(x, y float) raster.Point { - return raster.Point{raster.Fix32(x * 256), raster.Fix32(y * 256)} -} - -func (p *PathAdapter) MoveTo(x, y float) { - p.path.Start(floatToPoint(x, y)) - p.x, p.y = x, y - p.distance = p.dashOffset - p.currentDash = 0 -} - -func (p *PathAdapter) LineTo(x, y float) { - if p.dash != nil { - rest := p.dash[p.currentDash] - p.distance - for rest < 0 { - p.distance = p.distance - p.dash[p.currentDash] - p.currentDash = (p.currentDash + 1) % len(p.dash) - rest = p.dash[p.currentDash] - p.distance - } - d := distance(p.x, p.y, x, y) - for d >= rest { - k := rest / d - lx := p.x + k*(x-p.x) - ly := p.y + k*(y-p.y) - if p.currentDash%2 == 0 { - // line - p.path.Add1(floatToPoint(lx, ly)) - } else { - // gap - p.path.Start(floatToPoint(lx, ly)) - } - d = d - rest - p.x, p.y = lx, ly - p.currentDash = (p.currentDash + 1) % len(p.dash) - rest = p.dash[p.currentDash] - } - p.distance = d - if p.currentDash%2 == 0 { - p.path.Add1(floatToPoint(x, y)) - } else { - p.path.Start(floatToPoint(x, y)) - } - if p.distance >= p.dash[p.currentDash] { - p.distance = p.distance - p.dash[p.currentDash] - p.currentDash = (p.currentDash + 1) % len(p.dash) - } - } else { - p.path.Add1(floatToPoint(x, y)) - } - p.x, p.y = x, y -} diff --git a/draw2d/src/pkg/draw2d/path.go b/draw2d/src/pkg/draw2d/path.go index 2d2027e..bee511c 100644 --- a/draw2d/src/pkg/draw2d/path.go +++ b/draw2d/src/pkg/draw2d/path.go @@ -47,6 +47,10 @@ func (p *Path) LastPoint() (x, y float) { return p.x, p.y } +func (p *Path) isEmpty() bool { + return len(p.commands) == 0 +} + func (p *Path) Close() *Path { p.appendToPath(Close) return p @@ -159,7 +163,7 @@ func (p *Path) RArcTo(dcx, dcy, rx, ry, startAngle, angle float) *Path { return p } -func (p *Path) TraceLine(tracer LineTracer) { +func (p *Path) TraceLine(tracer LineTracer, approximationScale float) { j := 0 x, y := 0.0, 0.0 firstX, firstY := x, y @@ -180,15 +184,15 @@ func (p *Path) TraceLine(tracer LineTracer) { x, y = p.vertices[j], p.vertices[j+1] j = j + 2 case QuadCurveTo: - quadraticBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], 1.0, 0.0) + quadraticBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], approximationScale, 0) x, y = p.vertices[j+2], p.vertices[j+3] j = j + 4 case CubicCurveTo: - cubicBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], 1.0, 0.0, 0.0) + cubicBezier(tracer, x, y, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], approximationScale, 0, 0) x, y = p.vertices[j+4], p.vertices[j+5] j = j + 6 case ArcTo: - arc(tracer, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], 1) + arc(tracer, p.vertices[j], p.vertices[j+1], p.vertices[j+2], p.vertices[j+3], p.vertices[j+4], p.vertices[j+5], approximationScale) j = j + 6 case Close: tracer.LineTo(firstX, firstY) diff --git a/draw2d/src/pkg/draw2d/transform.go b/draw2d/src/pkg/draw2d/transform.go index 7926ba1..1962c64 100644 --- a/draw2d/src/pkg/draw2d/transform.go +++ b/draw2d/src/pkg/draw2d/transform.go @@ -2,6 +2,9 @@ // created: 21/11/2010 by Laurent Le Goff package draw2d +import ( + "freetype-go.googlecode.com/hg/freetype/raster" +) type MatrixTransform [6]float @@ -22,6 +25,15 @@ func (tr MatrixTransform) Transform(points ...*float) { } } +func (tr MatrixTransform) TransformRasterPoint(points ...*raster.Point) { + for _, point := range points { + x := float(point.X) / 256 + y := float(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 ...*float) { d := tr.Determinant() // matrix determinant for i, j := 0, 1; j < len(points); i, j = i+2, j+2 { @@ -148,6 +160,28 @@ func (tr MatrixTransform) GetTranslation() (x, y float) { return tr[4], tr[5] } +func (tr MatrixTransform) GetScaling() (x, y float) { + return tr[0], tr[3] +} + +func (tr MatrixTransform) GetMaxAbsScaling() (s float) { + sx := fabs(tr[0]) + sy := fabs(tr[3]) + if(sx > sy) { + return sx + } + return sy +} + +func (tr MatrixTransform) GetMinAbsScaling() (s float) { + sx := fabs(tr[0]) + sy := fabs(tr[3]) + if(sx > sy) { + return sy + } + return sx +} + // ******************** Testing ******************** /** @@ -186,3 +220,39 @@ func (tr MatrixTransform) IsTranslation() bool { func fequals(float1, float2 float) bool { return fabs(float1-float2) <= epsilon } + +// 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) +} +