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