1package drawing
2
3import (
4	"image"
5	"image/color"
6
7	"github.com/golang/freetype/truetype"
8)
9
10// StackGraphicContext is a context that does thngs.
11type StackGraphicContext struct {
12	current *ContextStack
13}
14
15// ContextStack is a graphic context implementation.
16type ContextStack struct {
17	Tr          Matrix
18	Path        *Path
19	LineWidth   float64
20	Dash        []float64
21	DashOffset  float64
22	StrokeColor color.Color
23	FillColor   color.Color
24	FillRule    FillRule
25	Cap         LineCap
26	Join        LineJoin
27
28	FontSizePoints float64
29	Font           *truetype.Font
30
31	Scale float64
32
33	Previous *ContextStack
34}
35
36// NewStackGraphicContext Create a new Graphic context from an image
37func NewStackGraphicContext() *StackGraphicContext {
38	gc := &StackGraphicContext{}
39	gc.current = new(ContextStack)
40	gc.current.Tr = NewIdentityMatrix()
41	gc.current.Path = new(Path)
42	gc.current.LineWidth = 1.0
43	gc.current.StrokeColor = image.Black
44	gc.current.FillColor = image.White
45	gc.current.Cap = RoundCap
46	gc.current.FillRule = FillRuleEvenOdd
47	gc.current.Join = RoundJoin
48	gc.current.FontSizePoints = 10
49	return gc
50}
51
52// GetMatrixTransform returns the matrix transform.
53func (gc *StackGraphicContext) GetMatrixTransform() Matrix {
54	return gc.current.Tr
55}
56
57// SetMatrixTransform sets the matrix transform.
58func (gc *StackGraphicContext) SetMatrixTransform(tr Matrix) {
59	gc.current.Tr = tr
60}
61
62// ComposeMatrixTransform composes a transform into the current transform.
63func (gc *StackGraphicContext) ComposeMatrixTransform(tr Matrix) {
64	gc.current.Tr.Compose(tr)
65}
66
67// Rotate rotates the matrix transform by an angle in degrees.
68func (gc *StackGraphicContext) Rotate(angle float64) {
69	gc.current.Tr.Rotate(angle)
70}
71
72// Translate translates a transform.
73func (gc *StackGraphicContext) Translate(tx, ty float64) {
74	gc.current.Tr.Translate(tx, ty)
75}
76
77// Scale scales a transform.
78func (gc *StackGraphicContext) Scale(sx, sy float64) {
79	gc.current.Tr.Scale(sx, sy)
80}
81
82// SetStrokeColor sets the stroke color.
83func (gc *StackGraphicContext) SetStrokeColor(c color.Color) {
84	gc.current.StrokeColor = c
85}
86
87// SetFillColor sets the fill color.
88func (gc *StackGraphicContext) SetFillColor(c color.Color) {
89	gc.current.FillColor = c
90}
91
92// SetFillRule sets the fill rule.
93func (gc *StackGraphicContext) SetFillRule(f FillRule) {
94	gc.current.FillRule = f
95}
96
97// SetLineWidth sets the line width.
98func (gc *StackGraphicContext) SetLineWidth(lineWidth float64) {
99	gc.current.LineWidth = lineWidth
100}
101
102// SetLineCap sets the line cap.
103func (gc *StackGraphicContext) SetLineCap(cap LineCap) {
104	gc.current.Cap = cap
105}
106
107// SetLineJoin sets the line join.
108func (gc *StackGraphicContext) SetLineJoin(join LineJoin) {
109	gc.current.Join = join
110}
111
112// SetLineDash sets the line dash.
113func (gc *StackGraphicContext) SetLineDash(dash []float64, dashOffset float64) {
114	gc.current.Dash = dash
115	gc.current.DashOffset = dashOffset
116}
117
118// SetFontSize sets the font size.
119func (gc *StackGraphicContext) SetFontSize(fontSizePoints float64) {
120	gc.current.FontSizePoints = fontSizePoints
121}
122
123// GetFontSize gets the font size.
124func (gc *StackGraphicContext) GetFontSize() float64 {
125	return gc.current.FontSizePoints
126}
127
128// SetFont sets the current font.
129func (gc *StackGraphicContext) SetFont(f *truetype.Font) {
130	gc.current.Font = f
131}
132
133// GetFont returns the font.
134func (gc *StackGraphicContext) GetFont() *truetype.Font {
135	return gc.current.Font
136}
137
138// BeginPath starts a new path.
139func (gc *StackGraphicContext) BeginPath() {
140	gc.current.Path.Clear()
141}
142
143// IsEmpty returns if the path is empty.
144func (gc *StackGraphicContext) IsEmpty() bool {
145	return gc.current.Path.IsEmpty()
146}
147
148// LastPoint returns the last point on the path.
149func (gc *StackGraphicContext) LastPoint() (x float64, y float64) {
150	return gc.current.Path.LastPoint()
151}
152
153// MoveTo moves the cursor for a path.
154func (gc *StackGraphicContext) MoveTo(x, y float64) {
155	gc.current.Path.MoveTo(x, y)
156}
157
158// LineTo draws a line.
159func (gc *StackGraphicContext) LineTo(x, y float64) {
160	gc.current.Path.LineTo(x, y)
161}
162
163// QuadCurveTo draws a quad curve.
164func (gc *StackGraphicContext) QuadCurveTo(cx, cy, x, y float64) {
165	gc.current.Path.QuadCurveTo(cx, cy, x, y)
166}
167
168// CubicCurveTo draws a cubic curve.
169func (gc *StackGraphicContext) CubicCurveTo(cx1, cy1, cx2, cy2, x, y float64) {
170	gc.current.Path.CubicCurveTo(cx1, cy1, cx2, cy2, x, y)
171}
172
173// ArcTo draws an arc.
174func (gc *StackGraphicContext) ArcTo(cx, cy, rx, ry, startAngle, delta float64) {
175	gc.current.Path.ArcTo(cx, cy, rx, ry, startAngle, delta)
176}
177
178// Close closes a path.
179func (gc *StackGraphicContext) Close() {
180	gc.current.Path.Close()
181}
182
183// Save pushes a context onto the stack.
184func (gc *StackGraphicContext) Save() {
185	context := new(ContextStack)
186	context.FontSizePoints = gc.current.FontSizePoints
187	context.Font = gc.current.Font
188	context.LineWidth = gc.current.LineWidth
189	context.StrokeColor = gc.current.StrokeColor
190	context.FillColor = gc.current.FillColor
191	context.FillRule = gc.current.FillRule
192	context.Dash = gc.current.Dash
193	context.DashOffset = gc.current.DashOffset
194	context.Cap = gc.current.Cap
195	context.Join = gc.current.Join
196	context.Path = gc.current.Path.Copy()
197	context.Font = gc.current.Font
198	context.Scale = gc.current.Scale
199	copy(context.Tr[:], gc.current.Tr[:])
200	context.Previous = gc.current
201	gc.current = context
202}
203
204// Restore restores the previous context.
205func (gc *StackGraphicContext) Restore() {
206	if gc.current.Previous != nil {
207		oldContext := gc.current
208		gc.current = gc.current.Previous
209		oldContext.Previous = nil
210	}
211}
212