/
canvas.go
116 lines (100 loc) · 2.5 KB
/
canvas.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package bitbuilder
// Author: Arne Vansteenkiste
// Some code snippets were taken from
// https://code.google.com/p/appengine-go/
import (
"code.google.com/p/freetype-go/freetype/raster"
"fmt"
"image"
"image/color"
"image/png"
"os"
)
// A Canvas is used to draw on.
type Canvas struct {
*image.RGBA
painter *raster.RGBAPainter
rasterizer *raster.Rasterizer
strokewidth raster.Fix32
strokecap raster.Capper
path raster.Path
}
// Make a new canvas of size w x h.
func NewCanvas(w, h int) *Canvas {
c := new(Canvas)
c.RGBA = image.NewRGBA(image.Rect(0, 0, w, h))
c.Clear(color.White)
c.painter = raster.NewRGBAPainter(c.RGBA)
c.rasterizer = raster.NewRasterizer(w, h)
c.rasterizer.UseNonZeroWinding = true
c.SetColor(color.Black)
c.path = make(raster.Path, 0, 100)
c.resetPath()
c.SetStroke(1, Round)
return c
}
// Set the color for drawing.
func (c *Canvas) SetColor(col color.Color) {
c.painter.SetColor(col)
}
// Line capping style.
var (
Round = raster.RoundCapper
Square = raster.SquareCapper
Butt = raster.ButtCapper
)
// Set the line width and end capping style.
func (c *Canvas) SetStroke(width int, cap_ raster.Capper) {
c.strokewidth = fix32(width)
c.strokecap = cap_
}
// Draw a line between points a and b.
// Uses the currently set color and stroke style.
func (c *Canvas) Line(a, b Point) {
c.path.Start(pt(a))
c.path.Add1(pt(b))
c.strokePath()
c.resetPath()
}
func (c *Canvas) resetPath() {
c.path = c.path[:0]
}
func (c *Canvas) strokePath() {
raster.Stroke(c.rasterizer, c.path, c.strokewidth, c.strokecap, nil)
c.rasterizer.Rasterize(c.painter)
}
func (c *Canvas) fillPath() {
c.rasterizer.AddPath(c.path)
c.rasterizer.Rasterize(c.painter)
}
// Save the Canvas' contents to a PNG file.
func (c *Canvas) Encode(fname string) {
// TODO: image postprocessing: make perfectly monochrome
out, err := os.OpenFile(fname, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0755)
check("topng", err)
defer out.Close()
err = png.Encode(out, c.RGBA)
check("topng", err)
}
func check(msg string, err error) {
if err != nil {
fmt.Fprintln(os.Stderr, msg, ":", err)
os.Exit(1)
}
}
func pt(a Point) raster.Point {
return raster.Point{fix32(a.X), fix32(a.Y)}
}
func fix32(x int) raster.Fix32 {
return raster.Fix32(x << 8)
}
// Clears the entire canvas to the specified color.
func (c *Canvas) Clear(col color.Color) {
img := c.RGBA
bounds := img.Bounds()
for i := bounds.Min.X; i < bounds.Max.X; i++ {
for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
img.Set(i, j, col)
}
}
}