diff --git a/Makefile b/Makefile index 6e40c37..d7ef39b 100644 --- a/Makefile +++ b/Makefile @@ -7,19 +7,20 @@ install: cd draw2d && make install cd draw2dgl && make install cd postscript && make install - #cd wingui && make install + cd wingui && make install clean: cd draw2d && make clean cd draw2dgl && make clean cd postscript && make clean cd cmd && make clean - #cd wingui && make clean + cd wingui && make clean nuke: cd draw2d && make nuke cd draw2dgl && make nuke cd postscript && make nuke + cd wingui && make nuke command: cd cmd && make diff --git a/cmd/draw2dgl.go b/cmd/draw2dgl.go index cce25e8..78610b5 100644 --- a/cmd/draw2dgl.go +++ b/cmd/draw2dgl.go @@ -18,45 +18,105 @@ package main import ( + "os" + "math" + "io/ioutil" + "strings" "gl" "glut" + "image" + "draw2d.googlecode.com/hg/draw2d" + "draw2d.googlecode.com/hg/draw2dgl" + "draw2d.googlecode.com/hg/postscript" + "log" + "time" ) -type GLPainter struct { +var postscriptContent string +func TestDrawCubicCurve(gc draw2d.GraphicContext) { + // draw a cubic curve + x, y := 25.6, 128.0 + x1, y1 := 102.4, 230.4 + x2, y2 := 153.6, 25.6 + x3, y3 := 230.4, 128.0 + + gc.SetStrokeColor(image.NRGBAColor{0, 0, 0, 0xff}) + gc.SetLineWidth(10) + gc.MoveTo(x, y) + gc.CubicCurveTo(x1, y1, x2, y2, x3, y3) + gc.Stroke() + + gc.SetStrokeColor(image.NRGBAColor{0xFF, 0x33, 0x33, 0x99}) + + gc.SetLineWidth(6) + // draw segment of curve + gc.MoveTo(x, y) + gc.LineTo(x1, y1) + gc.MoveTo(x2, y2) + gc.LineTo(x3, y3) + gc.Stroke() } +var ( + width, height int + rotate int +) + func reshape(w, h int) { /* Because Gil specified "screen coordinates" (presumably with an upper-left origin), this short bit of code sets up the coordinate system to correspond to actual window coodrinates. This code wouldn't be required if you chose a (more typical in 3D) abstract coordinate system. */ - + gl.ClearColor(1, 1, 1, 1) + //fmt.Println(gl.GetString(gl.EXTENSIONS)) gl.Viewport(0, 0, w, h) /* Establish viewing area to cover entire window. */ gl.MatrixMode(gl.PROJECTION) /* Start modifying the projection matrix. */ gl.LoadIdentity() /* Reset project matrix. */ gl.Ortho(0, float64(w), 0, float64(h), -1, 1) /* Map abstract coords directly to window coords. */ gl.Scalef(1, -1, 1) /* Invert Y axis so increasing Y goes down. */ gl.Translatef(0, float32(-h), 0) /* Shift origin up to upper-left corner. */ + gl.Enable(gl.BLEND) + gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + + width, height = w, h } func display() { - gl.Clear(gl.COLOR_BUFFER_BIT) - gl.Begin(gl.TRIANGLES) - gl.Color3f(0.0, 0.0, 1.0) /* blue */ - gl.Vertex2i(0, 0) - gl.Color3f(0.0, 1.0, 0.0) /* green */ - gl.Vertex2i(200, 200) - gl.Color3f(1.0, 0.0, 0.0) /* red */ - gl.Vertex2i(20, 200) - gl.End() + + gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) + gl.LineWidth(1) + gc := draw2dgl.NewGraphicContext(width, height) + + gc.Translate(380, 400) + gc.Scale(1, -1) + rotate = (rotate + 10) % 360 + gc.Rotate(float64(rotate) * math.Pi / 180) + gc.Translate(-380, -400) + interpreter := postscript.NewInterpreter(gc) + reader := strings.NewReader(postscriptContent) + lastTime := time.Nanoseconds() + interpreter.Execute(reader) + dt := time.Nanoseconds() - lastTime + log.Printf("Redraw in : %f ms\n", float64(dt)*1e-6) gl.Flush() /* Single buffered, so needs a flush. */ + glut.PostRedisplay() } func main() { + src, err := os.OpenFile("../resource/postscript/tiger.ps", 0, 0) + if err != nil { + log.Println("can't find postscript file.") + return + } + defer src.Close() + bytes, err := ioutil.ReadAll(src) + postscriptContent = string(bytes) glut.Init() + glut.InitWindowSize(800, 800) glut.CreateWindow("single triangle") + glut.DisplayFunc(display) glut.ReshapeFunc(reshape) glut.MainLoop() diff --git a/cmd/testGlutDraw.go b/cmd/testGlutDraw.go deleted file mode 100644 index 06675d3..0000000 --- a/cmd/testGlutDraw.go +++ /dev/null @@ -1,147 +0,0 @@ -// Ported from GLUT's samples. Original copyright below applies. - -/* Copyright (c) Mark J. Kilgard, 1996. */ - -/* This program is freely distributable without licensing fees - and is provided without guarantee or warrantee expressed or - implied. This program is -not- in the public domain. */ - -/* This program is a response to a question posed by Gil Colgate - about how lengthy a program is required using - OpenGL compared to using Direct3D immediate mode to "draw a - triangle at screen coordinates 0,0, to 200,200 to 20,200, and I - want it to be blue at the top vertex, red at the left vertex, and - green at the right vertex". I'm not sure how long the Direct3D - program is; Gil has used Direct3D and his guess is "about 3000 - lines of code". */ - -package main - -import ( - "gl" - "glut" - "exp/draw" - "image" - "freetype-go.googlecode.com/hg/freetype/raster" - "draw2d.googlecode.com/svn/trunk/draw2d/src/pkg/draw2d" - "postscript-go.googlecode.com/svn/trunk/postscript-go/src/pkg/postscript" - "fmt" -) - -type GLPainter struct { - // The Porter-Duff composition operator. - Op draw.Op - // The 16-bit color to paint the spans. - cr, cg, cb uint8 - ca uint32 -} - -const M16 uint32 = 1<<16 - 1 -const M32 uint32 = 1<<32 - 1 - -// Paint satisfies the Painter interface by painting ss onto an image.RGBA. -func (p *GLPainter) Paint(ss []raster.Span, done bool) { - gl.Begin(gl.LINES) - for _, s := range ss { - ma := s.A >> 16 - a := ma * p.ca / M16 - gl.Color4ub(p.cr, p.cg, p.cb, uint8(a>>8)) - gl.Vertex2i(s.X0, s.Y) - gl.Vertex2i(s.X1, s.Y) - } - gl.End() -} - -// SetColor sets the color to paint the spans. -func (p *GLPainter) SetColor(c image.Color) { - r, g, b, a := c.RGBA() - if a == 0 { - p.cr = 0 - p.cg = 0 - p.cb = 0 - p.ca = a - } else { - p.cr = uint8((r * M16 / a) >> 8) - p.cg = uint8((g * M16 / a) >> 8) - p.cb = uint8((b * M16 / a) >> 8) - p.ca = a - } -} - -// NewRGBAPainter creates a new RGBAPainter for the given image. -func NewGLPainter() *GLPainter { - return &GLPainter{} -} - - -func TestDrawCubicCurve(gc draw2d.GraphicContext) { - // draw a cubic curve - x, y := 25.6, 128.0 - x1, y1 := 102.4, 230.4 - x2, y2 := 153.6, 25.6 - x3, y3 := 230.4, 128.0 - - gc.SetStrokeColor(image.NRGBAColor{0, 0, 0, 0xff}) - gc.SetLineWidth(10) - gc.MoveTo(x, y) - gc.CubicCurveTo(x1, y1, x2, y2, x3, y3) - gc.Stroke() - - gc.SetStrokeColor(image.NRGBAColor{0xFF, 0x33, 0x33, 0x99}) - - gc.SetLineWidth(6) - // draw segment of curve - gc.MoveTo(x, y) - gc.LineTo(x1, y1) - gc.MoveTo(x2, y2) - gc.LineTo(x3, y3) - gc.Stroke() -} - -var ( - width, height int -) - -func reshape(w, h int) { - /* Because Gil specified "screen coordinates" (presumably with an - upper-left origin), this short bit of code sets up the coordinate - system to correspond to actual window coodrinates. This code - wouldn't be required if you chose a (more typical in 3D) abstract - coordinate system. */ - gl.ClearColor(1, 1, 1, 1) - //fmt.Println(gl.GetString(gl.EXTENSIONS)) - gl.Viewport(0, 0, w, h) /* Establish viewing area to cover entire window. */ - gl.MatrixMode(gl.PROJECTION) /* Start modifying the projection matrix. */ - gl.LoadIdentity() /* Reset project matrix. */ - gl.Ortho(0, float64(w), 0, float64(h), -1, 1) /* Map abstract coords directly to window coords. */ - gl.Scalef(1, -1, 1) /* Invert Y axis so increasing Y goes down. */ - gl.Translatef(0, float32(-h), 0) /* Shift origin up to upper-left corner. */ - gl.Enable(gl.BLEND) - gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) - - width, height = w, h -} - -func display() { - gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) - gl.LineWidth(1) - p := NewGLPainter() - fmt.Println("draw") - gc := draw2d.NewImageGraphicContextFromPainter(p, image.Rect(0, 0, width, height)) - gc.Translate(0, 380) - gc.Scale(1, -1) - gc.Translate(0, -380) - interpreter := postscript.NewInterpreter(gc) - interpreter.ExecuteFile("../../tiger.ps") - gl.Flush() /* Single buffered, so needs a flush. */ -} - -func main() { - glut.Init() - glut.InitWindowSize(800, 800) - glut.CreateWindow("single triangle") - - glut.DisplayFunc(display) - glut.ReshapeFunc(reshape) - glut.MainLoop() -} diff --git a/cmd/testWalkDraw.go b/cmd/testWalkDraw.go index 8bb4626..fffde5e 100644 --- a/cmd/testWalkDraw.go +++ b/cmd/testWalkDraw.go @@ -128,7 +128,7 @@ func WndProc(hwnd, msg uint32, wparam, lparam int32) uintptr { gc.Restore() // back buf in - wingui.BitBlt(hdc, 0, 0, 100/*int(wndBuffer.Width)*/, 100/*int(wndBuffer.Height)*/, hdcWndBuffer, 0, 0, wingui.SRCCOPY) + wingui.BitBlt(hdc, 0, 0, int(wndBuffer.Width), int(wndBuffer.Height), hdcWndBuffer, 0, 0, wingui.SRCCOPY) wingui.EndPaint(hwnd, &ps) rc = wingui.DefWindowProc(hwnd, msg, wparam, lparam) fmt.Printf("Redraw in : %f ms\n", float64(dt)*1e-6) diff --git a/draw2d/image.go b/draw2d/image.go index 3e1bb69..829df46 100644 --- a/draw2d/image.go +++ b/draw2d/image.go @@ -6,7 +6,6 @@ import ( "exp/draw" "image" "log" - "time" "freetype-go.googlecode.com/hg/freetype" "freetype-go.googlecode.com/hg/freetype/raster" ) @@ -77,22 +76,22 @@ func (gc *ImageGraphicContext) Clear() { } func (gc *ImageGraphicContext) ClearRect(x1, y1, x2, y2 int) { - imageColor := image.NewColorImage(gc.current.FillColor) + imageColor := image.NewColorImage(gc.Current.FillColor) draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP) } func (gc *ImageGraphicContext) DrawImage(img image.Image) { - DrawImage(img, gc.img, gc.current.Tr, draw.Over, BilinearFilter) + DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter) } func (gc *ImageGraphicContext) FillString(text string) (cursor float64) { - gc.freetype.SetSrc(image.NewColorImage(gc.current.StrokeColor)) + gc.freetype.SetSrc(image.NewColorImage(gc.Current.StrokeColor)) // Draw the text. - x, y := gc.current.Path.LastPoint() - gc.current.Tr.Transform(&x, &y) - x0, fontSize := 0.0, gc.current.FontSize - gc.current.Tr.VectorTransform(&x0, &fontSize) - font := GetFont(gc.current.FontData) + x, y := gc.Current.Path.LastPoint() + gc.Current.Tr.Transform(&x, &y) + x0, fontSize := 0.0, gc.Current.FontSize + gc.Current.Tr.VectorTransform(&x0, &fontSize) + font := GetFont(gc.Current.FontData) if font == nil { font = GetFont(defaultFontData) } @@ -106,9 +105,9 @@ func (gc *ImageGraphicContext) FillString(text string) (cursor float64) { if err != nil { log.Println(err) } - x1, _ := gc.current.Path.LastPoint() + x1, _ := gc.Current.Path.LastPoint() x2, y2 := float64(p.X)/256, float64(p.Y)/256 - gc.current.Tr.InverseTransform(&x2, &y2) + gc.Current.Tr.InverseTransform(&x2, &y2) width := x2 - x1 return width } @@ -118,124 +117,121 @@ func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color image. gc.painter.SetColor(color) rasterizer.Rasterize(gc.painter) rasterizer.Clear() - gc.current.Path = new(PathStorage) + gc.Current.Path = new(PathStorage) } /**** First method ****/ func (gc *ImageGraphicContext) Stroke2(paths ...*PathStorage) { - paths = append(paths, gc.current.Path) + paths = append(paths, gc.Current.Path) gc.strokeRasterizer.UseNonZeroWinding = true rasterPath := new(raster.Path) var pathConverter *PathConverter - if gc.current.Dash != nil && len(gc.current.Dash) > 0 { - dasher := NewDashConverter(gc.current.Dash, gc.current.DashOffset, NewVertexAdder(rasterPath)) + if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { + dasher := NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, NewVertexAdder(rasterPath)) pathConverter = NewPathConverter(dasher) } else { pathConverter = NewPathConverter(NewVertexAdder(rasterPath)) } - pathConverter.ApproximationScale = gc.current.Tr.GetMaxAbsScaling() + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() pathConverter.Convert(paths...) - mta := NewMatrixTransformAdder(gc.current.Tr, gc.strokeRasterizer) - raster.Stroke(mta, *rasterPath, raster.Fix32(gc.current.LineWidth*256), gc.current.Cap.capper(), gc.current.Join.joiner()) + mta := NewMatrixTransformAdder(gc.Current.Tr, gc.strokeRasterizer) + raster.Stroke(mta, *rasterPath, raster.Fix32(gc.Current.LineWidth*256), gc.Current.Cap.capper(), gc.Current.Join.joiner()) - gc.paint(gc.strokeRasterizer, gc.current.StrokeColor) + gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } /**** second method ****/ func (gc *ImageGraphicContext) Stroke(paths ...*PathStorage) { - paths = append(paths, gc.current.Path) + 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 + 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) + 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.GetMaxAbsScaling() + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() pathConverter.Convert(paths...) - gc.paint(gc.strokeRasterizer, gc.current.StrokeColor) + gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } /**** first method ****/ func (gc *ImageGraphicContext) Fill2(paths ...*PathStorage) { - paths = append(paths, gc.current.Path) - gc.fillRasterizer.UseNonZeroWinding = gc.current.FillRule.fillRule() + paths = append(paths, gc.Current.Path) + gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() - pathConverter := NewPathConverter(NewVertexAdder(NewMatrixTransformAdder(gc.current.Tr, gc.fillRasterizer))) - pathConverter.ApproximationScale = gc.current.Tr.GetMaxAbsScaling() + pathConverter := NewPathConverter(NewVertexAdder(NewMatrixTransformAdder(gc.Current.Tr, gc.fillRasterizer))) + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() pathConverter.Convert(paths...) - gc.paint(gc.fillRasterizer, gc.current.FillColor) + gc.paint(gc.fillRasterizer, gc.Current.FillColor) } /**** second method ****/ func (gc *ImageGraphicContext) Fill(paths ...*PathStorage) { - paths = append(paths, gc.current.Path) - gc.fillRasterizer.UseNonZeroWinding = gc.current.FillRule.fillRule() + 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.GetMaxAbsScaling() + pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer))) + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() pathConverter.Convert(paths...) - t := time.Nanoseconds() - gc.paint(gc.fillRasterizer, gc.current.FillColor) - dt := time.Nanoseconds() - t - log.Printf("Paint during %f\n", float64(dt)*1e-6) + gc.paint(gc.fillRasterizer, gc.Current.FillColor) } func (gc *ImageGraphicContext) FillStroke2(paths ...*PathStorage) { - paths = append(paths, gc.current.Path) - gc.fillRasterizer.UseNonZeroWinding = gc.current.FillRule.fillRule() + paths = append(paths, gc.Current.Path) + gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() gc.strokeRasterizer.UseNonZeroWinding = true - filler := NewVertexMatrixTransform(gc.current.Tr, NewVertexAdder(gc.fillRasterizer)) + filler := NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer)) rasterPath := new(raster.Path) stroker := NewVertexAdder(rasterPath) demux := NewDemuxConverter(filler, stroker) pathConverter := NewPathConverter(demux) - pathConverter.ApproximationScale = gc.current.Tr.GetMaxAbsScaling() + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() pathConverter.Convert(paths...) - mta := NewMatrixTransformAdder(gc.current.Tr, gc.strokeRasterizer) - raster.Stroke(mta, *rasterPath, raster.Fix32(gc.current.LineWidth*256), gc.current.Cap.capper(), gc.current.Join.joiner()) + mta := NewMatrixTransformAdder(gc.Current.Tr, gc.strokeRasterizer) + raster.Stroke(mta, *rasterPath, raster.Fix32(gc.Current.LineWidth*256), gc.Current.Cap.capper(), gc.Current.Join.joiner()) - gc.paint(gc.fillRasterizer, gc.current.FillColor) - gc.paint(gc.strokeRasterizer, gc.current.StrokeColor) + gc.paint(gc.fillRasterizer, gc.Current.FillColor) + gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } /* second method */ func (gc *ImageGraphicContext) FillStroke(paths ...*PathStorage) { - gc.fillRasterizer.UseNonZeroWinding = gc.current.FillRule.fillRule() + gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() gc.strokeRasterizer.UseNonZeroWinding = true - filler := NewVertexMatrixTransform(gc.current.Tr, NewVertexAdder(gc.fillRasterizer)) + 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 + 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) + paths = append(paths, gc.Current.Path) pathConverter := NewPathConverter(demux) - pathConverter.ApproximationScale = gc.current.Tr.GetMaxAbsScaling() + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() pathConverter.Convert(paths...) - gc.paint(gc.fillRasterizer, gc.current.FillColor) - gc.paint(gc.strokeRasterizer, gc.current.StrokeColor) + gc.paint(gc.fillRasterizer, gc.Current.FillColor) + gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) } -func (f FillRule) fillRule() bool { +func (f FillRule) UseNonZeroWinding() bool { switch f { case FillRuleEvenOdd: return false diff --git a/draw2d/stack_gc.go b/draw2d/stack_gc.go index bd7cfec..b1e09e5 100644 --- a/draw2d/stack_gc.go +++ b/draw2d/stack_gc.go @@ -7,7 +7,7 @@ import ( ) type StackGraphicContext struct { - current *ContextStack + Current *ContextStack } type ContextStack struct { @@ -32,168 +32,168 @@ type ContextStack struct { */ func NewStackGraphicContext() *StackGraphicContext { gc := &StackGraphicContext{} - gc.current = new(ContextStack) - gc.current.Tr = NewIdentityMatrix() - gc.current.Path = new(PathStorage) - 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 + gc.Current = new(ContextStack) + gc.Current.Tr = NewIdentityMatrix() + gc.Current.Path = new(PathStorage) + 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 + return gc.Current.Tr } func (gc *StackGraphicContext) SetMatrixTransform(Tr MatrixTransform) { - gc.current.Tr = Tr + gc.Current.Tr = Tr } func (gc *StackGraphicContext) ComposeMatrixTransform(Tr MatrixTransform) { - gc.current.Tr = Tr.Multiply(gc.current.Tr) + gc.Current.Tr = Tr.Multiply(gc.Current.Tr) } func (gc *StackGraphicContext) Rotate(angle float64) { - gc.current.Tr = NewRotationMatrix(angle).Multiply(gc.current.Tr) + 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) + 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) + gc.Current.Tr = NewScaleMatrix(sx, sy).Multiply(gc.Current.Tr) } func (gc *StackGraphicContext) SetStrokeColor(c image.Color) { - gc.current.StrokeColor = c + gc.Current.StrokeColor = c } func (gc *StackGraphicContext) SetFillColor(c image.Color) { - gc.current.FillColor = c + gc.Current.FillColor = c } func (gc *StackGraphicContext) SetFillRule(f FillRule) { - gc.current.FillRule = f + gc.Current.FillRule = f } func (gc *StackGraphicContext) SetLineWidth(LineWidth float64) { - gc.current.LineWidth = LineWidth + gc.Current.LineWidth = LineWidth } func (gc *StackGraphicContext) SetLineCap(Cap Cap) { - gc.current.Cap = Cap + gc.Current.Cap = Cap } func (gc *StackGraphicContext) SetLineJoin(Join Join) { - gc.current.Join = Join + gc.Current.Join = Join } func (gc *StackGraphicContext) SetLineDash(Dash []float64, DashOffset float64) { - gc.current.Dash = Dash - gc.current.DashOffset = DashOffset + gc.Current.Dash = Dash + gc.Current.DashOffset = DashOffset } func (gc *StackGraphicContext) SetFontSize(FontSize float64) { - gc.current.FontSize = FontSize + gc.Current.FontSize = FontSize } func (gc *StackGraphicContext) GetFontSize() float64 { - return gc.current.FontSize + return gc.Current.FontSize } func (gc *StackGraphicContext) SetFontData(FontData FontData) { - gc.current.FontData = FontData + gc.Current.FontData = FontData } func (gc *StackGraphicContext) GetFontData() FontData { - return gc.current.FontData + return gc.Current.FontData } func (gc *StackGraphicContext) BeginPath() { - gc.current.Path = new(PathStorage) + gc.Current.Path = new(PathStorage) } func (gc *StackGraphicContext) IsEmpty() bool { - return gc.current.Path.IsEmpty() + return gc.Current.Path.IsEmpty() } func (gc *StackGraphicContext) LastPoint() (float64, float64) { - return gc.current.Path.LastPoint() + return gc.Current.Path.LastPoint() } func (gc *StackGraphicContext) MoveTo(x, y float64) { - gc.current.Path.MoveTo(x, y) + gc.Current.Path.MoveTo(x, y) } func (gc *StackGraphicContext) RMoveTo(dx, dy float64) { - gc.current.Path.RMoveTo(dx, dy) + gc.Current.Path.RMoveTo(dx, dy) } func (gc *StackGraphicContext) LineTo(x, y float64) { - gc.current.Path.LineTo(x, y) + gc.Current.Path.LineTo(x, y) } func (gc *StackGraphicContext) RLineTo(dx, dy float64) { - gc.current.Path.RLineTo(dx, dy) + gc.Current.Path.RLineTo(dx, dy) } func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) { - gc.current.Path.QuadCurveTo(cx, cy, x, y) + 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) + 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) + 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) + 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) + 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) + gc.Current.Path.RArcTo(dcx, dcy, rx, ry, startAngle, angle) } func (gc *StackGraphicContext) Close() { - gc.current.Path.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() - copy(context.Tr[:], gc.current.Tr[:]) - context.previous = gc.current - gc.current = context + 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() + 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 + if gc.Current.previous != nil { + oldContext := gc.Current + gc.Current = gc.Current.previous oldContext.previous = nil } } diff --git a/draw2dgl/gc.go b/draw2dgl/gc.go index 29b9ba0..18f6dd9 100644 --- a/draw2dgl/gc.go +++ b/draw2dgl/gc.go @@ -1 +1,158 @@ package draw2dgl + +import ( + "image" + "exp/draw" + "gl" + "freetype-go.googlecode.com/hg/freetype/raster" + "draw2d.googlecode.com/hg/draw2d" +) + +type GLPainter struct { + // The Porter-Duff composition operator. + Op draw.Op + // The 16-bit color to paint the spans. + cr, cg, cb uint8 + ca uint32 +} + +const M16 uint32 = 1<<16 - 1 + +// Paint satisfies the Painter interface by painting ss onto an image.RGBA. +func (p *GLPainter) Paint(ss []raster.Span, done bool) { + gl.Begin(gl.LINES) + for _, s := range ss { + ma := s.A >> 16 + a := ma * p.ca / M16 + gl.Color4ub(p.cr, p.cg, p.cb, uint8(a>>8)) + gl.Vertex2i(s.X0, s.Y) + gl.Vertex2i(s.X1, s.Y) + } + gl.End() +} + +// SetColor sets the color to paint the spans. +func (p *GLPainter) SetColor(c image.Color) { + r, g, b, a := c.RGBA() + if a == 0 { + p.cr = 0 + p.cg = 0 + p.cb = 0 + p.ca = a + } else { + p.cr = uint8((r * M16 / a) >> 8) + p.cg = uint8((g * M16 / a) >> 8) + p.cb = uint8((b * M16 / a) >> 8) + p.ca = a + } +} + +// NewRGBAPainter creates a new RGBAPainter for the given image. +func NewGLPainter() *GLPainter { + return &GLPainter{} +} + +type GraphicContext struct { + *draw2d.StackGraphicContext + painter *GLPainter + fillRasterizer *raster.Rasterizer + strokeRasterizer *raster.Rasterizer +} + +/** + * Create a new Graphic context from an image + */ +func NewGraphicContext(width, height int) *GraphicContext { + gc := &GraphicContext{ + draw2d.NewStackGraphicContext(), + NewGLPainter(), + raster.NewRasterizer(width, height), + raster.NewRasterizer(width, height), + } + return gc +} + +func (gc *GraphicContext) SetDPI(dpi int) { + +} + +func (gc *GraphicContext) GetDPI() int { + return -1 +} + +func (gc *GraphicContext) Clear() { + +} + +func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) { + +} + +func (gc *GraphicContext) DrawImage(img image.Image) { + +} + + +func (gc *GraphicContext) FillString(text string) (cursor float64) { + return 0 +} + + +func (gc *GraphicContext) paint(rasterizer *raster.Rasterizer, color image.Color) { + gc.painter.SetColor(color) + rasterizer.Rasterize(gc.painter) + rasterizer.Clear() +} + +func (gc *GraphicContext) Stroke(paths ...*draw2d.PathStorage) { + paths = append(paths, gc.Current.Path) + gc.strokeRasterizer.UseNonZeroWinding = true + + stroker := draw2d.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.NewVertexMatrixTransform(gc.Current.Tr, draw2d.NewVertexAdder(gc.strokeRasterizer))) + stroker.HalfLineWidth = gc.Current.LineWidth / 2 + var pathConverter *draw2d.PathConverter + if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 { + dasher := draw2d.NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker) + pathConverter = draw2d.NewPathConverter(dasher) + } else { + pathConverter = draw2d.NewPathConverter(stroker) + } + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() + pathConverter.Convert(paths...) + + gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) + gc.Current.Path = new(draw2d.PathStorage) +} + +func (gc *GraphicContext) Fill(paths ...*draw2d.PathStorage) { + paths = append(paths, gc.Current.Path) + gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() + + pathConverter := draw2d.NewPathConverter(draw2d.NewVertexMatrixTransform(gc.Current.Tr, draw2d.NewVertexAdder(gc.fillRasterizer))) + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() + pathConverter.Convert(paths...) + + gc.paint(gc.fillRasterizer, gc.Current.FillColor) + gc.Current.Path = new(draw2d.PathStorage) +} + +func (gc *GraphicContext) FillStroke(paths ...*draw2d.PathStorage) { + gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding() + gc.strokeRasterizer.UseNonZeroWinding = true + + filler := draw2d.NewVertexMatrixTransform(gc.Current.Tr, draw2d.NewVertexAdder(gc.fillRasterizer)) + + stroker := draw2d.NewLineStroker(gc.Current.Cap, gc.Current.Join, draw2d.NewVertexMatrixTransform(gc.Current.Tr, draw2d.NewVertexAdder(gc.strokeRasterizer))) + stroker.HalfLineWidth = gc.Current.LineWidth / 2 + + demux := draw2d.NewDemuxConverter(filler, stroker) + paths = append(paths, gc.Current.Path) + pathConverter := draw2d.NewPathConverter(demux) + pathConverter.ApproximationScale = gc.Current.Tr.GetMaxAbsScaling() + pathConverter.Convert(paths...) + + gc.paint(gc.fillRasterizer, gc.Current.FillColor) + gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor) + gc.Current.Path = new(draw2d.PathStorage) +} +