geom.go 6.6 KB
Newer Older
1 2
// Copyright 2010 The Freetype-Go Authors. All rights reserved.
// Use of this source code is governed by your choice of either the
3 4
// FreeType License or the GNU General Public License version 2 (or
// any later version), both of which can be found in the LICENSE file.
5 6 7 8 9 10 11

package raster

import (
	"fmt"
	"math"

12 13
	"golang.org/x/image/math/fixed"
)
14

15
// maxAbs returns the maximum of abs(a) and abs(b).
16
func maxAbs(a, b fixed.Int26_6) fixed.Int26_6 {
17 18 19 20 21 22 23 24 25 26 27 28
	if a < 0 {
		a = -a
	}
	if b < 0 {
		b = -b
	}
	if a < b {
		return b
	}
	return a
}

29
// pNeg returns the vector -p, or equivalently p rotated by 180 degrees.
30 31
func pNeg(p fixed.Point26_6) fixed.Point26_6 {
	return fixed.Point26_6{-p.X, -p.Y}
32 33
}

34
// pDot returns the dot product p·q.
35
func pDot(p fixed.Point26_6, q fixed.Point26_6) fixed.Int52_12 {
36 37
	px, py := int64(p.X), int64(p.Y)
	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.
42
func pLen(p fixed.Point26_6) fixed.Int26_6 {
43 44 45
	// TODO(nigeltao): use fixed point math.
	x := float64(p.X)
	y := float64(p.Y)
46
	return fixed.Int26_6(math.Sqrt(x*x + y*y))
47 48
}

49 50
// pNorm returns the vector p normalized to the given length, or zero if p is
// degenerate.
51
func 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 57 58
	s, t := int64(length), int64(d)
	x := int64(p.X) * s / t
	y := int64(p.Y) * s / t
59
	return fixed.Point26_6{fixed.Int26_6(x), fixed.Int26_6(y)}
60 61
}

62 63
// pRot45CW returns the vector p rotated clockwise by 45 degrees.
//
64
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
65
func pRot45CW(p fixed.Point26_6) fixed.Point26_6 {
66 67 68 69
	// 181/256 is approximately 1/√2, or sin(π/4).
	px, py := int64(p.X), int64(p.Y)
	qx := (+px - py) * 181 / 256
	qy := (+px + py) * 181 / 256
70
	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
71 72
}

73 74
// pRot90CW returns the vector p rotated clockwise by 90 degrees.
//
75
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
76 77
func pRot90CW(p fixed.Point26_6) fixed.Point26_6 {
	return fixed.Point26_6{-p.Y, p.X}
78 79
}

80 81
// pRot135CW returns the vector p rotated clockwise by 135 degrees.
//
82
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
83
func pRot135CW(p fixed.Point26_6) fixed.Point26_6 {
84 85 86 87
	// 181/256 is approximately 1/√2, or sin(π/4).
	px, py := int64(p.X), int64(p.Y)
	qx := (-px - py) * 181 / 256
	qy := (+px - py) * 181 / 256
88
	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
89 90
}

91 92
// pRot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
//
93
// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
94
func pRot45CCW(p fixed.Point26_6) fixed.Point26_6 {
95 96 97 98
	// 181/256 is approximately 1/√2, or sin(π/4).
	px, py := int64(p.X), int64(p.Y)
	qx := (+px + py) * 181 / 256
	qy := (-px + py) * 181 / 256
99
	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
100 101
}

102 103
// pRot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
//
104
// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
105 106
func pRot90CCW(p fixed.Point26_6) fixed.Point26_6 {
	return fixed.Point26_6{p.Y, -p.X}
107 108
}

109 110
// pRot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
//
111
// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
112
func pRot135CCW(p fixed.Point26_6) fixed.Point26_6 {
113 114 115 116
	// 181/256 is approximately 1/√2, or sin(π/4).
	px, py := int64(p.X), int64(p.Y)
	qx := (-px + py) * 181 / 256
	qy := (-px - py) * 181 / 256
117
	return fixed.Point26_6{fixed.Int26_6(qx), fixed.Int26_6(qy)}
118 119
}

120 121 122
// An Adder accumulates points on a curve.
type Adder interface {
	// 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 133
}

// A Path is a sequence of curves, and a curve is a start point followed by a
// sequence of linear, quadratic or cubic segments.
134
type Path []fixed.Int26_6
135 136 137 138 139 140 141 142 143 144

// String returns a human-readable representation of a Path.
func (p Path) String() string {
	s := ""
	for i := 0; i < len(p); {
		if i != 0 {
			s += " "
		}
		switch p[i] {
		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 157 158 159 160 161 162 163 164
		default:
			panic("freetype/raster: bad path")
		}
	}
	return s
}

// Clear cancels any previous calls to p.Start or p.AddXxx.
func (p *Path) Clear() {
165
	*p = (*p)[:0]
166 167 168
}

// Start starts a new curve at the given point.
169
func (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.
174
func (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.
179
func (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.
184
func (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 189
}

// AddPath adds the Path q to p.
func (p *Path) AddPath(q Path) {
190
	*p = append(*p, q...)
191 192 193
}

// AddStroke adds a stroked Path.
194
func (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.
199 200
func (p Path) firstPoint() fixed.Point26_6 {
	return fixed.Point26_6{p[1], p[2]}
201 202
}

203
// lastPoint returns the last point in a non-empty Path.
204 205
func (p Path) lastPoint() fixed.Point26_6 {
	return fixed.Point26_6{p[len(p)-3], p[len(p)-2]}
206 207
}

208 209 210 211 212 213 214 215 216
// addPathReversed adds q reversed to p.
// For example, if q consists of a linear segment from A to B followed by a
// quadratic segment from B to C to D, then the values of q looks like:
// index: 01234567890123
// value: 0AA01BB12CCDD2
// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
func addPathReversed(p Adder, q Path) {
	if len(q) == 0 {
		return
217
	}
218
	i := len(q) - 1
219
	for {
220
		switch q[i] {
221
		case 0:
222
			return
223 224
		case 1:
			i -= 4
225 226 227
			p.Add1(
				fixed.Point26_6{q[i-2], q[i-1]},
			)
228 229
		case 2:
			i -= 6
230 231 232 233
			p.Add2(
				fixed.Point26_6{q[i+2], q[i+3]},
				fixed.Point26_6{q[i-2], q[i-1]},
			)
234 235
		case 3:
			i -= 8
236 237 238 239 240
			p.Add3(
				fixed.Point26_6{q[i+4], q[i+5]},
				fixed.Point26_6{q[i+2], q[i+3]},
				fixed.Point26_6{q[i-2], q[i-1]},
			)
241 242 243 244
		default:
			panic("freetype/raster: bad path")
		}
	}
245
}