1// Copyright 2010 The Freetype-Go Authors. All rights reserved.
2// Use of this source code is governed by your choice of either the
3// FreeType License or the GNU General Public License version 2 (or
4// any later version), both of which can be found in the LICENSE file.
5
6package raster
7
8import (
9	"fmt"
10	"math"
11
12	"golang.org/x/image/math/fixed"
13)
14
15// maxAbs returns the maximum of abs(a) and abs(b).
16func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
17	if a < 0 {
18		a = -a
19	}
20	if b < 0 {
21		b = -b
22	}
23	if a < b {
24		return b
25	}
26	return a
27}
28
29// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
30func pNeg(p fixed.Point26_6) fixed.Point26_6 {
31	return fixed.Point26_6{-p.X, -p.Y}
32}
33
34// pDot returns the dot product p·q.
35func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 {
36	px, py := int64(p.X), int64(p.Y)
37	qx, qy := int64(q.X), int64(q.Y)
38	return fixed.Int52_12(px*qx + py*qy)
39}
40
41// pLen returns the length of the vector p.
42func pLen(p fixed.Point26_6) fixed.Int26_6 {
43	// TODO(nigeltao): use fixed point math.
44	x := float64(p.X)
45	y := float64(p.Y)
46	return fixed.Int26_6(math.Sqrt(x*x + y*y))
47}
48
49// pNorm returns the vector p normalized to the given length, or zero if p is
50// degenerate.
51func pNorm(p fixed.Point26_6, length fixed.Int26_6) fixed.Point26_6 {
52	d := pLen(p)
53	if d == 0 {
54		return fixed.Point26_6{}
55	}
56	s, t := int64(length), int64(d)
57	x := int64(p.X) * s / t
58	y := int64(p.Y) * s / t
59	return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
60}
61
62// pRot45CW returns the vector p rotated clockwise by 45 degrees.
63//
64// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
65func pRot45CW(p fixed.Point26_6) fixed.Point26_6 {
66	// 181/256 is approximately 1/√2, or sin(π/4).
67	px, py := int64(p.X), int64(p.Y)
68	qx := (+px - py) * 181 / 256
69	qy := (+px + py) * 181 / 256
70	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
71}
72
73// pRot90CW returns the vector p rotated clockwise by 90 degrees.
74//
75// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
76func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
77	return fixed.Point26_6{-p.Y, p.X}
78}
79
80// pRot135CW returns the vector p rotated clockwise by 135 degrees.
81//
82// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
83func pRot135CW(p fixed.Point26_6) fixed.Point26_6 {
84	// 181/256 is approximately 1/√2, or sin(π/4).
85	px, py := int64(p.X), int64(p.Y)
86	qx := (-px - py) * 181 / 256
87	qy := (+px - py) * 181 / 256
88	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
89}
90
91// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
92//
93// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
94func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
95	// 181/256 is approximately 1/√2, or sin(π/4).
96	px, py := int64(p.X), int64(p.Y)
97	qx := (+px + py) * 181 / 256
98	qy := (-px + py) * 181 / 256
99	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
100}
101
102// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
103//
104// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
105func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
106	return fixed.Point26_6{p.Y, -p.X}
107}
108
109// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
110//
111// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
112func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 {
113	// 181/256 is approximately 1/√2, or sin(π/4).
114	px, py := int64(p.X), int64(p.Y)
115	qx := (-px + py) * 181 / 256
116	qy := (-px - py) * 181 / 256
117	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
118}
119
120// An Adder accumulates points on a curve.
121type Adder interface {
122	// Start starts a new curve at the given point.
123	Start(a fixed.Point26_6)
124	// Add1 adds a linear segment to the current curve.
125	Add1(b fixed.Point26_6)
126	// Add2 adds a quadratic segment to the current curve.
127	Add2(b, c fixed.Point26_6)
128	// Add3 adds a cubic segment to the current curve.
129	Add3(b, c, d fixed.Point26_6)
130}
131
132// A Path is a sequence of curves, and a curve is a start point followed by a
133// sequence of linear, quadratic or cubic segments.
134type Path []fixed.Int26_6
135
136// String returns a human-readable representation of a Path.
137func (p Path) String() string {
138	s := ""
139	for i := 0; i < len(p); {
140		if i != 0 {
141			s += " "
142		}
143		switch p[i] {
144		case 0:
145			s += "S0" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
146			i += 4
147		case 1:
148			s += "A1" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+3]))
149			i += 4
150		case 2:
151			s += "A2" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+5]))
152			i += 6
153		case 3:
154			s += "A3" + fmt.Sprint([]fixed.Int26_6(p[i+1:i+7]))
155			i += 8
156		default:
157			panic("freetype/raster: bad path")
158		}
159	}
160	return s
161}
162
163// Clear cancels any previous calls to p.Start or p.AddXxx.
164func (p *Path) Clear() {
165	*p = (*p)[:0]
166}
167
168// Start starts a new curve at the given point.
169func (p *Path) Start(a fixed.Point26_6) {
170	*p = append(*p, 0, a.X, a.Y, 0)
171}
172
173// Add1 adds a linear segment to the current curve.
174func (p *Path) Add1(b fixed.Point26_6) {
175	*p = append(*p, 1, b.X, b.Y, 1)
176}
177
178// Add2 adds a quadratic segment to the current curve.
179func (p *Path) Add2(b, c fixed.Point26_6) {
180	*p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
181}
182
183// Add3 adds a cubic segment to the current curve.
184func (p *Path) Add3(b, c, d fixed.Point26_6) {
185	*p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
186}
187
188// AddPath adds the Path q to p.
189func (p *Path) AddPath(q Path) {
190	*p = append(*p, q...)
191}
192
193// AddStroke adds a stroked Path.
194func (p *Path) AddStroke(q Path, width fixed.Int26_6, cr Capper, jr Joiner) {
195	Stroke(p, q, width, cr, jr)
196}
197
198// firstPoint returns the first point in a non-empty Path.
199func (p Path) firstPoint() fixed.Point26_6 {
200	return fixed.Point26_6{p[1], p[2]}
201}
202
203// lastPoint returns the last point in a non-empty Path.
204func (p Path) lastPoint() fixed.Point26_6 {
205	return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
206}
207
208// addPathReversed adds q reversed to p.
209// For example, if q consists of a linear segment from A to B followed by a
210// quadratic segment from B to C to D, then the values of q looks like:
211// index: 01234567890123
212// value: 0AA01BB12CCDD2
213// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
214func addPathReversed(p Adder, q Path) {
215	if len(q) == 0 {
216		return
217	}
218	i := len(q) - 1
219	for {
220		switch q[i] {
221		case 0:
222			return
223		case 1:
224			i -= 4
225			p.Add1(
226				fixed.Point26_6{q[i-2], q[i-1]},
227			)
228		case 2:
229			i -= 6
230			p.Add2(
231				fixed.Point26_6{q[i+2], q[i+3]},
232				fixed.Point26_6{q[i-2], q[i-1]},
233			)
234		case 3:
235			i -= 8
236			p.Add3(
237				fixed.Point26_6{q[i+4], q[i+5]},
238				fixed.Point26_6{q[i+2], q[i+3]},
239				fixed.Point26_6{q[i-2], q[i-1]},
240			)
241		default:
242			panic("freetype/raster: bad path")
243		}
244	}
245}
246