Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1056)

Side by Side Diff: freetype/raster/geom.go

Issue 1711048: code review 1711048: freetype/raster: make cap and join functions instead of... (Closed)
Patch Set: code review 1711048: freetype/raster: make cap and join functions instead of... Created 13 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | freetype/raster/raster.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2010 The Freetype-Go Authors. All rights reserved. 1 // Copyright 2010 The Freetype-Go Authors. All rights reserved.
2 // Use of this source code is governed by your choice of either the 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, 3 // FreeType License or the GNU General Public License version 2,
4 // both of which can be found in the LICENSE file. 4 // both of which can be found in the LICENSE file.
5 5
6 package raster 6 package raster
7 7
8 import ( 8 import (
9 "fmt" 9 "fmt"
10 "math" 10 "math"
(...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after
262 (*p)[n+7] = 3 262 (*p)[n+7] = 3
263 } 263 }
264 264
265 // AddPath adds the Path q to p. 265 // AddPath adds the Path q to p.
266 func (p *Path) AddPath(q Path) { 266 func (p *Path) AddPath(q Path) {
267 n, m := len(*p), len(q) 267 n, m := len(*p), len(q)
268 p.grow(m) 268 p.grow(m)
269 copy((*p)[n:n+m], q) 269 copy((*p)[n:n+m], q)
270 } 270 }
271 271
272 // TODO(nigeltao): should a Cap be a func rather than an int, so that callers 272 // A Capper signifies how to begin or end a stroked path.
273 // can specify custom cap styles? Similarly for Join. 273 type Capper interface {
274 » // Cap adds a cap to p given a pivot point and the normal vector of a
275 » // terminal segment. The normal's length is half of the stroke width.
276 » Cap(p Adder, halfWidth Fix32, pivot, n1 Point)
277 }
274 278
275 // A Cap signifies how to begin or end a stroked curve. 279 // The CapperFunc type adapts an ordinary function to be a Capper.
276 type Cap int 280 type CapperFunc func(Adder, Fix32, Point, Point)
277 281
278 const ( 282 func (f CapperFunc) Cap(p Adder, halfWidth Fix32, pivot, n1 Point) {
279 » RoundCap Cap = iota 283 » f(p, halfWidth, pivot, n1)
280 » ButtCap 284 }
281 » SquareCap
282 )
283 285
284 // A Join signifies how to join interior nodes of a stroked curve. 286 // A Joiner signifies how to join interior nodes of a stroked path.
285 type Join int 287 type Joiner interface {
288 » // Join adds a join to the two sides of a stroked path given a pivot
289 » // point and the normal vectors of the trailing and leading segments.
290 » // Both normals have length equal to half of the stroke width.
291 » Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point)
292 }
286 293
287 const ( 294 // The JoinerFunc type adapts an ordinary function to be a Joiner.
288 » RoundJoin Join = iota 295 type JoinerFunc func(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point)
289 » BevelJoin 296
290 » MiterJoin 297 func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point) {
291 ) 298 » f(lhs, rhs, halfWidth, pivot, n0, n1)
299 }
292 300
293 // AddStroke adds a stroked Path. 301 // AddStroke adds a stroked Path.
294 func (p *Path) AddStroke(q Path, width Fix32, cap Cap, join Join) { 302 func (p *Path) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
295 » Stroke(p, q, width, cap, join) 303 » Stroke(p, q, width, cr, jr)
296 } 304 }
297 305
298 // Stroke adds the stroked Path q to p. The resultant stroked path is typically 306 // Stroke adds the stroked Path q to p. The resultant stroked path is typically
299 // self-intersecting and should be rasterized with UseNonZeroWinding. 307 // self-intersecting and should be rasterized with UseNonZeroWinding.
300 func Stroke(p Adder, q Path, width Fix32, cap Cap, join Join) { 308 // cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
309 func Stroke(p Adder, q Path, width Fix32, cr Capper, jr Joiner) {
301 if len(q) == 0 { 310 if len(q) == 0 {
302 return 311 return
303 } 312 }
313 if cr == nil {
314 cr = RoundCapper
315 }
316 if jr == nil {
317 jr = RoundJoiner
318 }
304 if q[0] != 0 { 319 if q[0] != 0 {
305 panic("freetype/raster: bad path") 320 panic("freetype/raster: bad path")
306 } 321 }
307 i := 0 322 i := 0
308 for j := 4; j < len(q); { 323 for j := 4; j < len(q); {
309 switch q[j] { 324 switch q[j] {
310 case 0: 325 case 0:
311 » » » stroke(p, q[i:j], width, cap, join) 326 » » » stroke(p, q[i:j], width, cr, jr)
312 i, j = j, j+4 327 i, j = j, j+4
313 case 1: 328 case 1:
314 j += 4 329 j += 4
315 case 2: 330 case 2:
316 j += 6 331 j += 6
317 case 3: 332 case 3:
318 j += 8 333 j += 8
319 } 334 }
320 } 335 }
321 » stroke(p, q[i:len(q)], width, cap, join) 336 » stroke(p, q[i:len(q)], width, cr, jr)
322 } 337 }
323 338
324 func addCap(p Adder, cap Cap, center, end Point) { 339 // A RoundCapper adds round caps to a stroked path.
325 » switch cap { 340 var RoundCapper Capper = CapperFunc(func(p Adder, halfWidth Fix32, pivot, n1 Poi nt) {
326 » case RoundCap: 341 » // The cubic Bézier approximation to a circle involves the magic number
327 » » // The cubic Bézier approximation to a circle involves the magic number 342 » // (√2 - 1) * 4/3, which is approximately 141/256.
328 » » // (√2 - 1) * 4/3, which is approximately 141/256. 343 » const k = 141
329 » » const k = 141 344 » e := n1.Rot90CCW()
330 » » d := end.Sub(center) 345 » side := pivot.Add(e)
331 » » e := d.Rot90CCW() 346 » start, end := pivot.Sub(n1), pivot.Add(n1)
332 » » side := center.Add(e) 347 » d, e := n1.Mul(k), e.Mul(k)
333 » » start := center.Sub(d) 348 » p.Add3(start.Add(e), side.Sub(d), side)
334 » » d, e = d.Mul(k), e.Mul(k) 349 » p.Add3(side.Add(d), end.Add(e), end)
335 » » p.Add3(start.Add(e), side.Sub(d), side) 350 })
336 » » p.Add3(side.Add(d), end.Add(e), end) 351
337 » case ButtCap: 352 // A ButtCapper adds butt caps to a stroked path.
338 » » p.Add1(end) 353 var ButtCapper Capper = CapperFunc(func(p Adder, halfWidth Fix32, pivot, n1 Poin t) {
339 » case SquareCap: 354 » p.Add1(pivot.Add(n1))
340 » » d := end.Sub(center) 355 })
341 » » e := d.Rot90CCW() 356
342 » » side := center.Add(e) 357 // A SquareCapper adds square caps to a stroked path.
343 » » p.Add1(side.Sub(d)) 358 var SquareCapper Capper = CapperFunc(func(p Adder, halfWidth Fix32, pivot, n1 Po int) {
344 » » p.Add1(side.Add(d)) 359 » e := n1.Rot90CCW()
345 » » p.Add1(end) 360 » side := pivot.Add(e)
361 » p.Add1(side.Sub(n1))
362 » p.Add1(side.Add(n1))
363 » p.Add1(pivot.Add(n1))
364 })
365
366 // A RoundJoiner adds round joins to a stroked path.
367 var RoundJoiner Joiner = JoinerFunc(func(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
368 » dot := n0.Rot90CW().Dot(n1)
369 » if dot >= 0 {
370 » » addArc(lhs, pivot, n0, n1)
371 » » rhs.Add1(pivot.Sub(n1))
372 » } else {
373 » » lhs.Add1(pivot.Add(n1))
374 » » addArc(rhs, pivot, n0.Neg(), n1.Neg())
346 } 375 }
347 } 376 })
348 377
349 func addJoin(lhs, rhs Adder, join Join, a, anorm, bnorm Point) { 378 // A BevelJoiner adds bevel joins to a stroked path.
350 » switch join { 379 var BevelJoiner Joiner = JoinerFunc(func(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
351 » case RoundJoin: 380 » lhs.Add1(pivot.Add(n1))
352 » » dot := anorm.Rot90CW().Dot(bnorm) 381 » rhs.Add1(pivot.Sub(n1))
353 » » if dot >= 0 { 382 })
354 » » » addArc(lhs, a, anorm, bnorm)
355 » » » rhs.Add1(a.Sub(bnorm))
356 » » } else {
357 » » » lhs.Add1(a.Add(bnorm))
358 » » » addArc(rhs, a, anorm.Neg(), bnorm.Neg())
359 » » }
360 » case BevelJoin:
361 » » lhs.Add1(a.Add(bnorm))
362 » » rhs.Add1(a.Sub(bnorm))
363 » case MiterJoin:
364 » » panic("freetype/raster: miter join unimplemented")
365 » }
366 }
367 383
368 // addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of 384 // addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
369 // the two possible arcs is taken, i.e. the one spanning <= 180 degrees. 385 // the two possible arcs is taken, i.e. the one spanning <= 180 degrees.
370 // The two vectors n0 and n1 must be of equal length. 386 // The two vectors n0 and n1 must be of equal length.
371 func addArc(p Adder, pivot, n0, n1 Point) { 387 func addArc(p Adder, pivot, n0, n1 Point) {
372 // r2 is the square of the length of n0. 388 // r2 is the square of the length of n0.
373 r2 := n0.Dot(n0) 389 r2 := n0.Dot(n0)
374 if r2 < 4096 { 390 if r2 < 4096 {
375 // The arc radius is so small that we collapse to a straight lin e. 391 // The arc radius is so small that we collapse to a straight lin e.
376 p.Add1(pivot.Add(n1)) 392 p.Add1(pivot.Add(n1))
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
444 // When the angle is 45 degrees then 150/256 is the ratio of the lengths of 460 // When the angle is 45 degrees then 150/256 is the ratio of the lengths of
445 // the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}. 461 // the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
446 // d is the normalized dot product between s and n1. Since the angle ran ges 462 // d is the normalized dot product between s and n1. Since the angle ran ges
447 // between 0 and 45 degrees then d ranges between 256/256 and 181/256. 463 // between 0 and 45 degrees then d ranges between 256/256 and 181/256.
448 d := 256 * s.Dot(n1) / r2 464 d := 256 * s.Dot(n1) / r2
449 multiple := Fix32(150 - 22*(d-181)/(256-181)) 465 multiple := Fix32(150 - 22*(d-181)/(256-181))
450 p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1)) 466 p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
451 } 467 }
452 468
453 // stroke adds the stroked Path q to p, where q consists of exactly one curve. 469 // stroke adds the stroked Path q to p, where q consists of exactly one curve.
454 func stroke(p Adder, q Path, width Fix32, cap Cap, join Join) { 470 func stroke(p Adder, q Path, width Fix32, cr Capper, jr Joiner) {
455 // Stroking is implemented by deriving two paths each width/2 apart from q. 471 // Stroking is implemented by deriving two paths each width/2 apart from q.
456 // The left-hand-side path is added immediately to p; the right-hand-sid e 472 // The left-hand-side path is added immediately to p; the right-hand-sid e
457 // path is accumulated in r, and once we've finished adding the LHS to p 473 // path is accumulated in r, and once we've finished adding the LHS to p
458 // we add the RHS in reverse order. 474 // we add the RHS in reverse order.
459 r := Path(make([]Fix32, 0, len(q))) 475 r := Path(make([]Fix32, 0, len(q)))
476 u := width / 2
460 var start, anorm Point 477 var start, anorm Point
461 a := Point{q[1], q[2]} 478 a := Point{q[1], q[2]}
462 i := 4 479 i := 4
463 for i < len(q) { 480 for i < len(q) {
464 switch q[i] { 481 switch q[i] {
465 case 1: 482 case 1:
466 b := Point{q[i+1], q[i+2]} 483 b := Point{q[i+1], q[i+2]}
467 » » » bnorm := b.Sub(a).Norm(width / 2).Rot90CCW() 484 » » » bnorm := b.Sub(a).Norm(u).Rot90CCW()
468 if i == 4 { 485 if i == 4 {
469 start = a.Add(bnorm) 486 start = a.Add(bnorm)
470 p.Start(start) 487 p.Start(start)
471 r.Start(a.Sub(bnorm)) 488 r.Start(a.Sub(bnorm))
472 } else { 489 } else {
473 » » » » addJoin(p, &r, join, a, anorm, bnorm) 490 » » » » jr.Join(p, &r, u, a, anorm, bnorm)
474 } 491 }
475 p.Add1(b.Add(bnorm)) 492 p.Add1(b.Add(bnorm))
476 r.Add1(b.Sub(bnorm)) 493 r.Add1(b.Sub(bnorm))
477 a, anorm = b, bnorm 494 a, anorm = b, bnorm
478 i += 4 495 i += 4
479 case 2: 496 case 2:
480 panic("freetype/raster: stroke unimplemented for quadrat ic segments") 497 panic("freetype/raster: stroke unimplemented for quadrat ic segments")
481 case 3: 498 case 3:
482 panic("freetype/raster: stroke unimplemented for cubic s egments") 499 panic("freetype/raster: stroke unimplemented for cubic s egments")
483 default: 500 default:
484 panic("freetype/raster: bad path") 501 panic("freetype/raster: bad path")
485 } 502 }
486 } 503 }
487 i = len(r) - 1 504 i = len(r) - 1
488 » addCap(p, cap, Point{q[len(q)-3], q[len(q)-2]}, Point{r[i-2], r[i-1]}) 505 » cr.Cap(p, u, Point{q[len(q)-3], q[len(q)-2]}, anorm.Neg())
489 // Add r reversed to p. 506 // Add r reversed to p.
490 // For example, if r consists of a linear segment from A to B followed b y a 507 // For example, if r consists of a linear segment from A to B followed b y a
491 // quadratic segment from B to C to D, then the values of r looks like: 508 // quadratic segment from B to C to D, then the values of r looks like:
492 // index: 01234567890123 509 // index: 01234567890123
493 // value: 0AA01BB12CCDD2 510 // value: 0AA01BB12CCDD2
494 // So, when adding r backwards to p, we want to Add2(C, B) followed by A dd1(A). 511 // So, when adding r backwards to p, we want to Add2(C, B) followed by A dd1(A).
495 loop: 512 loop:
496 for { 513 for {
497 switch r[i] { 514 switch r[i] {
498 case 0: 515 case 0:
499 break loop 516 break loop
500 case 1: 517 case 1:
501 i -= 4 518 i -= 4
502 p.Add1(Point{r[i-2], r[i-1]}) 519 p.Add1(Point{r[i-2], r[i-1]})
503 case 2: 520 case 2:
504 i -= 6 521 i -= 6
505 p.Add2(Point{r[i+2], r[i+3]}, Point{r[i-2], r[i-1]}) 522 p.Add2(Point{r[i+2], r[i+3]}, Point{r[i-2], r[i-1]})
506 case 3: 523 case 3:
507 i -= 8 524 i -= 8
508 p.Add3(Point{r[i+4], r[i+5]}, Point{r[i+2], r[i+3]}, Poi nt{r[i-2], r[i-1]}) 525 p.Add3(Point{r[i+4], r[i+5]}, Point{r[i+2], r[i+3]}, Poi nt{r[i-2], r[i-1]})
509 default: 526 default:
510 panic("freetype/raster: bad path") 527 panic("freetype/raster: bad path")
511 } 528 }
512 } 529 }
513 // TODO(nigeltao): if q is a closed path then we should join the first a nd 530 // TODO(nigeltao): if q is a closed path then we should join the first a nd
514 // last segments instead of capping them. 531 // last segments instead of capping them.
515 » addCap(p, cap, Point{q[1], q[2]}, start) 532 » pivot := Point{q[1], q[2]}
533 » cr.Cap(p, u, pivot, start.Sub(pivot))
516 } 534 }
OLDNEW
« no previous file with comments | « no previous file | freetype/raster/raster.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b