Merge branch 'master' into Clean-Up
Conflicts: arc.go curve/curve_test.go draw2dgl/gc.go draw2dimg/rgba_interpolation.go draw2dpdf/gc.go draw2dpdf/path_converter.go path_adder.go path_storage.go raster/raster_test.go vertex2d.go
This commit is contained in:
commit
7ef94ce784
47 changed files with 7074 additions and 216 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -20,3 +20,4 @@ _test*
|
||||||
**/*.dll
|
**/*.dll
|
||||||
**/core*[0-9]
|
**/core*[0-9]
|
||||||
.private
|
.private
|
||||||
|
|
||||||
|
|
3
AUTHORS
3
AUTHORS
|
@ -1 +1,2 @@
|
||||||
Laurent Le Goff
|
Laurent Le Goff
|
||||||
|
Stani Michiels, gmail:stani.be
|
28
README.md
28
README.md
|
@ -1,15 +1,19 @@
|
||||||
draw2d
|
draw2d
|
||||||
======
|
======
|
||||||
|
|
||||||
Package draw2d is a pure [go](http://golang.org) 2D vector graphics library with support for multiple output devices such as [images](http://golang.org/pkg/image) (draw2d), pdf documents (draw2dpdf) and opengl (draw2dopengl), which can also be used on the google app engine. It can be used as a pure go [Cairo](http://www.cairographics.org/) alternative.
|
Package draw2d is a pure [go](http://golang.org) 2D vector graphics library with support for multiple output devices such as [images](http://golang.org/pkg/image) (draw2d), pdf documents (draw2dpdf) and opengl (draw2dgl), which can also be used on the google app engine. It can be used as a pure go [Cairo](http://www.cairographics.org/) alternative. draw2d is released under the BSD license. See the [documentation](http://godoc.org/github.com/llgcode/draw2d) for more details.
|
||||||
|
|
||||||
See the [documentation](http://godoc.org/github.com/llgcode/draw2d) for more details.
|
[![geometry](https://raw.githubusercontent.com/llgcode/draw2d/master/output/samples/geometry.png)](https://raw.githubusercontent.com/llgcode/draw2d/master/resource/image/geometry.pdf)[![postscript](https://raw.githubusercontent.com/llgcode/draw2d/master/output/samples/postscript.png)](https://raw.githubusercontent.com/llgcode/draw2d/master/resource/image/postscript.pdf)
|
||||||
|
|
||||||
|
Click on an image above to get the pdf, generated with exactly the same draw2d code. The first image is the output of `samples/geometry`. The second image is the result of `samples/postcript`, which demonstrates that draw2d can draw postscript files into images or pdf documents with the [ps](https://github.com/llgcode/ps) package.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Operations in draw2d include stroking and filling polygons, arcs, Bézier curves, drawing images and text rendering with truetype fonts. All drawing operations can be transformed by affine transformations (scale, rotation, translation).
|
Operations in draw2d include stroking and filling polygons, arcs, Bézier curves, drawing images and text rendering with truetype fonts. All drawing operations can be transformed by affine transformations (scale, rotation, translation).
|
||||||
|
|
||||||
|
Package draw2d follows the conventions of the [HTML Canvas 2D Context](http://www.w3.org/TR/2dcontext/) for coordinate system, angles, etc...
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -42,7 +46,7 @@ gc.Close()
|
||||||
gc.FillStroke()
|
gc.FillStroke()
|
||||||
|
|
||||||
// Save to file
|
// Save to file
|
||||||
draw2d.SaveToPngFile(fn, dest)
|
draw2d.SaveToPngFile("hello.png", dest)
|
||||||
```
|
```
|
||||||
|
|
||||||
The same Go code can also generate a pdf document with package draw2dpdf:
|
The same Go code can also generate a pdf document with package draw2dpdf:
|
||||||
|
@ -65,13 +69,26 @@ gc.Close()
|
||||||
gc.FillStroke()
|
gc.FillStroke()
|
||||||
|
|
||||||
// Save to file
|
// Save to file
|
||||||
draw2dpdf.SaveToPdfFile(fn, dest)
|
draw2dpdf.SaveToPdfFile("hello.pdf", dest)
|
||||||
```
|
```
|
||||||
|
|
||||||
There are more examples here: https://github.com/llgcode/draw2d.samples
|
There are more examples here: https://github.com/llgcode/draw2d/tree/master/samples
|
||||||
|
|
||||||
Drawing on opengl is provided by the draw2dgl package.
|
Drawing on opengl is provided by the draw2dgl package.
|
||||||
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
|
||||||
|
The samples are run as tests from the root package folder `draw2d` by:
|
||||||
|
```
|
||||||
|
go test ./...
|
||||||
|
```
|
||||||
|
Or if you want to run with test coverage:
|
||||||
|
```
|
||||||
|
go test -cover ./... | grep -v "no test"
|
||||||
|
```
|
||||||
|
This will generate output by the different backends in the output folder.
|
||||||
|
|
||||||
Acknowledgments
|
Acknowledgments
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
@ -94,3 +111,4 @@ References
|
||||||
|
|
||||||
- [antigrain.com](http://www.antigrain.com)
|
- [antigrain.com](http://www.antigrain.com)
|
||||||
- [freetype-go](http://code.google.com/p/freetype-go)
|
- [freetype-go](http://code.google.com/p/freetype-go)
|
||||||
|
-
|
||||||
|
|
22
draw2d.go
22
draw2d.go
|
@ -3,8 +3,9 @@
|
||||||
|
|
||||||
// Package draw2d is a pure go 2D vector graphics library with support
|
// Package draw2d is a pure go 2D vector graphics library with support
|
||||||
// for multiple output devices such as images (draw2d), pdf documents
|
// for multiple output devices such as images (draw2d), pdf documents
|
||||||
// (draw2dpdf) and opengl (draw2dopengl), which can also be used on the
|
// (draw2dpdf) and opengl (draw2dgl), which can also be used on the
|
||||||
// google app engine. It can be used as a pure go Cairo alternative.
|
// google app engine. It can be used as a pure go Cairo alternative.
|
||||||
|
// draw2d is released under the BSD license.
|
||||||
//
|
//
|
||||||
// Features
|
// Features
|
||||||
//
|
//
|
||||||
|
@ -13,6 +14,8 @@
|
||||||
// All drawing operations can be transformed by affine transformations
|
// All drawing operations can be transformed by affine transformations
|
||||||
// (scale, rotation, translation).
|
// (scale, rotation, translation).
|
||||||
//
|
//
|
||||||
|
// Package draw2d follows the conventions of http://www.w3.org/TR/2dcontext for coordinate system, angles, etc...
|
||||||
|
//
|
||||||
// Installation
|
// Installation
|
||||||
//
|
//
|
||||||
// To install or update the package draw2d on your system, run:
|
// To install or update the package draw2d on your system, run:
|
||||||
|
@ -40,15 +43,25 @@
|
||||||
// gc.FillStroke()
|
// gc.FillStroke()
|
||||||
//
|
//
|
||||||
// // Save to file
|
// // Save to file
|
||||||
// draw2d.SaveToPngFile(fn, dest)
|
// draw2d.SaveToPngFile("hello.png", dest)
|
||||||
//
|
//
|
||||||
// There are more examples here:
|
// There are more examples here:
|
||||||
// https://github.com/llgcode/draw2d.samples
|
// https://github.com/llgcode/draw2d/tree/master/samples
|
||||||
//
|
//
|
||||||
// Drawing on pdf documents is provided by the draw2dpdf package.
|
// Drawing on pdf documents is provided by the draw2dpdf package.
|
||||||
// Drawing on opengl is provided by the draw2dgl package.
|
// Drawing on opengl is provided by the draw2dgl package.
|
||||||
// See subdirectories at the bottom of this page.
|
// See subdirectories at the bottom of this page.
|
||||||
//
|
//
|
||||||
|
// Testing
|
||||||
|
//
|
||||||
|
// The samples are run as tests from the root package folder `draw2d` by:
|
||||||
|
// go test ./...
|
||||||
|
//
|
||||||
|
// Or if you want to run with test coverage:
|
||||||
|
// go test -cover ./... | grep -v "no test"
|
||||||
|
//
|
||||||
|
// This will generate output by the different backends in the output folder.
|
||||||
|
//
|
||||||
// Acknowledgments
|
// Acknowledgments
|
||||||
//
|
//
|
||||||
// Laurent Le Goff wrote this library, inspired by Postscript and
|
// Laurent Le Goff wrote this library, inspired by Postscript and
|
||||||
|
@ -58,9 +71,6 @@
|
||||||
// graphic context (https://github.com/llgcode/ps). Stani Michiels
|
// graphic context (https://github.com/llgcode/ps). Stani Michiels
|
||||||
// implemented the pdf backend with the gofpdf package.
|
// implemented the pdf backend with the gofpdf package.
|
||||||
//
|
//
|
||||||
// The package depends on freetype-go package for its rasterization
|
|
||||||
// algorithm.
|
|
||||||
//
|
|
||||||
// Packages using draw2d
|
// Packages using draw2d
|
||||||
//
|
//
|
||||||
// - https://github.com/llgcode/ps: Postscript interpreter written in Go
|
// - https://github.com/llgcode/ps: Postscript interpreter written in Go
|
||||||
|
|
|
@ -46,9 +46,9 @@ func SubdivideCubic(c, c1, c2 []float64) {
|
||||||
c2[0], c2[1] = c1[6], c1[7]
|
c2[0], c2[1] = c1[6], c1[7]
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceCubic generate lines subdividing the cubic curve using a Flattener
|
// TraceCubic generate lines subdividing the cubic curve using a Liner
|
||||||
// flattening_threshold helps determines the flattening expectation of the curve
|
// flattening_threshold helps determines the flattening expectation of the curve
|
||||||
func TraceCubic(t Flattener, cubic []float64, flatteningThreshold float64) {
|
func TraceCubic(t Liner, cubic []float64, flatteningThreshold float64) {
|
||||||
// Allocation curves
|
// Allocation curves
|
||||||
var curves [CurveRecursionLimit * 8]float64
|
var curves [CurveRecursionLimit * 8]float64
|
||||||
copy(curves[0:8], cubic[0:8])
|
copy(curves[0:8], cubic[0:8])
|
||||||
|
@ -101,9 +101,9 @@ func SubdivideQuad(c, c1, c2 []float64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceQuad generate lines subdividing the curve using a Flattener
|
// TraceQuad generate lines subdividing the curve using a Liner
|
||||||
// flattening_threshold helps determines the flattening expectation of the curve
|
// flattening_threshold helps determines the flattening expectation of the curve
|
||||||
func TraceQuad(t Flattener, quad []float64, flatteningThreshold float64) {
|
func TraceQuad(t Liner, quad []float64, flatteningThreshold float64) {
|
||||||
// Allocates curves stack
|
// Allocates curves stack
|
||||||
var curves [CurveRecursionLimit * 6]float64
|
var curves [CurveRecursionLimit * 6]float64
|
||||||
copy(curves[0:6], quad[0:6])
|
copy(curves[0:6], quad[0:6])
|
||||||
|
@ -131,8 +131,8 @@ func TraceQuad(t Flattener, quad []float64, flatteningThreshold float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceArc trace an arc using a Flattener
|
// TraceArc trace an arc using a Liner
|
||||||
func TraceArc(t Flattener, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) {
|
func TraceArc(t Liner, x, y, rx, ry, start, angle, scale float64) (lastX, lastY float64) {
|
||||||
end := start + angle
|
end := start + angle
|
||||||
clockWise := true
|
clockWise := true
|
||||||
if angle < 0 {
|
if angle < 0 {
|
||||||
|
|
|
@ -36,7 +36,7 @@ var (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
os.Mkdir("test_results", 0666)
|
os.Mkdir("test_results", 0666)
|
||||||
f, err := os.Create("test_results/_test.html")
|
f, err := os.Create("../output/curve/_test.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -81,7 +81,7 @@ func TestCubicCurve(t *testing.T) {
|
||||||
raster.PolylineBresenham(img, image.Black, p.Points...)
|
raster.PolylineBresenham(img, image.Black, p.Points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||||
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.Points...)
|
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.Points...)
|
||||||
SaveToPngFile(fmt.Sprintf("test_results/_test%d.png", i/8), img)
|
SaveToPngFile(fmt.Sprintf("../output/curve/_test%d.png", i/8), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.Points))
|
log.Printf("Num of points: %d\n", len(p.Points))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
@ -97,7 +97,7 @@ func TestQuadCurve(t *testing.T) {
|
||||||
raster.PolylineBresenham(img, image.Black, p.Points...)
|
raster.PolylineBresenham(img, image.Black, p.Points...)
|
||||||
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
//drawPoints(img, image.NRGBAColor{0, 0, 0, 0xff}, curve[:]...)
|
||||||
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.Points...)
|
drawPoints(img, color.NRGBA{0, 0, 0, 0xff}, p.Points...)
|
||||||
SaveToPngFile(fmt.Sprintf("test_results/_testQuad%d.png", i), img)
|
SaveToPngFile(fmt.Sprintf("../output/curve/_testQuad%d.png", i), img)
|
||||||
log.Printf("Num of points: %d\n", len(p.Points))
|
log.Printf("Num of points: %d\n", len(p.Points))
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
|
@ -7,6 +7,12 @@ import (
|
||||||
"github.com/llgcode/draw2d"
|
"github.com/llgcode/draw2d"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Liner receive segment definition
|
||||||
|
type Liner interface {
|
||||||
|
// LineTo Draw a line from the current position to the point (x, y)
|
||||||
|
LineTo(x, y float64)
|
||||||
|
}
|
||||||
|
|
||||||
// Flattener receive segment definition
|
// Flattener receive segment definition
|
||||||
type Flattener interface {
|
type Flattener interface {
|
||||||
// MoveTo Start a New line from the point (x, y)
|
// MoveTo Start a New line from the point (x, y)
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
package draw2dimg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"image/draw"
|
|
||||||
|
|
||||||
"code.google.com/p/freetype-go/freetype/raster"
|
|
||||||
"code.google.com/p/freetype-go/freetype/truetype"
|
|
||||||
"github.com/llgcode/draw2d"
|
|
||||||
"github.com/llgcode/draw2d/draw2dbase"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Drawer struct {
|
|
||||||
matrix draw2d.Matrix
|
|
||||||
img draw.Image
|
|
||||||
painter Painter
|
|
||||||
fillRasterizer *raster.Rasterizer
|
|
||||||
strokeRasterizer *raster.Rasterizer
|
|
||||||
glyphBuf *truetype.GlyphBuf
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDrawer(img *image.RGBA) *Drawer {
|
|
||||||
width, height := img.Bounds().Dx(), img.Bounds().Dy()
|
|
||||||
return &Drawer{
|
|
||||||
draw2d.NewIdentityMatrix(),
|
|
||||||
img,
|
|
||||||
raster.NewRGBAPainter(img),
|
|
||||||
raster.NewRasterizer(width, height),
|
|
||||||
raster.NewRasterizer(width, height),
|
|
||||||
truetype.NewGlyphBuf(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drawer) Matrix() *draw2d.Matrix {
|
|
||||||
return &d.matrix
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drawer) Fill(path *draw2d.Path, style draw2d.FillStyle) {
|
|
||||||
switch fillStyle := style.(type) {
|
|
||||||
case draw2d.SolidFillStyle:
|
|
||||||
d.fillRasterizer.UseNonZeroWinding = fillStyle.FillRule == draw2d.FillRuleWinding
|
|
||||||
d.painter.SetColor(fillStyle.Color)
|
|
||||||
default:
|
|
||||||
panic("FillStyle not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
flattener := draw2dbase.Transformer{d.matrix, draw2dbase.FtLineBuilder{d.fillRasterizer}}
|
|
||||||
|
|
||||||
draw2dbase.Flatten(path, flattener, d.matrix.GetScale())
|
|
||||||
|
|
||||||
d.fillRasterizer.Rasterize(d.painter)
|
|
||||||
d.fillRasterizer.Clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drawer) Stroke(path *draw2d.Path, style draw2d.StrokeStyle) {
|
|
||||||
d.strokeRasterizer.UseNonZeroWinding = true
|
|
||||||
|
|
||||||
stroker := draw2dbase.NewLineStroker(style.LineCap, style.LineJoin, draw2dbase.Transformer{d.matrix, draw2dbase.FtLineBuilder{d.strokeRasterizer}})
|
|
||||||
stroker.HalfLineWidth = style.Width / 2
|
|
||||||
|
|
||||||
var liner draw2dbase.Flattener
|
|
||||||
if style.Dash != nil && len(style.Dash) > 0 {
|
|
||||||
liner = draw2dbase.NewDashConverter(style.Dash, style.DashOffset, stroker)
|
|
||||||
} else {
|
|
||||||
liner = stroker
|
|
||||||
}
|
|
||||||
|
|
||||||
draw2dbase.Flatten(path, liner, d.matrix.GetScale())
|
|
||||||
|
|
||||||
d.painter.SetColor(style.Color)
|
|
||||||
d.strokeRasterizer.Rasterize(d.painter)
|
|
||||||
d.strokeRasterizer.Clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drawer) Text(text string, x, y float64, style draw2d.TextStyle) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drawer) Image(image image.Image, x, y float64, scaling draw2d.ImageScaling) {
|
|
||||||
}
|
|
42
draw2dpdf/README.md
Normal file
42
draw2dpdf/README.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
draw2d pdf
|
||||||
|
==========
|
||||||
|
|
||||||
|
Package draw2dpdf provides a graphic context that can draw vector graphics and text on pdf file with the [gofpdf](https://github.com/jung-kurt/gofpdf) package.
|
||||||
|
|
||||||
|
Quick Start
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The following Go code generates a simple drawing and saves it to a pdf document:
|
||||||
|
```go
|
||||||
|
// Initialize the graphic context on an RGBA image
|
||||||
|
dest := draw2dpdf.NewPdf("L", "mm", "A4")
|
||||||
|
gc := draw2d.NewGraphicContext(dest)
|
||||||
|
|
||||||
|
// Set some properties
|
||||||
|
gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff})
|
||||||
|
gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
|
||||||
|
gc.SetLineWidth(5)
|
||||||
|
|
||||||
|
// Draw a closed shape
|
||||||
|
gc.MoveTo(10, 10) // should always be called first for a new path
|
||||||
|
gc.LineTo(100, 50)
|
||||||
|
gc.QuadCurveTo(100, 10, 10, 10)
|
||||||
|
gc.Close()
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// Save to file
|
||||||
|
draw2dpdf.SaveToPdfFile("hello.pdf", dest)
|
||||||
|
```
|
||||||
|
|
||||||
|
There are more examples here: https://github.com/llgcode/draw2d/tree/master/samples
|
||||||
|
|
||||||
|
Alternative backends
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
- Drawing on images is provided by the draw2d package.
|
||||||
|
- Drawing on opengl is provided by the draw2dgl package.
|
||||||
|
|
||||||
|
Acknowledgments
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The pdf backend uses https://github.com/jung-kurt/gofpdf
|
|
@ -2,7 +2,7 @@
|
||||||
// created: 26/06/2015 by Stani Michiels
|
// created: 26/06/2015 by Stani Michiels
|
||||||
|
|
||||||
// Package draw2dpdf provides a graphic context that can draw vector
|
// Package draw2dpdf provides a graphic context that can draw vector
|
||||||
// graphics and text on pdf file.
|
// graphics and text on pdf file with the gofpdf package.
|
||||||
//
|
//
|
||||||
// Quick Start
|
// Quick Start
|
||||||
//
|
//
|
||||||
|
@ -25,10 +25,12 @@
|
||||||
// gc.FillStroke()
|
// gc.FillStroke()
|
||||||
//
|
//
|
||||||
// // Save to file
|
// // Save to file
|
||||||
// draw2dpdf.SaveToPdfFile(fn, dest)
|
// draw2dpdf.SaveToPdfFile("hello.pdf", dest)
|
||||||
//
|
//
|
||||||
// There are more examples here:
|
// There are more examples here:
|
||||||
// https://github.com/llgcode/draw2d.samples
|
// https://github.com/llgcode/draw2d/tree/master/samples
|
||||||
|
//
|
||||||
|
// Alternative backends
|
||||||
//
|
//
|
||||||
// Drawing on images is provided by the draw2d package.
|
// Drawing on images is provided by the draw2d package.
|
||||||
// Drawing on opengl is provided by the draw2dgl package.
|
// Drawing on opengl is provided by the draw2dgl package.
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2015 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 26/06/2015 by Stani Michiels
|
||||||
|
|
||||||
package draw2dpdf
|
package draw2dpdf
|
||||||
|
|
||||||
import "github.com/jung-kurt/gofpdf"
|
import "github.com/jung-kurt/gofpdf"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright 2015 The draw2d Authors. All rights reserved.
|
// Copyright 2015 The draw2d Authors. All rights reserved.
|
||||||
// created: 26/06/2015 by Stani Michiels
|
// created: 26/06/2015 by Stani Michiels
|
||||||
// TODO: fonts, dpi
|
// TODO: dashed line
|
||||||
|
|
||||||
package draw2dpdf
|
package draw2dpdf
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"code.google.com/p/freetype-go/freetype/truetype"
|
"code.google.com/p/freetype-go/freetype/truetype"
|
||||||
|
@ -47,8 +46,14 @@ var (
|
||||||
// a page and set fill color to white.
|
// a page and set fill color to white.
|
||||||
func NewPdf(orientationStr, unitStr, sizeStr string) *gofpdf.Fpdf {
|
func NewPdf(orientationStr, unitStr, sizeStr string) *gofpdf.Fpdf {
|
||||||
pdf := gofpdf.New(orientationStr, unitStr, sizeStr, draw2d.GetFontFolder())
|
pdf := gofpdf.New(orientationStr, unitStr, sizeStr, draw2d.GetFontFolder())
|
||||||
|
// to be compatible with draw2d
|
||||||
|
pdf.SetMargins(0, 0, 0)
|
||||||
|
pdf.SetDrawColor(0, 0, 0)
|
||||||
|
pdf.SetFillColor(255, 255, 255)
|
||||||
|
pdf.SetLineCapStyle("round")
|
||||||
|
pdf.SetLineJoinStyle("round")
|
||||||
|
pdf.SetLineWidth(1)
|
||||||
pdf.AddPage()
|
pdf.AddPage()
|
||||||
pdf.SetFillColor(255, 255, 255) // to be compatible with draw2d
|
|
||||||
return pdf
|
return pdf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +96,7 @@ func NewGraphicContext(pdf *gofpdf.Fpdf) *GraphicContext {
|
||||||
// TODO: add type (tp) as parameter to argument list?
|
// TODO: add type (tp) as parameter to argument list?
|
||||||
func (gc *GraphicContext) DrawImage(image image.Image) {
|
func (gc *GraphicContext) DrawImage(image image.Image) {
|
||||||
name := strconv.Itoa(int(imageCount))
|
name := strconv.Itoa(int(imageCount))
|
||||||
imageCount += 1
|
imageCount++
|
||||||
tp := "PNG" // "JPG", "JPEG", "PNG" and "GIF"
|
tp := "PNG" // "JPG", "JPEG", "PNG" and "GIF"
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
png.Encode(b, image)
|
png.Encode(b, image)
|
||||||
|
@ -108,7 +113,8 @@ func (gc *GraphicContext) Clear() {
|
||||||
clearRect(gc, 0, 0, width, height)
|
clearRect(gc, 0, 0, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearRect draws a white rectangle over the specified area
|
// ClearRect draws a white rectangle over the specified area.
|
||||||
|
// Samples: line.
|
||||||
func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) {
|
func (gc *GraphicContext) ClearRect(x1, y1, x2, y2 int) {
|
||||||
clearRect(gc, float64(x1), float64(y1), float64(x2), float64(y2))
|
clearRect(gc, float64(x1), float64(y1), float64(x2), float64(y2))
|
||||||
}
|
}
|
||||||
|
@ -134,18 +140,31 @@ func (gc *GraphicContext) GetDPI() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStringBounds returns the approximate pixel bounds of the string s at x, y.
|
// GetStringBounds returns the approximate pixel bounds of the string s at x, y.
|
||||||
|
// 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 *GraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
|
func (gc *GraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
|
||||||
_, h := gc.pdf.GetFontSize()
|
_, h := gc.pdf.GetFontSize()
|
||||||
return 0, 0, gc.pdf.GetStringWidth(s), h
|
d := gc.pdf.GetFontDesc("", "")
|
||||||
|
if d.Ascent == 0 {
|
||||||
|
// not defined (standard font?), use average of 81%
|
||||||
|
top = 0.81 * h
|
||||||
|
} else {
|
||||||
|
top = -float64(d.Ascent) * h / float64(d.Ascent-d.Descent)
|
||||||
|
}
|
||||||
|
return 0, top, gc.pdf.GetStringWidth(s), top + h
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateStringPath creates a path from the string s at x, y, and returns the string width.
|
// CreateStringPath creates a path from the string s at x, y, and returns the string width.
|
||||||
func (gc *GraphicContext) CreateStringPath(text string, x, y float64) (cursor float64) {
|
func (gc *GraphicContext) CreateStringPath(text string, x, y float64) (cursor float64) {
|
||||||
_, _, w, h := gc.GetStringBounds(text)
|
//fpdf uses the top left corner
|
||||||
|
left, top, right, bottom := gc.GetStringBounds(text)
|
||||||
|
w := right - left
|
||||||
|
h := bottom - top
|
||||||
|
// gc.pdf.SetXY(x, y-h) do not use this as y-h might be negative
|
||||||
margin := gc.pdf.GetCellMargin()
|
margin := gc.pdf.GetCellMargin()
|
||||||
gc.pdf.MoveTo(x-margin, y+margin-0.82*h)
|
gc.pdf.MoveTo(x-left-margin, y+top)
|
||||||
gc.pdf.CellFormat(w, h, text, "", 0, "BL", false, 0, "")
|
gc.pdf.CellFormat(w, h, text, "", 0, "BL", false, 0, "")
|
||||||
// gc.pdf.Cell(w, h, text)
|
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,29 +192,53 @@ func (gc *GraphicContext) StrokeStringAt(text string, x, y float64) (cursor floa
|
||||||
|
|
||||||
// Stroke strokes the paths with the color specified by SetStrokeColor
|
// Stroke strokes the paths with the color specified by SetStrokeColor
|
||||||
func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
|
func (gc *GraphicContext) Stroke(paths ...*draw2d.Path) {
|
||||||
gc.draw("D", paths...)
|
_, _, _, alphaS := gc.Current.StrokeColor.RGBA()
|
||||||
|
gc.draw("D", alphaS, paths...)
|
||||||
|
gc.Current.Path.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill fills the paths with the color specified by SetFillColor
|
// Fill fills the paths with the color specified by SetFillColor
|
||||||
func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
|
func (gc *GraphicContext) Fill(paths ...*draw2d.Path) {
|
||||||
gc.draw("F", paths...)
|
style := "F"
|
||||||
|
if gc.Current.FillRule != draw2d.FillRuleWinding {
|
||||||
|
style += "*"
|
||||||
|
}
|
||||||
|
_, _, _, alphaF := gc.Current.FillColor.RGBA()
|
||||||
|
gc.draw(style, alphaF, paths...)
|
||||||
|
gc.Current.Path.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FillStroke first fills the paths and than strokes them
|
// FillStroke first fills the paths and than strokes them
|
||||||
func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
|
func (gc *GraphicContext) FillStroke(paths ...*draw2d.Path) {
|
||||||
gc.draw("FD", paths...)
|
var rule string
|
||||||
|
if gc.Current.FillRule != draw2d.FillRuleWinding {
|
||||||
|
rule = "*"
|
||||||
|
}
|
||||||
|
_, _, _, alphaS := gc.Current.StrokeColor.RGBA()
|
||||||
|
_, _, _, alphaF := gc.Current.FillColor.RGBA()
|
||||||
|
if alphaS == alphaF {
|
||||||
|
gc.draw("FD"+rule, alphaF, paths...)
|
||||||
|
} else {
|
||||||
|
gc.draw("F"+rule, alphaF, paths...)
|
||||||
|
gc.draw("S", alphaS, paths...)
|
||||||
|
}
|
||||||
|
gc.Current.Path.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
var logger = log.New(os.Stdout, "", log.Lshortfile)
|
var logger = log.New(os.Stdout, "", log.Lshortfile)
|
||||||
|
|
||||||
|
const alphaMax = float64(0xFFFF)
|
||||||
|
|
||||||
// draw fills and/or strokes paths
|
// draw fills and/or strokes paths
|
||||||
func (gc *GraphicContext) draw(style string, paths ...*draw2d.Path) {
|
func (gc *GraphicContext) draw(style string, alpha uint32, paths ...*draw2d.Path) {
|
||||||
paths = append(paths, gc.Current.Path)
|
paths = append(paths, gc.Current.Path)
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
ConvertPath(p, gc.pdf)
|
ConvertPath(p, gc.pdf)
|
||||||
}
|
}
|
||||||
if gc.Current.FillRule == draw2d.FillRuleWinding {
|
a := float64(alpha) / alphaMax
|
||||||
style += "*"
|
current, blendMode := gc.pdf.GetAlpha()
|
||||||
|
if a != current {
|
||||||
|
gc.pdf.SetAlpha(a, blendMode)
|
||||||
}
|
}
|
||||||
gc.pdf.DrawPath(style)
|
gc.pdf.DrawPath(style)
|
||||||
}
|
}
|
||||||
|
@ -242,8 +285,9 @@ func (gc *GraphicContext) SetFontData(fontData draw2d.FontData) {
|
||||||
}
|
}
|
||||||
fn := draw2d.FontFileName(fontData)
|
fn := draw2d.FontFileName(fontData)
|
||||||
fn = fn[:len(fn)-4]
|
fn = fn[:len(fn)-4]
|
||||||
jfn := filepath.Join(draw2d.GetFontFolder(), fn+".json")
|
size, _ := gc.pdf.GetFontSize()
|
||||||
gc.pdf.AddFont(fn, style, jfn)
|
gc.pdf.AddFont(fontData.Name, style, fn+".json")
|
||||||
|
gc.pdf.SetFont(fontData.Name, style, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFontSize sets the font size in points (as in ``a 12 point font'').
|
// SetFontSize sets the font size in points (as in ``a 12 point font'').
|
||||||
|
@ -254,6 +298,12 @@ func (gc *GraphicContext) SetFontSize(fontSize float64) {
|
||||||
gc.pdf.SetFontSize(fontSize * gc.Current.Scale)
|
gc.pdf.SetFontSize(fontSize * gc.Current.Scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLineDash sets the line dash pattern
|
||||||
|
func (gc *GraphicContext) SetLineDash(Dash []float64, DashOffset float64) {
|
||||||
|
gc.StackGraphicContext.SetLineDash(Dash, DashOffset)
|
||||||
|
gc.pdf.SetDashPattern(Dash, DashOffset)
|
||||||
|
}
|
||||||
|
|
||||||
// SetLineWidth sets the line width
|
// SetLineWidth sets the line width
|
||||||
func (gc *GraphicContext) SetLineWidth(LineWidth float64) {
|
func (gc *GraphicContext) SetLineWidth(LineWidth float64) {
|
||||||
gc.StackGraphicContext.SetLineWidth(LineWidth)
|
gc.StackGraphicContext.SetLineWidth(LineWidth)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// Copyright 2015 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 26/06/2015 by Stani Michiels
|
||||||
// See also test_test.go
|
// See also test_test.go
|
||||||
|
|
||||||
package draw2dpdf_test
|
package draw2dpdf_test
|
||||||
|
@ -6,29 +8,43 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/llgcode/draw2d"
|
"github.com/llgcode/draw2d"
|
||||||
"github.com/llgcode/draw2d.samples"
|
"github.com/llgcode/draw2d/samples/android"
|
||||||
"github.com/llgcode/draw2d.samples/android"
|
"github.com/llgcode/draw2d/samples/frameimage"
|
||||||
"github.com/llgcode/draw2d.samples/frameimage"
|
"github.com/llgcode/draw2d/samples/geometry"
|
||||||
"github.com/llgcode/draw2d.samples/gopher"
|
"github.com/llgcode/draw2d/samples/gopher"
|
||||||
"github.com/llgcode/draw2d.samples/helloworld"
|
"github.com/llgcode/draw2d/samples/gopher2"
|
||||||
"github.com/llgcode/draw2d.samples/line"
|
"github.com/llgcode/draw2d/samples/helloworld"
|
||||||
"github.com/llgcode/draw2d.samples/linecapjoin"
|
"github.com/llgcode/draw2d/samples/line"
|
||||||
"github.com/llgcode/draw2d.samples/postscript"
|
"github.com/llgcode/draw2d/samples/linecapjoin"
|
||||||
|
"github.com/llgcode/draw2d/samples/postscript"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSampleAndroid(t *testing.T) {
|
func TestSampleAndroid(t *testing.T) {
|
||||||
test(t, android.Main)
|
test(t, android.Main)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: FillString: w (width) is incorrect
|
||||||
|
func TestSampleGeometry(t *testing.T) {
|
||||||
|
// Set the global folder for searching fonts
|
||||||
|
// The pdf backend needs for every ttf file its corresponding
|
||||||
|
// json/.z file which is generated by gofpdf/makefont.
|
||||||
|
draw2d.SetFontFolder("../resource/font")
|
||||||
|
test(t, geometry.Main)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSampleGopher(t *testing.T) {
|
func TestSampleGopher(t *testing.T) {
|
||||||
test(t, gopher.Main)
|
test(t, gopher.Main)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSampleGopher2(t *testing.T) {
|
||||||
|
test(t, gopher2.Main)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSampleHelloWorld(t *testing.T) {
|
func TestSampleHelloWorld(t *testing.T) {
|
||||||
// Set the global folder for searching fonts
|
// Set the global folder for searching fonts
|
||||||
// The pdf backend needs for every ttf file its corresponding json
|
// The pdf backend needs for every ttf file its corresponding
|
||||||
// file which is generated by gofpdf/makefont.
|
// json/.z file which is generated by gofpdf/makefont.
|
||||||
draw2d.SetFontFolder(samples.Dir("helloworld", "../"))
|
draw2d.SetFontFolder("../resource/font")
|
||||||
test(t, helloworld.Main)
|
test(t, helloworld.Main)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
// Copyright 2015 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 26/06/2015 by Stani Michiels
|
||||||
|
|
||||||
// Package draw2dpdf_test gives test coverage with the command:
|
// Package draw2dpdf_test gives test coverage with the command:
|
||||||
// go test -cover ./... | grep -v "no test"
|
// go test -cover ./... | grep -v "no test"
|
||||||
// (It should be run from its parent draw2d directory.)
|
// (It should be run from its parent draw2d directory.)
|
||||||
package draw2dpdf_test
|
package draw2dpdf_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/llgcode/draw2d"
|
"github.com/llgcode/draw2d"
|
||||||
|
@ -18,18 +20,20 @@ func test(t *testing.T, draw sample) {
|
||||||
dest := draw2dpdf.NewPdf("L", "mm", "A4")
|
dest := draw2dpdf.NewPdf("L", "mm", "A4")
|
||||||
gc := draw2dpdf.NewGraphicContext(dest)
|
gc := draw2dpdf.NewGraphicContext(dest)
|
||||||
// Draw sample
|
// Draw sample
|
||||||
fn, err := draw(gc, "pdf")
|
output, err := draw(gc, "pdf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Drawing %q failed: %v", fn, err)
|
t.Errorf("Drawing %q failed: %v", output, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Save to pdf only if it doesn't exist because of git
|
/*
|
||||||
if _, err = os.Stat(fn); err == nil {
|
// Save to pdf only if it doesn't exist because of git
|
||||||
t.Skipf("Saving %q skipped, as it exists already. (Git would consider it modified.)", fn)
|
if _, err = os.Stat(output); err == nil {
|
||||||
return
|
t.Skipf("Saving %q skipped, as it exists already. (Git would consider it modified.)", output)
|
||||||
}
|
return
|
||||||
err = draw2dpdf.SaveToPdfFile(fn, dest)
|
}
|
||||||
|
*/
|
||||||
|
err = draw2dpdf.SaveToPdfFile(output, dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Saving %q failed: %v", fn, err)
|
t.Errorf("Saving %q failed: %v", output, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
font.go
18
font.go
|
@ -7,13 +7,15 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"code.google.com/p/freetype-go/freetype/truetype"
|
"code.google.com/p/freetype-go/freetype/truetype"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fontFolder = "../resource/font/"
|
fontFolder = "../resource/font/"
|
||||||
fonts = make(map[string]*truetype.Font)
|
fonts = make(map[string]*truetype.Font)
|
||||||
|
fontNamer FontFileNamer = FontFileName
|
||||||
)
|
)
|
||||||
|
|
||||||
type FontStyle byte
|
type FontStyle byte
|
||||||
|
@ -38,6 +40,8 @@ type FontData struct {
|
||||||
Style FontStyle
|
Style FontStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FontFileNamer func(fontData FontData) string
|
||||||
|
|
||||||
func FontFileName(fontData FontData) string {
|
func FontFileName(fontData FontData) string {
|
||||||
fontFileName := fontData.Name
|
fontFileName := fontData.Name
|
||||||
switch fontData.Family {
|
switch fontData.Family {
|
||||||
|
@ -62,11 +66,11 @@ func FontFileName(fontData FontData) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterFont(fontData FontData, font *truetype.Font) {
|
func RegisterFont(fontData FontData, font *truetype.Font) {
|
||||||
fonts[FontFileName(fontData)] = font
|
fonts[fontNamer(fontData)] = font
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFont(fontData FontData) *truetype.Font {
|
func GetFont(fontData FontData) *truetype.Font {
|
||||||
fontFileName := FontFileName(fontData)
|
fontFileName := fontNamer(fontData)
|
||||||
font := fonts[fontFileName]
|
font := fonts[fontFileName]
|
||||||
if font != nil {
|
if font != nil {
|
||||||
return font
|
return font
|
||||||
|
@ -80,7 +84,11 @@ func GetFontFolder() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetFontFolder(folder string) {
|
func SetFontFolder(folder string) {
|
||||||
fontFolder = folder
|
fontFolder = filepath.Clean(folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetFontNamer(fn FontFileNamer) {
|
||||||
|
fontNamer = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFont(fontFileName string) *truetype.Font {
|
func loadFont(fontFileName string) *truetype.Font {
|
||||||
|
|
11
output/README.md
Normal file
11
output/README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Demo output
|
||||||
|
===========
|
||||||
|
|
||||||
|
These folders are empty when you check out the git repository. The output is generated by the tests:
|
||||||
|
```
|
||||||
|
go test ./...
|
||||||
|
```
|
||||||
|
or with coverage:
|
||||||
|
```
|
||||||
|
go test -cover ./... | grep -v "no test"
|
||||||
|
```
|
4
output/curve/.gitignore
vendored
Normal file
4
output/curve/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
4
output/raster/.gitignore
vendored
Normal file
4
output/raster/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
6
output/samples/.gitignore
vendored
Normal file
6
output/samples/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
||||||
|
!geometry.png
|
||||||
|
!postscript.png
|
BIN
output/samples/geometry.png
Normal file
BIN
output/samples/geometry.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
BIN
output/samples/postscript.png
Normal file
BIN
output/samples/postscript.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
|
@ -1,39 +1,16 @@
|
||||||
package raster
|
package raster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/png"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.google.com/p/freetype-go/freetype/raster"
|
"code.google.com/p/freetype-go/freetype/raster"
|
||||||
"github.com/llgcode/draw2d/curve"
|
"github.com/llgcode/draw2d/draw2dbase"
|
||||||
|
"github.com/llgcode/draw2d/draw2dimg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var flattening_threshold float64 = 0.5
|
var flatteningThreshold = 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 {
|
type Path struct {
|
||||||
points []float64
|
points []float64
|
||||||
|
@ -54,53 +31,59 @@ func (p *Path) LineTo(x, y float64) {
|
||||||
func TestFreetype(t *testing.T) {
|
func TestFreetype(t *testing.T) {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5)
|
||||||
c.Trace(&p, flattening_threshold)
|
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := color.RGBA{0, 0, 0, 0xff}
|
color := color.RGBA{0, 0, 0, 0xff}
|
||||||
|
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
||||||
rasterizer := raster.NewRasterizer(200, 200)
|
rasterizer := raster.NewRasterizer(200, 200)
|
||||||
rasterizer.UseNonZeroWinding = false
|
rasterizer.UseNonZeroWinding = false
|
||||||
rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
|
rasterizer.Start(raster.Point{
|
||||||
|
X: raster.Fix32(10 * 256),
|
||||||
|
Y: raster.Fix32(190 * 256)})
|
||||||
for j := 0; j < len(poly); j = j + 2 {
|
for j := 0; j < len(poly); j = j + 2 {
|
||||||
rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
|
rasterizer.Add1(raster.Point{
|
||||||
|
X: raster.Fix32(poly[j] * 256),
|
||||||
|
Y: raster.Fix32(poly[j+1] * 256)})
|
||||||
}
|
}
|
||||||
painter := raster.NewRGBAPainter(img)
|
painter := raster.NewRGBAPainter(img)
|
||||||
painter.SetColor(color)
|
painter.SetColor(color)
|
||||||
rasterizer.Rasterize(painter)
|
rasterizer.Rasterize(painter)
|
||||||
|
|
||||||
savepng("_testFreetype.png", img)
|
draw2dimg.SaveToPngFile("../output/raster/TestFreetype.png", img)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFreetypeNonZeroWinding(t *testing.T) {
|
func TestFreetypeNonZeroWinding(t *testing.T) {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5)
|
||||||
c.Trace(&p, flattening_threshold)
|
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := color.RGBA{0, 0, 0, 0xff}
|
color := color.RGBA{0, 0, 0, 0xff}
|
||||||
|
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
||||||
rasterizer := raster.NewRasterizer(200, 200)
|
rasterizer := raster.NewRasterizer(200, 200)
|
||||||
rasterizer.UseNonZeroWinding = true
|
rasterizer.UseNonZeroWinding = true
|
||||||
rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
|
rasterizer.Start(raster.Point{
|
||||||
|
X: raster.Fix32(10 * 256),
|
||||||
|
Y: raster.Fix32(190 * 256)})
|
||||||
for j := 0; j < len(poly); j = j + 2 {
|
for j := 0; j < len(poly); j = j + 2 {
|
||||||
rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
|
rasterizer.Add1(raster.Point{
|
||||||
|
X: raster.Fix32(poly[j] * 256),
|
||||||
|
Y: raster.Fix32(poly[j+1] * 256)})
|
||||||
}
|
}
|
||||||
painter := raster.NewRGBAPainter(img)
|
painter := raster.NewRGBAPainter(img)
|
||||||
painter.SetColor(color)
|
painter.SetColor(color)
|
||||||
rasterizer.Rasterize(painter)
|
rasterizer.Rasterize(painter)
|
||||||
|
|
||||||
savepng("_testFreetypeNonZeroWinding.png", img)
|
draw2dimg.SaveToPngFile("../output/raster/TestFreetypeNonZeroWinding.png", img)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRasterizer(t *testing.T) {
|
func TestRasterizer(t *testing.T) {
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5)
|
||||||
c.Trace(&p, flattening_threshold)
|
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := color.RGBA{0, 0, 0, 0xff}
|
color := color.RGBA{0, 0, 0, 0xff}
|
||||||
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
||||||
|
@ -108,15 +91,15 @@ func TestRasterizer(t *testing.T) {
|
||||||
//PolylineBresenham(img, image.Black, poly...)
|
//PolylineBresenham(img, image.Black, poly...)
|
||||||
|
|
||||||
r.RenderEvenOdd(img, &color, &poly, tr)
|
r.RenderEvenOdd(img, &color, &poly, tr)
|
||||||
savepng("_testRasterizer.png", img)
|
draw2dimg.SaveToPngFile("../output/raster/TestRasterizer.png", img)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRasterizerNonZeroWinding(t *testing.T) {
|
func TestRasterizerNonZeroWinding(t *testing.T) {
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5)
|
||||||
c.Trace(&p, flattening_threshold)
|
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := color.RGBA{0, 0, 0, 0xff}
|
color := color.RGBA{0, 0, 0, 0xff}
|
||||||
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
||||||
|
@ -124,14 +107,14 @@ func TestRasterizerNonZeroWinding(t *testing.T) {
|
||||||
//PolylineBresenham(img, image.Black, poly...)
|
//PolylineBresenham(img, image.Black, poly...)
|
||||||
|
|
||||||
r.RenderNonZeroWinding(img, &color, &poly, tr)
|
r.RenderNonZeroWinding(img, &color, &poly, tr)
|
||||||
savepng("_testRasterizerNonZeroWinding.png", img)
|
draw2dimg.SaveToPngFile("../output/raster/TestRasterizerNonZeroWinding.png", img)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkFreetype(b *testing.B) {
|
func BenchmarkFreetype(b *testing.B) {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5)
|
||||||
c.Trace(&p, flattening_threshold)
|
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := color.RGBA{0, 0, 0, 0xff}
|
color := color.RGBA{0, 0, 0, 0xff}
|
||||||
|
|
||||||
|
@ -139,20 +122,25 @@ func BenchmarkFreetype(b *testing.B) {
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
||||||
rasterizer := raster.NewRasterizer(200, 200)
|
rasterizer := raster.NewRasterizer(200, 200)
|
||||||
rasterizer.UseNonZeroWinding = false
|
rasterizer.UseNonZeroWinding = false
|
||||||
rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
|
rasterizer.Start(raster.Point{
|
||||||
|
X: raster.Fix32(10 * 256),
|
||||||
|
Y: raster.Fix32(190 * 256)})
|
||||||
for j := 0; j < len(poly); j = j + 2 {
|
for j := 0; j < len(poly); j = j + 2 {
|
||||||
rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
|
rasterizer.Add1(raster.Point{
|
||||||
|
X: raster.Fix32(poly[j] * 256),
|
||||||
|
Y: raster.Fix32(poly[j+1] * 256)})
|
||||||
}
|
}
|
||||||
painter := raster.NewRGBAPainter(img)
|
painter := raster.NewRGBAPainter(img)
|
||||||
painter.SetColor(color)
|
painter.SetColor(color)
|
||||||
rasterizer.Rasterize(painter)
|
rasterizer.Rasterize(painter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkFreetypeNonZeroWinding(b *testing.B) {
|
func BenchmarkFreetypeNonZeroWinding(b *testing.B) {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5)
|
||||||
c.Trace(&p, flattening_threshold)
|
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := color.RGBA{0, 0, 0, 0xff}
|
color := color.RGBA{0, 0, 0, 0xff}
|
||||||
|
|
||||||
|
@ -160,9 +148,13 @@ func BenchmarkFreetypeNonZeroWinding(b *testing.B) {
|
||||||
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
||||||
rasterizer := raster.NewRasterizer(200, 200)
|
rasterizer := raster.NewRasterizer(200, 200)
|
||||||
rasterizer.UseNonZeroWinding = true
|
rasterizer.UseNonZeroWinding = true
|
||||||
rasterizer.Start(raster.Point{raster.Fix32(10 * 256), raster.Fix32(190 * 256)})
|
rasterizer.Start(raster.Point{
|
||||||
|
X: raster.Fix32(10 * 256),
|
||||||
|
Y: raster.Fix32(190 * 256)})
|
||||||
for j := 0; j < len(poly); j = j + 2 {
|
for j := 0; j < len(poly); j = j + 2 {
|
||||||
rasterizer.Add1(raster.Point{raster.Fix32(poly[j] * 256), raster.Fix32(poly[j+1] * 256)})
|
rasterizer.Add1(raster.Point{
|
||||||
|
X: raster.Fix32(poly[j] * 256),
|
||||||
|
Y: raster.Fix32(poly[j+1] * 256)})
|
||||||
}
|
}
|
||||||
painter := raster.NewRGBAPainter(img)
|
painter := raster.NewRGBAPainter(img)
|
||||||
painter.SetColor(color)
|
painter.SetColor(color)
|
||||||
|
@ -173,8 +165,8 @@ func BenchmarkFreetypeNonZeroWinding(b *testing.B) {
|
||||||
func BenchmarkRasterizerNonZeroWinding(b *testing.B) {
|
func BenchmarkRasterizerNonZeroWinding(b *testing.B) {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5)
|
||||||
c.Trace(&p, flattening_threshold)
|
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := color.RGBA{0, 0, 0, 0xff}
|
color := color.RGBA{0, 0, 0, 0xff}
|
||||||
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
||||||
|
@ -188,8 +180,8 @@ func BenchmarkRasterizerNonZeroWinding(b *testing.B) {
|
||||||
func BenchmarkRasterizer(b *testing.B) {
|
func BenchmarkRasterizer(b *testing.B) {
|
||||||
var p Path
|
var p Path
|
||||||
p.LineTo(10, 190)
|
p.LineTo(10, 190)
|
||||||
c := curve.CubicCurveFloat64{10, 190, 10, 10, 190, 10, 190, 190}
|
draw2dbase.TraceCubic(&p, []float64{10, 190, 10, 10, 190, 10, 190, 190}, 0.5)
|
||||||
c.Trace(&p, flattening_threshold)
|
|
||||||
poly := Polygon(p.points)
|
poly := Polygon(p.points)
|
||||||
color := color.RGBA{0, 0, 0, 0xff}
|
color := color.RGBA{0, 0, 0, 0xff}
|
||||||
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
tr := [6]float64{1, 0, 0, 1, 0, 0}
|
||||||
|
|
1
resource/font/luximbi.json
Normal file
1
resource/font/luximbi.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"Tp":"TrueType","Name":"LuxiMono-BoldOblique","Desc":{"Ascent":783,"Descent":-205,"CapHeight":783,"Flags":97,"FontBBox":{"Xmin":-29,"Ymin":-211,"Xmax":764,"Ymax":1012},"ItalicAngle":-8,"StemV":120,"MissingWidth":600},"Up":0,"Ut":0,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,653,600,653,600,600,653,600,653,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600],"Enc":"cp1252","Diff":"","File":"luximbi.z","Size1":0,"Size2":0,"OriginalSize":69872,"I":0,"N":0,"DiffN":0}
|
BIN
resource/font/luximbi.z
Normal file
BIN
resource/font/luximbi.z
Normal file
Binary file not shown.
BIN
resource/image/geometry.pdf
Normal file
BIN
resource/image/geometry.pdf
Normal file
Binary file not shown.
BIN
resource/image/gopher.png
Normal file
BIN
resource/image/gopher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
BIN
resource/image/postscript.pdf
Normal file
BIN
resource/image/postscript.pdf
Normal file
Binary file not shown.
2733
resource/image/tiger.ps
Normal file
2733
resource/image/tiger.ps
Normal file
File diff suppressed because it is too large
Load diff
88
samples/README.md
Normal file
88
samples/README.md
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
draw2d samples
|
||||||
|
==============
|
||||||
|
|
||||||
|
Various samples for using draw2d
|
||||||
|
|
||||||
|
Using the image backend
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
The following Go code draws the android sample on a png image:
|
||||||
|
|
||||||
|
```
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/samples/android"
|
||||||
|
)
|
||||||
|
|
||||||
|
function main(){}
|
||||||
|
// Initialize the graphic context on an RGBA image
|
||||||
|
dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
|
||||||
|
gc := draw2d.NewGraphicContext(dest)
|
||||||
|
// Draw Android logo
|
||||||
|
fn, err := android.Main(gc, "png")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Drawing %q failed: %v", fn, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Save to png
|
||||||
|
err = draw2d.SaveToPngFile(fn, dest)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Saving %q failed: %v", fn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the pdf backend
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The following Go code draws the android sample on a pdf document:
|
||||||
|
|
||||||
|
```
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d/draw2dpdf"
|
||||||
|
"github.com/llgcode/draw2d/samples/android"
|
||||||
|
)
|
||||||
|
|
||||||
|
function main(){}
|
||||||
|
// Initialize the graphic context on a pdf document
|
||||||
|
dest := draw2dpdf.NewPdf("L", "mm", "A4")
|
||||||
|
gc := draw2dpdf.NewGraphicContext(dest)
|
||||||
|
// Draw Android logo
|
||||||
|
fn, err := android.Main(gc, "png")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Drawing %q failed: %v", fn, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Save to pdf
|
||||||
|
err = draw2dpdf.SaveToPdfFile(fn, dest)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Saving %q failed: %v", fn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
|
||||||
|
These samples are run as tests from the root package folder `draw2d` by:
|
||||||
|
```
|
||||||
|
go test ./...
|
||||||
|
```
|
||||||
|
Or if you want to run with test coverage:
|
||||||
|
```
|
||||||
|
go test -cover ./... | grep -v "no test"
|
||||||
|
```
|
||||||
|
The following files are responsible to run the image tests:
|
||||||
|
```
|
||||||
|
draw2d/test_test.go
|
||||||
|
draw2d/samples_test.go
|
||||||
|
```
|
||||||
|
The following files are responsible to run the pdf tests:
|
||||||
|
```
|
||||||
|
draw2d/pdf/test_test.go
|
||||||
|
draw2dpdf/samples_test.go
|
||||||
|
```
|
76
samples/android/android.go
Normal file
76
samples/android/android.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 21/11/2010 by Laurent Le Goff
|
||||||
|
|
||||||
|
// Package android draws an android avatar.
|
||||||
|
package android
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dkit"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws a droid and returns the filename. This should only be
|
||||||
|
// used during testing.
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
// Draw the droid
|
||||||
|
Draw(gc, 65, 0)
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("android", ext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the droid on a certain position.
|
||||||
|
func Draw(gc draw2d.GraphicContext, x, y float64) {
|
||||||
|
// set the fill and stroke color of the droid
|
||||||
|
gc.SetFillColor(color.RGBA{0x44, 0xff, 0x44, 0xff})
|
||||||
|
gc.SetStrokeColor(color.RGBA{0x44, 0x44, 0x44, 0xff})
|
||||||
|
|
||||||
|
// set line properties
|
||||||
|
gc.SetLineCap(draw2d.RoundCap)
|
||||||
|
gc.SetLineWidth(5)
|
||||||
|
|
||||||
|
// head
|
||||||
|
gc.MoveTo(x+30, y+70)
|
||||||
|
gc.ArcTo(x+80, y+70, 50, 50, 180*(math.Pi/180), 180*(math.Pi/180))
|
||||||
|
gc.Close()
|
||||||
|
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()
|
||||||
|
|
||||||
|
// left eye
|
||||||
|
draw2dkit.Circle(gc, x+60, y+45, 5)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// right eye
|
||||||
|
draw2dkit.Circle(gc, x+100, y+45, 5)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// body
|
||||||
|
draw2dkit.RoundedRectangle(gc, x+30, y+75, x+30+100, y+75+90, 10, 10)
|
||||||
|
gc.FillStroke()
|
||||||
|
draw2dkit.Rectangle(gc, x+30, y+75, x+30+100, y+75+80)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// left arm
|
||||||
|
draw2dkit.RoundedRectangle(gc, x+5, y+80, x+5+20, y+80+70, 10, 10)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// right arm
|
||||||
|
draw2dkit.RoundedRectangle(gc, x+135, y+80, x+135+20, y+80+70, 10, 10)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// left leg
|
||||||
|
draw2dkit.RoundedRectangle(gc, x+50, y+150, x+50+20, y+150+50, 10, 10)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// right leg
|
||||||
|
draw2dkit.RoundedRectangle(gc, x+90, y+150, x+90+20, y+150+50, 10, 10)
|
||||||
|
gc.FillStroke()
|
||||||
|
}
|
8
samples/appengine/app.yaml
Normal file
8
samples/appengine/app.yaml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
application: draw2d-test
|
||||||
|
version: 1
|
||||||
|
runtime: go
|
||||||
|
api_version: go1
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- url: /.*
|
||||||
|
script: _go_app
|
73
samples/appengine/server.go
Normal file
73
samples/appengine/server.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// +build appengine
|
||||||
|
|
||||||
|
// Package gae demonstrates draw2d on a Google appengine server.
|
||||||
|
package gae
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dpdf"
|
||||||
|
"github.com/llgcode/draw2d/samples/android"
|
||||||
|
|
||||||
|
"appengine"
|
||||||
|
)
|
||||||
|
|
||||||
|
type appError struct {
|
||||||
|
Error error
|
||||||
|
Message string
|
||||||
|
Code int
|
||||||
|
}
|
||||||
|
|
||||||
|
type appHandler func(http.ResponseWriter, *http.Request) *appError
|
||||||
|
|
||||||
|
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
|
||||||
|
c := appengine.NewContext(r)
|
||||||
|
c.Errorf("%v", e.Error)
|
||||||
|
http.Error(w, e.Message, e.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
http.Handle("/pdf", appHandler(pdf))
|
||||||
|
http.Handle("/png", appHandler(imgPng))
|
||||||
|
}
|
||||||
|
|
||||||
|
func pdf(w http.ResponseWriter, r *http.Request) *appError {
|
||||||
|
w.Header().Set("Content-type", "application/pdf")
|
||||||
|
|
||||||
|
// Initialize the graphic context on an pdf document
|
||||||
|
dest := draw2dpdf.NewPdf("L", "mm", "A4")
|
||||||
|
gc := draw2dpdf.NewGraphicContext(dest)
|
||||||
|
|
||||||
|
// Draw sample
|
||||||
|
android.Draw(gc, 65, 0)
|
||||||
|
|
||||||
|
err := dest.Output(w)
|
||||||
|
if err != nil {
|
||||||
|
return &appError{err, fmt.Sprintf("Can't write: %s", err), 500}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func imgPng(w http.ResponseWriter, r *http.Request) *appError {
|
||||||
|
w.Header().Set("Content-type", "image/png")
|
||||||
|
|
||||||
|
// Initialize the graphic context on an RGBA image
|
||||||
|
dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
|
||||||
|
gc := draw2d.NewGraphicContext(dest)
|
||||||
|
|
||||||
|
// Draw sample
|
||||||
|
android.Draw(gc, 65, 0)
|
||||||
|
|
||||||
|
err := png.Encode(w, dest)
|
||||||
|
if err != nil {
|
||||||
|
return &appError{err, fmt.Sprintf("Can't encode: %s", err), 500}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
60
samples/frameimage/frameimage.go
Normal file
60
samples/frameimage/frameimage.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 21/11/2010 by Laurent Le Goff, Stani Michiels
|
||||||
|
|
||||||
|
// Package frameimage centers a png image and rotates it.
|
||||||
|
package frameimage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dimg"
|
||||||
|
"github.com/llgcode/draw2d/draw2dkit"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws the image frame and returns the filename.
|
||||||
|
// This should only be used during testing.
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
// Margin between the image and the frame
|
||||||
|
const margin = 30
|
||||||
|
// Line width od the frame
|
||||||
|
const lineWidth = 3
|
||||||
|
|
||||||
|
// Gopher image
|
||||||
|
gopher := samples.Resource("image", "gopher.png", ext)
|
||||||
|
|
||||||
|
// Draw gopher
|
||||||
|
err := Draw(gc, gopher, 297, 210, margin, lineWidth)
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("frameimage", ext), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the image frame with certain parameters.
|
||||||
|
func Draw(gc draw2d.GraphicContext, png string,
|
||||||
|
dw, dh, margin, lineWidth float64) error {
|
||||||
|
// Draw frame
|
||||||
|
draw2dkit.RoundedRectangle(gc, lineWidth, lineWidth, dw-lineWidth, dh-lineWidth, 100, 100)
|
||||||
|
gc.SetLineWidth(lineWidth)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// load the source image
|
||||||
|
source, err := draw2dimg.LoadFromPngFile(png)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Size of source image
|
||||||
|
sw, sh := float64(source.Bounds().Dx()), float64(source.Bounds().Dy())
|
||||||
|
// Draw image to fit in the frame
|
||||||
|
// TODO Seems to have a transform bug here on draw image
|
||||||
|
scale := math.Min((dw-margin*2)/sw, (dh-margin*2)/sh)
|
||||||
|
gc.Save()
|
||||||
|
gc.Translate((dw-sw*scale)/2, (dh-sh*scale)/2)
|
||||||
|
gc.Scale(scale, scale)
|
||||||
|
gc.Rotate(0.2)
|
||||||
|
|
||||||
|
gc.DrawImage(source)
|
||||||
|
gc.Restore()
|
||||||
|
return nil
|
||||||
|
}
|
334
samples/geometry/geometry.go
Normal file
334
samples/geometry/geometry.go
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 21/11/2010 by Laurent Le Goff
|
||||||
|
|
||||||
|
// Package geometry draws some geometric tests.
|
||||||
|
package geometry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dkit"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
"github.com/llgcode/draw2d/samples/gopher2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws geometry and returns the filename. This should only be
|
||||||
|
// used during testing.
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
// Draw the droid
|
||||||
|
Draw(gc, 297, 210)
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("geometry", ext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bubble draws a text balloon.
|
||||||
|
func Bubble(gc draw2d.GraphicContext, x, y, width, height float64) {
|
||||||
|
sx, sy := width/100, height/100
|
||||||
|
gc.MoveTo(x+sx*50, y)
|
||||||
|
gc.QuadCurveTo(x, y, x, y+sy*37.5)
|
||||||
|
gc.QuadCurveTo(x, y+sy*75, x+sx*25, y+sy*75)
|
||||||
|
gc.QuadCurveTo(x+sx*25, y+sy*95, x+sx*5, y+sy*100)
|
||||||
|
gc.QuadCurveTo(x+sx*35, y+sy*95, x+sx*40, y+sy*75)
|
||||||
|
gc.QuadCurveTo(x+sx*100, y+sy*75, x+sx*100, y+sy*37.5)
|
||||||
|
gc.QuadCurveTo(x+sx*100, y, x+sx*50, y)
|
||||||
|
gc.Stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurveRectangle draws a rectangle with bezier curves (not rounded rectangle).
|
||||||
|
func CurveRectangle(gc draw2d.GraphicContext, x0, y0,
|
||||||
|
rectWidth, rectHeight float64, stroke, fill color.Color) {
|
||||||
|
radius := (rectWidth + rectHeight) / 4
|
||||||
|
|
||||||
|
x1 := x0 + rectWidth
|
||||||
|
y1 := y0 + rectHeight
|
||||||
|
if rectWidth/2 < radius {
|
||||||
|
if rectHeight/2 < radius {
|
||||||
|
gc.MoveTo(x0, (y0+y1)/2)
|
||||||
|
gc.CubicCurveTo(x0, y0, x0, y0, (x0+x1)/2, y0)
|
||||||
|
gc.CubicCurveTo(x1, y0, x1, y0, x1, (y0+y1)/2)
|
||||||
|
gc.CubicCurveTo(x1, y1, x1, y1, (x1+x0)/2, y1)
|
||||||
|
gc.CubicCurveTo(x0, y1, x0, y1, x0, (y0+y1)/2)
|
||||||
|
} else {
|
||||||
|
gc.MoveTo(x0, y0+radius)
|
||||||
|
gc.CubicCurveTo(x0, y0, x0, y0, (x0+x1)/2, y0)
|
||||||
|
gc.CubicCurveTo(x1, y0, x1, y0, x1, y0+radius)
|
||||||
|
gc.LineTo(x1, y1-radius)
|
||||||
|
gc.CubicCurveTo(x1, y1, x1, y1, (x1+x0)/2, y1)
|
||||||
|
gc.CubicCurveTo(x0, y1, x0, y1, x0, y1-radius)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if rectHeight/2 < radius {
|
||||||
|
gc.MoveTo(x0, (y0+y1)/2)
|
||||||
|
gc.CubicCurveTo(x0, y0, x0, y0, x0+radius, y0)
|
||||||
|
gc.LineTo(x1-radius, y0)
|
||||||
|
gc.CubicCurveTo(x1, y0, x1, y0, x1, (y0+y1)/2)
|
||||||
|
gc.CubicCurveTo(x1, y1, x1, y1, x1-radius, y1)
|
||||||
|
gc.LineTo(x0+radius, y1)
|
||||||
|
gc.CubicCurveTo(x0, y1, x0, y1, x0, (y0+y1)/2)
|
||||||
|
} else {
|
||||||
|
gc.MoveTo(x0, y0+radius)
|
||||||
|
gc.CubicCurveTo(x0, y0, x0, y0, x0+radius, y0)
|
||||||
|
gc.LineTo(x1-radius, y0)
|
||||||
|
gc.CubicCurveTo(x1, y0, x1, y0, x1, y0+radius)
|
||||||
|
gc.LineTo(x1, y1-radius)
|
||||||
|
gc.CubicCurveTo(x1, y1, x1, y1, x1-radius, y1)
|
||||||
|
gc.LineTo(x0+radius, y1)
|
||||||
|
gc.CubicCurveTo(x0, y1, x0, y1, x0, y1-radius)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gc.Close()
|
||||||
|
gc.SetStrokeColor(stroke)
|
||||||
|
gc.SetFillColor(fill)
|
||||||
|
gc.SetLineWidth(10.0)
|
||||||
|
gc.FillStroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dash draws a line with a dash pattern
|
||||||
|
func Dash(gc draw2d.GraphicContext, x, y, width, height float64) {
|
||||||
|
sx, sy := width/162, height/205
|
||||||
|
gc.SetStrokeColor(image.Black)
|
||||||
|
gc.SetLineDash([]float64{height / 10, height / 50, height / 50, height / 50}, -50.0)
|
||||||
|
gc.SetLineCap(draw2d.ButtCap)
|
||||||
|
gc.SetLineJoin(draw2d.RoundJoin)
|
||||||
|
gc.SetLineWidth(height / 50)
|
||||||
|
|
||||||
|
gc.MoveTo(x+sx*60.0, y)
|
||||||
|
gc.LineTo(x+sx*60.0, y)
|
||||||
|
gc.LineTo(x+sx*162, y+sy*205)
|
||||||
|
rLineTo(gc, sx*-102.4, 0)
|
||||||
|
gc.CubicCurveTo(x+sx*-17, y+sy*205, x+sx*-17, y+sy*103, x+sx*60.0, y+sy*103.0)
|
||||||
|
gc.Stroke()
|
||||||
|
gc.SetLineDash(nil, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arc draws an arc with a positive angle (clockwise)
|
||||||
|
func Arc(gc draw2d.GraphicContext, xc, yc, width, height float64) {
|
||||||
|
// draw an arc
|
||||||
|
xc += width / 2
|
||||||
|
yc += height / 2
|
||||||
|
radiusX, radiusY := width/2, height/2
|
||||||
|
startAngle := 45 * (math.Pi / 180.0) /* angles are specified */
|
||||||
|
angle := 135 * (math.Pi / 180.0) /* clockwise in radians */
|
||||||
|
gc.SetLineWidth(width / 10)
|
||||||
|
gc.SetLineCap(draw2d.ButtCap)
|
||||||
|
gc.SetStrokeColor(image.Black)
|
||||||
|
gc.MoveTo(xc+math.Cos(startAngle)*radiusX, yc+math.Sin(startAngle)*radiusY)
|
||||||
|
gc.ArcTo(xc, yc, radiusX, radiusY, startAngle, angle)
|
||||||
|
gc.Stroke()
|
||||||
|
|
||||||
|
// fill a circle
|
||||||
|
gc.SetStrokeColor(color.NRGBA{255, 0x33, 0x33, 0x80})
|
||||||
|
gc.SetFillColor(color.NRGBA{255, 0x33, 0x33, 0x80})
|
||||||
|
gc.SetLineWidth(width / 20)
|
||||||
|
|
||||||
|
gc.MoveTo(xc+math.Cos(startAngle)*radiusX, yc+math.Sin(startAngle)*radiusY)
|
||||||
|
gc.LineTo(xc, yc)
|
||||||
|
gc.LineTo(xc-radiusX, yc)
|
||||||
|
gc.Stroke()
|
||||||
|
|
||||||
|
gc.MoveTo(xc, yc)
|
||||||
|
gc.ArcTo(xc, yc, width/10.0, height/10.0, 0, 2*math.Pi)
|
||||||
|
gc.Fill()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArcNegative draws an arc with a negative angle (anti clockwise).
|
||||||
|
func ArcNegative(gc draw2d.GraphicContext, xc, yc, width, height float64) {
|
||||||
|
xc += width / 2
|
||||||
|
yc += height / 2
|
||||||
|
radiusX, radiusY := width/2, height/2
|
||||||
|
startAngle := 45.0 * (math.Pi / 180.0) /* angles are specified */
|
||||||
|
angle := -225 * (math.Pi / 180.0) /* clockwise in radians */
|
||||||
|
gc.SetLineWidth(width / 10)
|
||||||
|
gc.SetLineCap(draw2d.ButtCap)
|
||||||
|
gc.SetStrokeColor(image.Black)
|
||||||
|
|
||||||
|
gc.ArcTo(xc, yc, radiusX, radiusY, startAngle, angle)
|
||||||
|
gc.Stroke()
|
||||||
|
// fill a circle
|
||||||
|
gc.SetStrokeColor(color.NRGBA{255, 0x33, 0x33, 0x80})
|
||||||
|
gc.SetFillColor(color.NRGBA{255, 0x33, 0x33, 0x80})
|
||||||
|
gc.SetLineWidth(width / 20)
|
||||||
|
|
||||||
|
gc.MoveTo(xc+math.Cos(startAngle)*radiusX, yc+math.Sin(startAngle)*radiusY)
|
||||||
|
gc.LineTo(xc, yc)
|
||||||
|
gc.LineTo(xc-radiusX, yc)
|
||||||
|
gc.Stroke()
|
||||||
|
|
||||||
|
gc.ArcTo(xc, yc, width/10.0, height/10.0, 0, 2*math.Pi)
|
||||||
|
gc.Fill()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CubicCurve draws a cubic curve with its control points.
|
||||||
|
func CubicCurve(gc draw2d.GraphicContext, x, y, width, height float64) {
|
||||||
|
sx, sy := width/162, height/205
|
||||||
|
x0, y0 := x, y+sy*100.0
|
||||||
|
x1, y1 := x+sx*75, y+sy*205
|
||||||
|
x2, y2 := x+sx*125, y
|
||||||
|
x3, y3 := x+sx*205, y+sy*100
|
||||||
|
|
||||||
|
gc.SetStrokeColor(image.Black)
|
||||||
|
gc.SetFillColor(color.NRGBA{0xAA, 0xAA, 0xAA, 0xFF})
|
||||||
|
gc.SetLineWidth(width / 10)
|
||||||
|
gc.MoveTo(x0, y0)
|
||||||
|
gc.CubicCurveTo(x1, y1, x2, y2, x3, y3)
|
||||||
|
gc.Stroke()
|
||||||
|
|
||||||
|
gc.SetStrokeColor(color.NRGBA{0xFF, 0x33, 0x33, 0x88})
|
||||||
|
|
||||||
|
gc.SetLineWidth(width / 20)
|
||||||
|
// draw segment of curve
|
||||||
|
gc.MoveTo(x0, y0)
|
||||||
|
gc.LineTo(x1, y1)
|
||||||
|
gc.LineTo(x2, y2)
|
||||||
|
gc.LineTo(x3, y3)
|
||||||
|
gc.Stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillString draws a filled and stroked string.
|
||||||
|
func FillString(gc draw2d.GraphicContext, x, y, width, height float64) {
|
||||||
|
sx, sy := width/100, height/100
|
||||||
|
gc.Save()
|
||||||
|
gc.SetStrokeColor(image.Black)
|
||||||
|
gc.SetLineWidth(1)
|
||||||
|
draw2dkit.RoundedRectangle(gc, x+sx*5, y+sy*5, x+sx*95, y+sy*95, sx*10, sy*10)
|
||||||
|
gc.FillStroke()
|
||||||
|
gc.SetFillColor(image.Black)
|
||||||
|
gc.SetFontSize(height / 6)
|
||||||
|
gc.Translate(x+sx*6, y+sy*52)
|
||||||
|
gc.SetFontData(draw2d.FontData{
|
||||||
|
Name: "luxi",
|
||||||
|
Family: draw2d.FontFamilyMono,
|
||||||
|
Style: draw2d.FontStyleBold | draw2d.FontStyleItalic})
|
||||||
|
w := gc.FillString("Hug")
|
||||||
|
gc.Translate(w+sx, 0)
|
||||||
|
left, top, right, bottom := gc.GetStringBounds("cou")
|
||||||
|
gc.SetStrokeColor(color.NRGBA{255, 0x33, 0x33, 0x80})
|
||||||
|
draw2dkit.Rectangle(gc, left, top, right, bottom)
|
||||||
|
gc.SetLineWidth(height / 50)
|
||||||
|
gc.Stroke()
|
||||||
|
gc.SetFillColor(color.NRGBA{0x33, 0x33, 0xff, 0xff})
|
||||||
|
gc.SetStrokeColor(color.NRGBA{0x33, 0x33, 0xff, 0xff})
|
||||||
|
gc.SetLineWidth(height / 100)
|
||||||
|
gc.StrokeString("Hug")
|
||||||
|
gc.Restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillStroke first fills and afterwards strokes a path.
|
||||||
|
func FillStroke(gc draw2d.GraphicContext, x, y, width, height float64) {
|
||||||
|
sx, sy := width/210, height/215
|
||||||
|
gc.MoveTo(x+sx*113.0, y)
|
||||||
|
gc.LineTo(x+sx*215.0, y+sy*215)
|
||||||
|
rLineTo(gc, sx*-100, 0)
|
||||||
|
gc.CubicCurveTo(x+sx*35, y+sy*215, x+sx*35, y+sy*113, x+sx*113.0, y+sy*113)
|
||||||
|
gc.Close()
|
||||||
|
|
||||||
|
gc.MoveTo(x+sx*50.0, y)
|
||||||
|
rLineTo(gc, sx*51.2, sy*51.2)
|
||||||
|
rLineTo(gc, sx*-51.2, sy*51.2)
|
||||||
|
rLineTo(gc, sx*-51.2, sy*-51.2)
|
||||||
|
gc.Close()
|
||||||
|
|
||||||
|
gc.SetLineWidth(width / 20.0)
|
||||||
|
gc.SetFillColor(color.NRGBA{0, 0, 0xFF, 0xFF})
|
||||||
|
gc.SetStrokeColor(image.Black)
|
||||||
|
gc.FillStroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
func rLineTo(path draw2d.PathBuilder, x, y float64) {
|
||||||
|
x0, y0 := path.LastPoint()
|
||||||
|
path.LineTo(x0+x, y0+y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillStyle demonstrates the difference between even odd and non zero winding rule.
|
||||||
|
func FillStyle(gc draw2d.GraphicContext, x, y, width, height float64) {
|
||||||
|
sx, sy := width/232, height/220
|
||||||
|
gc.SetLineWidth(width / 40)
|
||||||
|
|
||||||
|
draw2dkit.Rectangle(gc, x+sx*0, y+sy*12, x+sx*232, y+sy*70)
|
||||||
|
|
||||||
|
var wheel1, wheel2 draw2d.Path
|
||||||
|
wheel1.ArcTo(x+sx*52, y+sy*70, sx*40, sy*40, 0, 2*math.Pi)
|
||||||
|
wheel2.ArcTo(x+sx*180, y+sy*70, sx*40, sy*40, 0, -2*math.Pi)
|
||||||
|
|
||||||
|
gc.SetFillRule(draw2d.FillRuleEvenOdd)
|
||||||
|
gc.SetFillColor(color.NRGBA{0, 0xB2, 0, 0xFF})
|
||||||
|
|
||||||
|
gc.SetStrokeColor(image.Black)
|
||||||
|
gc.FillStroke(&wheel1, &wheel2)
|
||||||
|
|
||||||
|
draw2dkit.Rectangle(gc, x, y+sy*140, x+sx*232, y+sy*198)
|
||||||
|
wheel1.Clear()
|
||||||
|
wheel1.ArcTo(x+sx*52, y+sy*198, sx*40, sy*40, 0, 2*math.Pi)
|
||||||
|
wheel2.Clear()
|
||||||
|
wheel2.ArcTo(x+sx*180, y+sy*198, sx*40, sy*40, 0, -2*math.Pi)
|
||||||
|
|
||||||
|
gc.SetFillRule(draw2d.FillRuleWinding)
|
||||||
|
gc.SetFillColor(color.NRGBA{0, 0, 0xE5, 0xFF})
|
||||||
|
gc.FillStroke(&wheel1, &wheel2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathTransform scales a path differently in horizontal and vertical direction.
|
||||||
|
func PathTransform(gc draw2d.GraphicContext, x, y, width, height float64) {
|
||||||
|
gc.Save()
|
||||||
|
gc.SetLineWidth(width / 10)
|
||||||
|
gc.Translate(x+width/2, y+height/2)
|
||||||
|
gc.Scale(1, 4)
|
||||||
|
gc.ArcTo(0, 0, width/8, height/8, 0, math.Pi*2)
|
||||||
|
gc.Close()
|
||||||
|
gc.Stroke()
|
||||||
|
gc.Restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Star draws many lines from a center.
|
||||||
|
func Star(gc draw2d.GraphicContext, x, y, width, height float64) {
|
||||||
|
gc.Save()
|
||||||
|
gc.Translate(x+width/2, y+height/2)
|
||||||
|
gc.SetLineWidth(width / 40)
|
||||||
|
for i := 0.0; i < 360; i = i + 10 { // Go from 0 to 360 degrees in 10 degree steps
|
||||||
|
gc.Save() // Keep rotations temporary
|
||||||
|
gc.Rotate(i * (math.Pi / 180.0)) // Rotate by degrees on stack from 'for'
|
||||||
|
gc.MoveTo(0, 0)
|
||||||
|
gc.LineTo(width/2, 0)
|
||||||
|
gc.Stroke()
|
||||||
|
gc.Restore()
|
||||||
|
}
|
||||||
|
gc.Restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw all figures in a nice 4x3 grid.
|
||||||
|
func Draw(gc draw2d.GraphicContext, width, height float64) {
|
||||||
|
mx, my := width*0.025, height*0.025 // margin
|
||||||
|
dx, dy := (width-2*mx)/4, (height-2*my)/3
|
||||||
|
w, h := dx-2*mx, dy-2*my
|
||||||
|
x0, y := 2*mx, 2*my
|
||||||
|
x := x0
|
||||||
|
Bubble(gc, x, y, w, h)
|
||||||
|
x += dx
|
||||||
|
CurveRectangle(gc, x, y, w, h, color.NRGBA{0x80, 0, 0, 0x80}, color.NRGBA{0x80, 0x80, 0xFF, 0xFF})
|
||||||
|
x += dx
|
||||||
|
Dash(gc, x, y, w, h)
|
||||||
|
x += dx
|
||||||
|
Arc(gc, x, y, w, h)
|
||||||
|
x = x0
|
||||||
|
y += dy
|
||||||
|
ArcNegative(gc, x, y, w, h)
|
||||||
|
x += dx
|
||||||
|
CubicCurve(gc, x, y, w, h)
|
||||||
|
x += dx
|
||||||
|
FillString(gc, x, y, w, h)
|
||||||
|
x += dx
|
||||||
|
FillStroke(gc, x, y, w, h)
|
||||||
|
x = x0
|
||||||
|
y += dy
|
||||||
|
FillStyle(gc, x, y, w, h)
|
||||||
|
x += dx
|
||||||
|
PathTransform(gc, x, y, w, h)
|
||||||
|
x += dx
|
||||||
|
Star(gc, x, y, w, h)
|
||||||
|
x += dx
|
||||||
|
gopher2.Draw(gc, x, y, w, h/2)
|
||||||
|
}
|
73
samples/gopher/gopher.go
Normal file
73
samples/gopher/gopher.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 21/11/2010 by Laurent Le Goff
|
||||||
|
|
||||||
|
// Package gopher draws a gopher avatar based on a svg of:
|
||||||
|
// https://github.com/golang-samples/gopher-vector/
|
||||||
|
package gopher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws a left hand and ear of a gopher. Afterwards it returns
|
||||||
|
// the filename. This should only be used during testing.
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
gc.Save()
|
||||||
|
gc.Scale(0.5, 0.5)
|
||||||
|
// Draw a (partial) gopher
|
||||||
|
Draw(gc)
|
||||||
|
gc.Restore()
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("gopher", ext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a left hand and ear of a gopher using a gc thanks to
|
||||||
|
// https://github.com/golang-samples/gopher-vector/
|
||||||
|
func Draw(gc draw2d.GraphicContext) {
|
||||||
|
// Initialize Stroke Attribute
|
||||||
|
gc.SetLineWidth(3)
|
||||||
|
gc.SetLineCap(draw2d.RoundCap)
|
||||||
|
gc.SetStrokeColor(color.Black)
|
||||||
|
|
||||||
|
// Left hand
|
||||||
|
// <path fill-rule="evenodd" clip-rule="evenodd" fill="#F6D2A2" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||||
|
// M10.634,300.493c0.764,15.751,16.499,8.463,23.626,3.539c6.765-4.675,8.743-0.789,9.337-10.015
|
||||||
|
// c0.389-6.064,1.088-12.128,0.744-18.216c-10.23-0.927-21.357,1.509-29.744,7.602C10.277,286.542,2.177,296.561,10.634,300.493"/>
|
||||||
|
gc.SetFillColor(color.RGBA{0xF6, 0xD2, 0xA2, 0xff})
|
||||||
|
gc.MoveTo(10.634, 300.493)
|
||||||
|
rCubicCurveTo(gc, 0.764, 15.751, 16.499, 8.463, 23.626, 3.539)
|
||||||
|
rCubicCurveTo(gc, 6.765, -4.675, 8.743, -0.789, 9.337, -10.015)
|
||||||
|
rCubicCurveTo(gc, 0.389, -6.064, 1.088, -12.128, 0.744, -18.216)
|
||||||
|
rCubicCurveTo(gc, -10.23, -0.927, -21.357, 1.509, -29.744, 7.602)
|
||||||
|
gc.CubicCurveTo(10.277, 286.542, 2.177, 296.561, 10.634, 300.493)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// <path fill-rule="evenodd" clip-rule="evenodd" fill="#C6B198" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||||
|
// M10.634,300.493c2.29-0.852,4.717-1.457,6.271-3.528"/>
|
||||||
|
gc.MoveTo(10.634, 300.493)
|
||||||
|
rCubicCurveTo(gc, 2.29, -0.852, 4.717, -1.457, 6.271, -3.528)
|
||||||
|
gc.Stroke()
|
||||||
|
|
||||||
|
// Left Ear
|
||||||
|
// <path fill-rule="evenodd" clip-rule="evenodd" fill="#6AD7E5" stroke="#000000" stroke-width="3" stroke-linecap="round" d="
|
||||||
|
// M46.997,112.853C-13.3,95.897,31.536,19.189,79.956,50.74L46.997,112.853z"/>
|
||||||
|
gc.MoveTo(46.997, 112.853)
|
||||||
|
gc.CubicCurveTo(-13.3, 95.897, 31.536, 19.189, 79.956, 50.74)
|
||||||
|
gc.LineTo(46.997, 112.853)
|
||||||
|
gc.Close()
|
||||||
|
gc.Stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
func rQuadCurveTo(path draw2d.PathBuilder, dcx, dcy, dx, dy float64) {
|
||||||
|
x, y := path.LastPoint()
|
||||||
|
path.QuadCurveTo(x+dcx, y+dcy, x+dx, y+dy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rCubicCurveTo(path draw2d.PathBuilder, dcx1, dcy1, dcx2, dcy2, dx, dy float64) {
|
||||||
|
x, y := path.LastPoint()
|
||||||
|
path.CubicCurveTo(x+dcx1, y+dcy1, x+dcx2, y+dcy2, x+dx, y+dy)
|
||||||
|
}
|
122
samples/gopher2/gopher2.go
Normal file
122
samples/gopher2/gopher2.go
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 21/11/2010 by Laurent Le Goff
|
||||||
|
|
||||||
|
// Package gopher2 draws a gopher avatar based on a svg of:
|
||||||
|
// https://github.com/golang-samples/gopher-vector/
|
||||||
|
package gopher2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dkit"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws a rotated face of the gopher. Afterwards it returns
|
||||||
|
// the filename. This should only be used during testing.
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
gc.SetStrokeColor(image.Black)
|
||||||
|
gc.SetFillColor(image.White)
|
||||||
|
gc.Save()
|
||||||
|
// Draw a (partial) gopher
|
||||||
|
gc.Translate(-60, 65)
|
||||||
|
gc.Rotate(-30 * (math.Pi / 180.0))
|
||||||
|
Draw(gc, 48, 48, 240, 72)
|
||||||
|
gc.Restore()
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("gopher2", ext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a gopher head (not rotated)
|
||||||
|
func Draw(gc draw2d.GraphicContext, x, y, w, h float64) {
|
||||||
|
h23 := (h * 2) / 3
|
||||||
|
|
||||||
|
blf := color.RGBA{0, 0, 0, 0xff} // black
|
||||||
|
wf := color.RGBA{0xff, 0xff, 0xff, 0xff} // white
|
||||||
|
nf := color.RGBA{0x8B, 0x45, 0x13, 0xff} // brown opaque
|
||||||
|
brf := color.RGBA{0x8B, 0x45, 0x13, 0x99} // brown transparant
|
||||||
|
brb := color.RGBA{0x8B, 0x45, 0x13, 0xBB} // brown transparant
|
||||||
|
|
||||||
|
// round head top
|
||||||
|
gc.MoveTo(x, y+h*1.002)
|
||||||
|
gc.CubicCurveTo(x+w/4, y-h/3, x+3*w/4, y-h/3, x+w, y+h*1.002)
|
||||||
|
gc.Close()
|
||||||
|
gc.SetFillColor(brb)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// rectangle head bottom
|
||||||
|
draw2dkit.RoundedRectangle(gc, x, y+h, x+w, y+h+h, h/5, h/5)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// left ear outside
|
||||||
|
draw2dkit.Circle(gc, x, y+h, w/12)
|
||||||
|
gc.SetFillColor(brf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// left ear inside
|
||||||
|
draw2dkit.Circle(gc, x, y+h, 0.5*w/12)
|
||||||
|
gc.SetFillColor(nf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// right ear outside
|
||||||
|
draw2dkit.Circle(gc, x+w, y+h, w/12)
|
||||||
|
gc.SetFillColor(brf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// right ear inside
|
||||||
|
draw2dkit.Circle(gc, x+w, y+h, 0.5*w/12)
|
||||||
|
gc.SetFillColor(nf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// left eye outside white
|
||||||
|
draw2dkit.Circle(gc, x+w/3, y+h23, w/9)
|
||||||
|
gc.SetFillColor(wf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// left eye black
|
||||||
|
draw2dkit.Circle(gc, x+w/3+w/24, y+h23, 0.5*w/9)
|
||||||
|
gc.SetFillColor(blf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// left eye inside white
|
||||||
|
draw2dkit.Circle(gc, x+w/3+w/24+w/48, y+h23, 0.2*w/9)
|
||||||
|
gc.SetFillColor(wf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// right eye outside white
|
||||||
|
draw2dkit.Circle(gc, x+w-w/3, y+h23, w/9)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// right eye black
|
||||||
|
draw2dkit.Circle(gc, x+w-w/3+w/24, y+h23, 0.5*w/9)
|
||||||
|
gc.SetFillColor(blf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// right eye inside white
|
||||||
|
draw2dkit.Circle(gc, x+w-(w/3)+w/24+w/48, y+h23, 0.2*w/9)
|
||||||
|
gc.SetFillColor(wf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// left tooth
|
||||||
|
gc.SetFillColor(wf)
|
||||||
|
draw2dkit.RoundedRectangle(gc, x+w/2-w/8, y+h+h/2.5, x+w/2-w/8+w/8, y+h+h/2.5+w/6, w/10, w/10)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// right tooth
|
||||||
|
draw2dkit.RoundedRectangle(gc, x+w/2, y+h+h/2.5, x+w/2+w/8, y+h+h/2.5+w/6, w/10, w/10)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// snout
|
||||||
|
draw2dkit.Ellipse(gc, x+(w/2), y+h+h/2.5, w/6, w/12)
|
||||||
|
gc.SetFillColor(nf)
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
// nose
|
||||||
|
draw2dkit.Ellipse(gc, x+(w/2), y+h+h/7, w/10, w/12)
|
||||||
|
gc.SetFillColor(blf)
|
||||||
|
gc.Fill()
|
||||||
|
}
|
40
samples/helloworld/helloworld.go
Normal file
40
samples/helloworld/helloworld.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 21/11/2010 by Laurent Le Goff, Stani Michiels
|
||||||
|
|
||||||
|
// Package helloworld displays multiple "Hello World" (one rotated)
|
||||||
|
// in a rounded rectangle.
|
||||||
|
package helloworld
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dkit"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws "Hello World" and returns the filename. This should only be
|
||||||
|
// used during testing.
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
// Draw hello world
|
||||||
|
Draw(gc, fmt.Sprintf("Hello World %d dpi", gc.GetDPI()))
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("helloworld", ext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw "Hello World"
|
||||||
|
func Draw(gc draw2d.GraphicContext, text string) {
|
||||||
|
// Draw a rounded rectangle using default colors
|
||||||
|
draw2dkit.RoundedRectangle(gc, 5, 5, 135, 95, 10, 10)
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// Set the font luximbi.ttf
|
||||||
|
gc.SetFontData(draw2d.FontData{"luxi", draw2d.FontFamilyMono, draw2d.FontStyleBold | draw2d.FontStyleItalic})
|
||||||
|
// Set the fill text color to black
|
||||||
|
gc.SetFillColor(image.Black)
|
||||||
|
gc.SetFontSize(14)
|
||||||
|
// Display Hello World
|
||||||
|
gc.FillStringAt("Hello World", 8, 52)
|
||||||
|
}
|
119
samples/helloworldgl/helloworldgl.go
Normal file
119
samples/helloworldgl/helloworldgl.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
// Open an OpenGl window and display a rectangle using a OpenGl GraphicContext
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/go-gl/gl/v2.1/gl"
|
||||||
|
"github.com/go-gl/glfw/v3.1/glfw"
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dgl"
|
||||||
|
"github.com/llgcode/draw2d/draw2dkit"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// global rotation
|
||||||
|
rotate int
|
||||||
|
width, height int
|
||||||
|
redraw = true
|
||||||
|
font draw2d.FontData
|
||||||
|
)
|
||||||
|
|
||||||
|
func reshape(window *glfw.Window, w, h int) {
|
||||||
|
gl.ClearColor(1, 1, 1, 1)
|
||||||
|
/* Establish viewing area to cover entire window. */
|
||||||
|
gl.Viewport(0, 0, int32(w), int32(h))
|
||||||
|
/* PROJECTION Matrix mode. */
|
||||||
|
gl.MatrixMode(gl.PROJECTION)
|
||||||
|
/* Reset project matrix. */
|
||||||
|
gl.LoadIdentity()
|
||||||
|
/* Map abstract coords directly to window coords. */
|
||||||
|
gl.Ortho(0, float64(w), 0, float64(h), -1, 1)
|
||||||
|
/* Invert Y axis so increasing Y goes down. */
|
||||||
|
gl.Scalef(1, -1, 1)
|
||||||
|
/* Shift origin up to upper-left corner. */
|
||||||
|
gl.Translatef(0, float32(-h), 0)
|
||||||
|
gl.Enable(gl.BLEND)
|
||||||
|
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
||||||
|
gl.Disable(gl.DEPTH_TEST)
|
||||||
|
width, height = w, h
|
||||||
|
redraw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask to refresh
|
||||||
|
func invalidate() {
|
||||||
|
redraw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func display() {
|
||||||
|
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||||
|
|
||||||
|
gl.LineWidth(1)
|
||||||
|
gc := draw2dgl.NewGraphicContext(width, height)
|
||||||
|
gc.SetFontData(draw2d.FontData{
|
||||||
|
Name: "luxi",
|
||||||
|
Family: draw2d.FontFamilyMono,
|
||||||
|
Style: draw2d.FontStyleBold | draw2d.FontStyleItalic})
|
||||||
|
|
||||||
|
gc.BeginPath()
|
||||||
|
draw2dkit.RoundedRectangle(gc, 200, 200, 600, 600, 100, 100)
|
||||||
|
|
||||||
|
gc.SetFillColor(color.RGBA{0, 0, 0, 0xff})
|
||||||
|
gc.Fill()
|
||||||
|
|
||||||
|
gl.Flush() /* Single buffered, so needs a flush. */
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := glfw.Init()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer glfw.Terminate()
|
||||||
|
width, height = 800, 800
|
||||||
|
window, err := glfw.CreateWindow(width, height, "Show RoundedRect", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.MakeContextCurrent()
|
||||||
|
window.SetSizeCallback(reshape)
|
||||||
|
window.SetKeyCallback(onKey)
|
||||||
|
window.SetCharCallback(onChar)
|
||||||
|
|
||||||
|
glfw.SwapInterval(1)
|
||||||
|
|
||||||
|
err = gl.Init()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reshape(window, width, height)
|
||||||
|
for !window.ShouldClose() {
|
||||||
|
if redraw {
|
||||||
|
display()
|
||||||
|
window.SwapBuffers()
|
||||||
|
redraw = false
|
||||||
|
}
|
||||||
|
glfw.PollEvents()
|
||||||
|
// time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onChar(w *glfw.Window, char rune) {
|
||||||
|
log.Println(char)
|
||||||
|
}
|
||||||
|
|
||||||
|
func onKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
|
||||||
|
switch {
|
||||||
|
case key == glfw.KeyEscape && action == glfw.Press,
|
||||||
|
key == glfw.KeyQ && action == glfw.Press:
|
||||||
|
w.SetShouldClose(true)
|
||||||
|
}
|
||||||
|
}
|
39
samples/line/line.go
Normal file
39
samples/line/line.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 21/11/2010 by Laurent Le Goff, Stani Michiels
|
||||||
|
|
||||||
|
// Package line draws vertically spaced lines.
|
||||||
|
package line
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dkit"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws vertically spaced lines and returns the filename.
|
||||||
|
// This should only be used during testing.
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
gc.SetFillRule(draw2d.FillRuleWinding)
|
||||||
|
gc.Clear()
|
||||||
|
// Draw the line
|
||||||
|
for x := 5.0; x < 297; x += 10 {
|
||||||
|
Draw(gc, x, 0, x, 210)
|
||||||
|
}
|
||||||
|
gc.ClearRect(100, 75, 197, 135)
|
||||||
|
draw2dkit.Ellipse(gc, 148.5, 105, 35, 25)
|
||||||
|
gc.SetFillColor(color.RGBA{0xff, 0xff, 0x44, 0xff})
|
||||||
|
gc.FillStroke()
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("line", ext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw vertically spaced lines
|
||||||
|
func Draw(gc draw2d.GraphicContext, x0, y0, x1, y1 float64) {
|
||||||
|
// Draw a line
|
||||||
|
gc.MoveTo(x0, y0)
|
||||||
|
gc.LineTo(x1, y1)
|
||||||
|
gc.Stroke()
|
||||||
|
}
|
52
samples/linecapjoin/linecapjoin.go
Normal file
52
samples/linecapjoin/linecapjoin.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2010 The draw2d Authors. All rights reserved.
|
||||||
|
// created: 21/11/2010 by Laurent Le Goff
|
||||||
|
|
||||||
|
// Package linecapjoin demonstrates the different line caps and joins.
|
||||||
|
package linecapjoin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws the different line caps and joins.
|
||||||
|
// This should only be used during testing.
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
// Draw the line
|
||||||
|
const offset = 75.0
|
||||||
|
x := 35.0
|
||||||
|
caps := []draw2d.LineCap{draw2d.ButtCap, draw2d.SquareCap, draw2d.RoundCap}
|
||||||
|
joins := []draw2d.LineJoin{draw2d.BevelJoin, draw2d.MiterJoin, draw2d.RoundJoin}
|
||||||
|
for i := range caps {
|
||||||
|
Draw(gc, caps[i], joins[i], x, 50, x, 160, offset)
|
||||||
|
x += offset
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("linecapjoin", ext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a line with an angle with specified line cap and join
|
||||||
|
func Draw(gc draw2d.GraphicContext, cap draw2d.LineCap, join draw2d.LineJoin,
|
||||||
|
x0, y0, x1, y1, offset float64) {
|
||||||
|
gc.SetLineCap(cap)
|
||||||
|
gc.SetLineJoin(join)
|
||||||
|
|
||||||
|
// Draw thick line
|
||||||
|
gc.SetStrokeColor(color.NRGBA{0x33, 0x33, 0x33, 0xFF})
|
||||||
|
gc.SetLineWidth(30.0)
|
||||||
|
gc.MoveTo(x0, y0)
|
||||||
|
gc.LineTo((x0+x1)/2+offset, (y0+y1)/2)
|
||||||
|
gc.LineTo(x1, y1)
|
||||||
|
gc.Stroke()
|
||||||
|
|
||||||
|
// Draw thin helping line
|
||||||
|
gc.SetStrokeColor(color.NRGBA{0xFF, 0x33, 0x33, 0xFF})
|
||||||
|
gc.SetLineWidth(2.56)
|
||||||
|
gc.MoveTo(x0, y0)
|
||||||
|
gc.LineTo((x0+x1)/2+offset, (y0+y1)/2)
|
||||||
|
gc.LineTo(x1, y1)
|
||||||
|
gc.Stroke()
|
||||||
|
}
|
49
samples/postscript/postscript.go
Normal file
49
samples/postscript/postscript.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Package postscript reads the tiger.ps file and draws it to a backend.
|
||||||
|
package postscript
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/llgcode/ps"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/samples"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main draws the tiger
|
||||||
|
func Main(gc draw2d.GraphicContext, ext string) (string, error) {
|
||||||
|
gc.Save()
|
||||||
|
|
||||||
|
// flip the image
|
||||||
|
gc.Translate(0, 200)
|
||||||
|
gc.Scale(0.35, -0.35)
|
||||||
|
gc.Translate(70, -200)
|
||||||
|
|
||||||
|
// Tiger postscript drawing
|
||||||
|
tiger := samples.Resource("image", "tiger.ps", ext)
|
||||||
|
|
||||||
|
// Draw tiger
|
||||||
|
Draw(gc, tiger)
|
||||||
|
gc.Restore()
|
||||||
|
|
||||||
|
// Return the output filename
|
||||||
|
return samples.Output("postscript", ext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a tiger
|
||||||
|
func Draw(gc draw2d.GraphicContext, filename string) {
|
||||||
|
// Open the postscript
|
||||||
|
src, err := os.OpenFile(filename, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
bytes, err := ioutil.ReadAll(src)
|
||||||
|
reader := strings.NewReader(string(bytes))
|
||||||
|
|
||||||
|
// Initialize and interpret the postscript
|
||||||
|
interpreter := ps.NewInterpreter(gc)
|
||||||
|
interpreter.Execute(reader)
|
||||||
|
}
|
114
samples/postscriptgl/postscriptgl.go
Normal file
114
samples/postscriptgl/postscriptgl.go
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
// Open a OpenGL window and display a tiger interpreting a postscript file
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gl/gl/v2.1/gl"
|
||||||
|
"github.com/go-gl/glfw/v3.1/glfw"
|
||||||
|
"github.com/llgcode/draw2d/draw2dgl"
|
||||||
|
"github.com/llgcode/ps"
|
||||||
|
)
|
||||||
|
|
||||||
|
var postscriptContent string
|
||||||
|
|
||||||
|
var (
|
||||||
|
width, height int
|
||||||
|
rotate int
|
||||||
|
window *glfw.Window
|
||||||
|
)
|
||||||
|
|
||||||
|
func reshape(window *glfw.Window, w, h int) {
|
||||||
|
gl.ClearColor(1, 1, 1, 1)
|
||||||
|
//fmt.Println(gl.GetString(gl.EXTENSIONS))
|
||||||
|
gl.Viewport(0, 0, int32(w), int32(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)
|
||||||
|
gl.Disable(gl.DEPTH_TEST)
|
||||||
|
width, height = w, h
|
||||||
|
}
|
||||||
|
|
||||||
|
func display() {
|
||||||
|
|
||||||
|
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||||
|
|
||||||
|
lastTime := time.Now()
|
||||||
|
gl.LineWidth(1)
|
||||||
|
gc := draw2dgl.NewGraphicContext(width, height)
|
||||||
|
|
||||||
|
gc.Translate(380, 400)
|
||||||
|
gc.Scale(1, -1)
|
||||||
|
rotate = (rotate + 1) % 360
|
||||||
|
gc.Rotate(float64(rotate) * math.Pi / 180)
|
||||||
|
gc.Translate(-380, -400)
|
||||||
|
|
||||||
|
interpreter := ps.NewInterpreter(gc)
|
||||||
|
reader := strings.NewReader(postscriptContent)
|
||||||
|
|
||||||
|
interpreter.Execute(reader)
|
||||||
|
dt := time.Now().Sub(lastTime)
|
||||||
|
log.Printf("Redraw in : %f ms\n", float64(dt)*1e-6)
|
||||||
|
gl.Flush() /* Single buffered, so needs a flush. */
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
src, err := os.OpenFile("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)
|
||||||
|
err = glfw.Init()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer glfw.Terminate()
|
||||||
|
|
||||||
|
window, err = glfw.CreateWindow(800, 800, "Show Tiger in OpenGL", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.MakeContextCurrent()
|
||||||
|
window.SetSizeCallback(reshape)
|
||||||
|
window.SetKeyCallback(onKey)
|
||||||
|
|
||||||
|
glfw.SwapInterval(1)
|
||||||
|
|
||||||
|
err = gl.Init()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
reshape(window, 800, 800)
|
||||||
|
for !window.ShouldClose() {
|
||||||
|
display()
|
||||||
|
window.SwapBuffers()
|
||||||
|
glfw.PollEvents()
|
||||||
|
// time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func onKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
|
||||||
|
switch {
|
||||||
|
case key == glfw.KeyEscape && action == glfw.Press,
|
||||||
|
key == glfw.KeyQ && action == glfw.Press:
|
||||||
|
w.SetShouldClose(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
}
|
2733
samples/postscriptgl/tiger.ps
Normal file
2733
samples/postscriptgl/tiger.ps
Normal file
File diff suppressed because it is too large
Load diff
24
samples/samples.go
Normal file
24
samples/samples.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Package samples provides examples which can be used with different
|
||||||
|
// backends. They are also used for testing and coverage of the
|
||||||
|
// draw2d package.
|
||||||
|
package samples
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Resource returns a resource filename for testing.
|
||||||
|
func Resource(folder, filename, ext string) string {
|
||||||
|
var root string
|
||||||
|
if ext == "pdf" {
|
||||||
|
root = "../"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%sresource/%s/%s", root, folder, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output returns the output filename for testing.
|
||||||
|
func Output(name, ext string) string {
|
||||||
|
var root string
|
||||||
|
if ext == "pdf" {
|
||||||
|
root = "../"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%soutput/samples/%s.%s", root, name, ext)
|
||||||
|
}
|
|
@ -6,27 +6,40 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/llgcode/draw2d"
|
"github.com/llgcode/draw2d"
|
||||||
"github.com/llgcode/draw2d.samples"
|
"github.com/llgcode/draw2d/samples/android"
|
||||||
"github.com/llgcode/draw2d.samples/android"
|
"github.com/llgcode/draw2d/samples/frameimage"
|
||||||
"github.com/llgcode/draw2d.samples/frameimage"
|
"github.com/llgcode/draw2d/samples/geometry"
|
||||||
"github.com/llgcode/draw2d.samples/gopher"
|
"github.com/llgcode/draw2d/samples/gopher"
|
||||||
"github.com/llgcode/draw2d.samples/helloworld"
|
"github.com/llgcode/draw2d/samples/gopher2"
|
||||||
"github.com/llgcode/draw2d.samples/line"
|
"github.com/llgcode/draw2d/samples/helloworld"
|
||||||
"github.com/llgcode/draw2d.samples/linecapjoin"
|
"github.com/llgcode/draw2d/samples/line"
|
||||||
"github.com/llgcode/draw2d.samples/postscript"
|
"github.com/llgcode/draw2d/samples/linecapjoin"
|
||||||
|
"github.com/llgcode/draw2d/samples/postscript"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSampleAndroid(t *testing.T) {
|
func TestSampleAndroid(t *testing.T) {
|
||||||
test(t, android.Main)
|
test(t, android.Main)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSampleGeometry(t *testing.T) {
|
||||||
|
// Set the global folder for searching fonts
|
||||||
|
// The pdf backend needs for every ttf file its corresponding
|
||||||
|
// json/.z file which is generated by gofpdf/makefont.
|
||||||
|
draw2d.SetFontFolder("resource/font")
|
||||||
|
test(t, geometry.Main)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSampleGopher(t *testing.T) {
|
func TestSampleGopher(t *testing.T) {
|
||||||
test(t, gopher.Main)
|
test(t, gopher.Main)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSampleGopher2(t *testing.T) {
|
||||||
|
test(t, gopher2.Main)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSampleHelloWorld(t *testing.T) {
|
func TestSampleHelloWorld(t *testing.T) {
|
||||||
// Set the global folder for searching fonts
|
// Set the global folder for searching fonts
|
||||||
draw2d.SetFontFolder(samples.Dir("helloworld", ""))
|
draw2d.SetFontFolder("resource/font")
|
||||||
test(t, helloworld.Main)
|
test(t, helloworld.Main)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +51,7 @@ func TestSampleLine(t *testing.T) {
|
||||||
test(t, line.Main)
|
test(t, line.Main)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSampleLineCap(t *testing.T) {
|
func TestSampleLineCapJoin(t *testing.T) {
|
||||||
test(t, linecapjoin.Main)
|
test(t, linecapjoin.Main)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
test
Normal file
8
test
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
echo golint
|
||||||
|
golint ./... | grep "draw2dpdf\|samples\|^advanced_path\|^arc\|draw2d[.]\|fileutil\|^gc\|math\|^path[.]\|rgba_interpolation\|test\|vertex2d"
|
||||||
|
echo
|
||||||
|
echo go vet
|
||||||
|
go vet ./...
|
||||||
|
echo
|
||||||
|
echo go test
|
||||||
|
go test -cover ./... | grep -v "no test"
|
11
test_test.go
11
test_test.go
|
@ -7,6 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/llgcode/draw2d"
|
"github.com/llgcode/draw2d"
|
||||||
|
"github.com/llgcode/draw2d/draw2dimg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type sample func(gc draw2d.GraphicContext, ext string) (string, error)
|
type sample func(gc draw2d.GraphicContext, ext string) (string, error)
|
||||||
|
@ -14,16 +15,16 @@ type sample func(gc draw2d.GraphicContext, ext string) (string, error)
|
||||||
func test(t *testing.T, draw sample) {
|
func test(t *testing.T, draw sample) {
|
||||||
// Initialize the graphic context on an RGBA image
|
// Initialize the graphic context on an RGBA image
|
||||||
dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
|
dest := image.NewRGBA(image.Rect(0, 0, 297, 210.0))
|
||||||
gc := draw2d.NewGraphicContext(dest)
|
gc := draw2dimg.NewGraphicContext(dest)
|
||||||
// Draw Android logo
|
// Draw Android logo
|
||||||
fn, err := draw(gc, "png")
|
output, err := draw(gc, "png")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Drawing %q failed: %v", fn, err)
|
t.Errorf("Drawing %q failed: %v", output, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Save to png
|
// Save to png
|
||||||
err = draw2d.SaveToPngFile(fn, dest)
|
err = draw2dimg.SaveToPngFile(output, dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Saving %q failed: %v", fn, err)
|
t.Errorf("Saving %q failed: %v", output, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue