raqote/rasterizer.rs
1/* Copyright 2013 Jeff Muizelaar
2 *
3 * Use of this source code is governed by a MIT-style license that can be
4 * found in the LICENSE file.
5 *
6 * Portions Copyright 2006 The Android Open Source Project
7 *
8 * Use of that source code is governed by a BSD-style license that can be
9 * found in the LICENSE.skia file.
10 */
11
12
13use typed_arena::Arena;
14
15use crate::{IntRect, Point};
16use crate::blitter::RasterBlitter;
17use crate::path_builder::Winding;
18use crate::geom::intrect;
19
20use std::ptr::NonNull;
21
22// One reason to have separate Edge/ActiveEdge is reduce the
23// memory usage of inactive edges. On the other hand
24// managing the lifetime of ActiveEdges is a lot
25// trickier than Edges. Edges can stay alive for the entire
26// rasterization. ActiveEdges will come and go in a much
27// less predictable order. On the other hand having the
28// ActiveEdges close together in memory would help
29// avoid cache misses. If we did switch to having separate
30// active edges it might be wise to store the active edges
31// in an array instead of as a linked list. This will work
32// well for the bubble sorting, but will cause more problems
33// for insertion.
34
35struct Edge {
36 //XXX: it is probably worth renaming this to top and bottom
37 x1: Dot2,
38 y1: Dot2,
39 x2: Dot2,
40 y2: Dot2,
41 control_x: Dot2,
42 control_y: Dot2,
43}
44
45// Fixed point representation:
46// We use 30.2 for the end points and 16.16 for the intermediate
47// results. I believe this essentially limits us to a 16.16 space.
48//
49// Prior Work:
50// - Cairo used to be 16.16 but switched to 24.8. Cairo converts paths
51// early to this fixed representation
52// - Fitz uses different precision depending on the AA settings
53// and uses the following Bresenham style adjustment in its step function
54// to avoid having to worry about intermediate precision
55// edge->x += edge->xmove;
56// edge->e += edge->adj_up;
57// if (edge->e > 0) {
58// edge->x += edge->xdir;
59// edge->e -= edge->adj_down;
60// }
61
62/// 16.16 fixed point representation.
63type Dot16 = i32;
64/// 30.2 fixed point representation.
65type Dot2 = i32;
66/// 26.6 fixed point representation.
67type Dot6 = i32;
68/// A "sliding fixed point" representation 16.16 >> shift.
69type ShiftedDot16 = i32;
70
71
72fn div_fixed16_fixed16(a: Dot16, b: Dot16) -> Dot16 {
73 (((a as i64) << 16) / (b as i64)) as i32
74}
75
76#[inline]
77fn dot2_to_dot16(val: Dot2) -> Dot16 {
78 val << (16 - SAMPLE_SHIFT)
79}
80
81#[inline]
82fn dot16_to_dot2(val: Dot2) -> Dot16 {
83 val >> (16 - SAMPLE_SHIFT)
84}
85
86#[inline]
87fn dot2_to_int(val: Dot2) -> i32 {
88 val >> SAMPLE_SHIFT
89}
90
91#[inline]
92fn int_to_dot2(val: i32) -> Dot2 {
93 val << SAMPLE_SHIFT
94}
95
96#[inline]
97fn f32_to_dot2(val: f32) -> Dot2 {
98 (val * SAMPLE_SIZE) as i32
99}
100
101#[inline]
102fn dot2_to_dot6(val: Dot2) -> Dot6 {
103 val << 4
104}
105
106// it is possible to fit this into 64 bytes on x86-64
107// with the following layout:
108//
109// 4 x2,y2
110// 8 next
111// 6*4 slope_x,fullx,next_x,next_y, old_x,old_y
112// 4*4 dx,ddx,dy,ddy
113// 2 cury
114// 1 count
115// 1 shift
116//
117// some example counts 5704 curves, 1720 lines 7422 edges
118pub struct ActiveEdge {
119 x2: Dot2,
120 y2: Dot2,
121 next: Option<NonNull<ActiveEdge>>,
122 slope_x: Dot16,
123 fullx: Dot16,
124 next_x: Dot16,
125 next_y: Dot16,
126
127 dx: Dot16,
128 ddx: ShiftedDot16,
129 dy: Dot16,
130 ddy: ShiftedDot16,
131
132 old_x: Dot16,
133 old_y: Dot16,
134
135 // Shift is used to "scale" how fast we move along the quadratic curve.
136 // The key is that we scale dx and dy by the same amount so it doesn't need to have a physical unit
137 // as long as it doesn't overshoot.
138 // shift isdiv_fixed16_fixed16 probably also needed to balance number of bits that we need and make sure we don't
139 // overflow the 32 bits we have.
140 // It looks like some of quantities are stored in a "sliding fixed point" representation where the
141 // point depends on the shift.
142 shift: i32,
143 // we need to use count so that we make sure that we always line the last point up
144 // exactly. i.e. we don't have a great way to know when we're at the end implicitly.
145 count: i32,
146 winding: i8, // the direction of the edge
147}
148
149impl ActiveEdge {
150 fn new() -> ActiveEdge {
151 ActiveEdge {
152 x2: 0,
153 y2: 0,
154 next: None,
155 slope_x: 0,
156 fullx: 0,
157 next_x: 0,
158 next_y: 0,
159 dx: 0,
160 ddx: 0,
161 dy: 0,
162 ddy: 0,
163 old_x: 0,
164 old_y: 0,
165 shift: 0,
166 count: 0,
167 winding: 0,
168 }
169 }
170
171 // we want this to inline into step_edges() to
172 // avoid the call overhead
173 fn step(&mut self, cury: Dot2) {
174 // if we have a shift that means we have a curve
175 if self.shift != 0 {
176 if cury >= dot16_to_dot2(self.next_y) {
177 self.old_y = self.next_y;
178 self.old_x = self.next_x;
179 self.fullx = self.next_x;
180 // increment until we have a next_y that's greater
181 while self.count > 0 && cury >= dot16_to_dot2(self.next_y) {
182 self.next_x += self.dx >> self.shift;
183 self.dx += self.ddx;
184 self.next_y += self.dy >> self.shift;
185 self.dy += self.ddy;
186 self.count -= 1;
187 }
188 if self.count == 0 {
189 // for the last line sgement we can
190 // just set next_y,x to the end point
191 self.next_y = dot2_to_dot16(self.y2);
192 self.next_x = dot2_to_dot16(self.x2);
193 }
194 // update slope if we're going to be using it
195 // we want to avoid dividing by 0 which can happen if we exited the loop above early
196 if (cury + 1) < self.y2 {
197 self.slope_x = div_fixed16_fixed16(self.next_x - self.old_x, self.next_y - self.old_y) >> 2;
198 }
199 }
200 self.fullx += self.slope_x;
201 } else {
202 // XXX: look into bresenham to control error here
203 self.fullx += self.slope_x;
204 }
205 }
206}
207
208
209
210pub struct Rasterizer {
211 edge_starts: Vec<Option<NonNull<ActiveEdge>>>,
212 width: i32,
213 height: i32,
214 cur_y: Dot2,
215
216 // we use this rect to track the bounds of the added edges
217 bounds_top: i32,
218 bounds_bottom: i32,
219 bounds_left: i32,
220 bounds_right: i32,
221
222 active_edges: Option<NonNull<ActiveEdge>>,
223
224 edge_arena: Arena<ActiveEdge>,
225}
226
227impl Rasterizer {
228 pub fn new(width: i32, height: i32) -> Rasterizer {
229 let mut edge_starts = Vec::new();
230 for _ in 0..int_to_dot2(height) {
231 edge_starts.push(None);
232 }
233 Rasterizer {
234 width: int_to_dot2(width),
235 height: int_to_dot2(height),
236 bounds_right: 0,
237 bounds_left: width,
238 bounds_top: height,
239 bounds_bottom: 0,
240 cur_y: 0,
241 edge_starts,
242 edge_arena: Arena::new(),
243 active_edges: None,
244 }
245 }
246}
247
248// A cheap version of the "Alpha max plus beta min" algorithm (⍺=1, β=0.5)
249fn cheap_distance(mut dx: Dot6, mut dy: Dot6) -> Dot6 {
250 dx = dx.abs();
251 dy = dy.abs();
252 // return max + min/2
253 if dx > dy {
254 dx + (dy >> 1)
255 } else {
256 dy + (dx >> 1)
257 }
258}
259
260fn diff_to_shift(dx: Dot6, dy: Dot6) -> i32 {
261 // cheap calc of distance from center of p0-p2 to the center of the curve
262 let mut dist = cheap_distance(dx, dy);
263
264 // shift down dist (it is currently in dot6)
265 // down by 5 should give us 1/2 pixel accuracy (assuming our dist is accurate...)
266 // this is chosen by heuristic: make it as big as possible (to minimize segments)
267 // ... but small enough so that our curves still look smooth
268 dist = (dist + (1 << 4)) >> 5;
269
270 // each subdivision (shift value) cuts this dist (error) by 1/4
271 (32 - ((dist as u32).leading_zeros()) as i32) >> 1
272}
273
274// this metric is taken from skia
275fn compute_curve_steps(e: &Edge) -> i32 {
276 let dx = e.control_x * 2 - e.x1 - e.x2;
277 let dy = e.control_y * 2 - e.y1 - e.y2;
278 let shift = diff_to_shift(dot2_to_dot6(dx), dot2_to_dot6(dy));
279 assert!(shift >= 0);
280
281 shift
282}
283
284const SAMPLE_SIZE: f32 = (1 << SAMPLE_SHIFT) as f32;
285pub const SAMPLE_SHIFT: i32 = 2;
286
287/* We store 1<<shift in a (signed) byte, so its maximum value is 1<<6 == 64.
288 Note that this limits the number of lines we use to approximate a curve.
289 If we need to increase this, we need to store fCurveCount in something
290 larger than int8_t.
291*/
292const MAX_COEFF_SHIFT: i32 = 6;
293
294// An example number of edges is 7422 but
295// can go as high as edge count: 374640
296// with curve count: 67680
297impl Rasterizer {
298
299 // Overflow:
300 // cairo just does _cairo_fixed_from_double (x) which ends up having some
301 // weird behaviour around overflow because of the double to fixed conversion trick that it does.
302
303 // Fitz clips edges to the bounding box
304 #[allow(non_snake_case)]
305 pub fn add_edge(&mut self, mut start: Point, mut end: Point, curve: bool, control: Point) {
306 if curve {
307 //println!("add_edge {}, {} - {}, {} - {}, {}", start.x, start.y, control.x, control.y, end.x, end.y);
308 } else {
309 //println!("add_edge {}, {} - {}, {}", start.x, start.y, end.x, end.y);
310 }
311 // order the points from top to bottom
312
313 // how do we deal with edges to the right and left of the canvas?
314 let e = self.edge_arena.alloc(ActiveEdge::new());
315 if end.y < start.y {
316 std::mem::swap(&mut start, &mut end);
317 e.winding = -1;
318 } else {
319 e.winding = 1;
320 }
321 let edge = Edge {
322 x1: f32_to_dot2(start.x),
323 y1: f32_to_dot2(start.y),
324 control_x: f32_to_dot2(control.x),
325 control_y: f32_to_dot2(control.y),
326 x2: f32_to_dot2(end.x),
327 y2: f32_to_dot2(end.y),
328 };
329 e.x2 = edge.x2;
330 e.y2 = edge.y2;
331
332 e.next = None;
333 //e.curx = e.edge.x1;
334 let mut cury = edge.y1;
335 e.fullx = dot2_to_dot16(edge.x1);
336
337 // if the edge is completely above or completely below we can drop it
338 if edge.y2 < 0 || edge.y1 >= self.height {
339 return;
340 }
341
342 // drop horizontal edges
343 if cury >= e.y2 {
344 return;
345 }
346
347 self.bounds_top = self.bounds_top.min(dot2_to_int(edge.y1));
348 self.bounds_bottom = self.bounds_bottom.max(dot2_to_int(edge.y2 + 3));
349
350 self.bounds_left = self.bounds_left.min(dot2_to_int(edge.x1));
351 self.bounds_left = self.bounds_left.min(dot2_to_int(edge.x2));
352
353 self.bounds_right = self.bounds_right.max(dot2_to_int(edge.x1 + 3));
354 self.bounds_right = self.bounds_right.max(dot2_to_int(edge.x2 + 3));
355
356 if curve {
357 self.bounds_left = self.bounds_left.min(dot2_to_int(edge.control_x));
358 self.bounds_right = self.bounds_right.max(dot2_to_int(edge.control_x + 3));
359
360 // Based on Skia
361 // we'll iterate t from 0..1 (0-256)
362 // range of A is 4 times coordinate-range
363 // we can get more accuracy here by using the input points instead of the rounded versions
364 // A is derived from `dot2_to_dot16(2 * (from - 2 * ctrl + to))`, it is the second derivative of the
365 // quadratic bézier.
366 let mut A = (edge.x1 - edge.control_x - edge.control_x + edge.x2) << (15 - SAMPLE_SHIFT);
367 let mut B = edge.control_x - edge.x1; // The derivative at the start of the curve is 2 * (ctrl - from).
368 //let mut C = edge.x1;
369 let mut shift = compute_curve_steps(&edge);
370
371 if shift == 0 {
372 shift = 1;
373 } else if shift > MAX_COEFF_SHIFT {
374 shift = MAX_COEFF_SHIFT;
375 }
376 e.shift = shift;
377 e.count = 1 << shift;
378 e.dx = 2 * (A >> shift) + 2 * B * (1 << (16 - SAMPLE_SHIFT));
379 e.ddx = 2 * (A >> (shift - 1));
380
381 A = (edge.y1 - edge.control_y - edge.control_y + edge.y2) << (15 - SAMPLE_SHIFT);
382 B = edge.control_y - edge.y1;
383 //C = edge.y1;
384 e.dy = 2 * (A >> shift) + 2 * B * (1 << (16 - SAMPLE_SHIFT));
385 e.ddy = 2 * (A >> (shift - 1));
386
387 // compute the first next_x,y
388 e.count -= 1;
389 e.next_x = (e.fullx) + (e.dx >> e.shift);
390 e.next_y = (cury * (1 << (16 - SAMPLE_SHIFT))) + (e.dy >> e.shift);
391 e.dx += e.ddx;
392 e.dy += e.ddy;
393
394 // skia does this part in UpdateQuad. unfortunately we duplicate it
395 while e.count > 0 && cury >= dot16_to_dot2(e.next_y) {
396 e.next_x += e.dx >> shift;
397 e.dx += e.ddx;
398 e.next_y += e.dy >> shift;
399 e.dy += e.ddy;
400 e.count -= 1;
401 }
402 if e.count == 0 {
403 e.next_y = dot2_to_dot16(edge.y2);
404 e.next_x = dot2_to_dot16(edge.x2);
405 }
406 e.slope_x = (e.next_x - (e.fullx)) / dot16_to_dot2(e.next_y - dot2_to_dot16(cury));
407 } else {
408 e.shift = 0;
409 e.slope_x = (edge.x2 - edge.x1) * (1 << (16 - SAMPLE_SHIFT)) / (edge.y2 - edge.y1);
410 }
411
412 if cury < 0 {
413 // XXX: we could compute an intersection with the top and bottom so we don't need to step them into view
414 // for curves we can just step them into place.
415 while cury < 0 {
416 e.step(cury);
417 cury += 1;
418 }
419
420 // cury was adjusted so check again for horizontal edges
421 if cury >= e.y2 {
422 return;
423 }
424 }
425
426 // add to the beginning of the edge start list
427 // if edges are added from left to right
428 // they'll be in this list from right to left
429 // this works out later during insertion
430 e.next = self.edge_starts[cury as usize];
431 self.edge_starts[cury as usize] = Some(NonNull::from(e));
432 }
433
434 fn step_edges(&mut self) {
435 let mut prev_ptr = &mut self.active_edges as *mut _;
436 let mut edge = self.active_edges;
437 let cury = self.cur_y; // avoid any aliasing problems
438 while let Some(mut e_ptr) = edge {
439 let e = unsafe { e_ptr.as_mut() };
440 e.step(cury);
441 // avoid aliasing between edge->next and prev_ptr so that we can reuse next
442 let next = e.next;
443 // remove any finished edges
444 if (cury + 1) >= e.y2 {
445 // remove from active list
446 unsafe { *prev_ptr = next };
447 } else {
448 prev_ptr = &mut e.next;
449 }
450 edge = next;
451 }
452 }
453 /*
454 int comparisons;
455 static inline void dump_edges(ActiveEdge *e)
456 {
457 while (e) {
458 printf("%d ", e.fullx);
459 e = e.next;
460 }
461 printf("\n");
462 }
463 */
464 // Insertion sort the new edges into the active list
465 // The new edges could be showing up at any x coordinate
466 // but existing active edges will be sorted.
467 //
468 // Merge in the new edges. Since both lists are sorted we can do
469 // this in a single pass.
470 // Note: we could do just O(1) append the list of new active edges
471 // to the existing active edge list, but then we'd have to sort
472 // the entire resulting list
473 fn insert_starting_edges(&mut self) {
474 let mut new_edges: Option<NonNull<ActiveEdge>> = None;
475 let mut edge = self.edge_starts[self.cur_y as usize];
476 // insertion sort all of the new edges
477 while let Some(mut e_ptr) = edge {
478 let e = unsafe { e_ptr.as_mut() };
479 let mut prev_ptr = &mut new_edges as *mut _;
480 let mut new = new_edges;
481 while let Some(mut new_ptr) = new {
482 let a = unsafe { new_ptr.as_mut() };
483 if e.fullx <= a.fullx {
484 break;
485 }
486 // comparisons++;
487 prev_ptr = &mut a.next;
488 new = a.next;
489 }
490 edge = e.next;
491 e.next = new;
492 unsafe { *prev_ptr = Some(e_ptr) };
493 }
494
495 // merge the sorted new_edges into active_edges
496 let mut prev_ptr = &mut self.active_edges as *mut _;
497 let mut active = self.active_edges;
498 let mut edge = new_edges;
499 while let Some(mut e_ptr) = edge {
500 let e = unsafe { e_ptr.as_mut() };
501 while let Some(mut a_ptr) = active {
502 let a = unsafe { a_ptr.as_mut() };
503 if e.fullx <= a.fullx {
504 break;
505 }
506
507 // comparisons++;
508 prev_ptr = &mut a.next;
509 active = a.next;
510 }
511 edge = e.next;
512 e.next = active;
513 let next_prev_ptr = &mut e.next as *mut _;
514 unsafe { *prev_ptr = Some(e_ptr) };
515 prev_ptr = next_prev_ptr;
516 }
517 }
518
519 // Skia does stepping and scanning of edges in a single
520 // pass over the edge list.
521 fn scan_edges(&mut self, blitter: &mut dyn RasterBlitter, winding_mode: Winding) {
522 let mut edge = self.active_edges;
523 let mut winding = 0;
524
525 // handle edges that begin to the left of the bitmap
526 while let Some(mut e_ptr) = edge {
527 let e = unsafe { e_ptr.as_mut() };
528 if e.fullx >= 0 {
529 break;
530 }
531 winding += e.winding as i32;
532 edge = e.next;
533 }
534
535 let mut prevx = 0;
536 while let Some(mut e_ptr) = edge {
537 let e = unsafe { e_ptr.as_mut() };
538
539 let inside = match winding_mode {
540 Winding::EvenOdd => winding & 1 != 0,
541 Winding::NonZero => winding != 0,
542 };
543
544 if inside {
545 blitter.blit_span(
546 self.cur_y,
547 dot16_to_dot2(prevx + (1 << (15 - SAMPLE_SHIFT))),
548 dot16_to_dot2(e.fullx + (1 << (15 - SAMPLE_SHIFT))),
549 );
550 }
551
552 if dot16_to_dot2(e.fullx) >= self.width {
553 break;
554 }
555 winding += e.winding as i32;
556 prevx = e.fullx;
557 edge = e.next;
558 }
559
560 // we don't need to worry about any edges beyond width
561 }
562
563 // You may have heard that one should never use a bubble sort.
564 // However in our situation a bubble sort is actually a good choice.
565 // The input list will be mostly sorted except for a couple of lines
566 // that have need to be swapped. Further it is common that our edges are
567 // already sorted and bubble sort lets us avoid doing any memory writes.
568
569 // Some statistics from using a bubble sort on an
570 // example scene. You can see that bubble sort does
571 // noticeably better than O (n lg n).
572 // summary(edges*bubble_sort_iterations)
573 // Min. 1st Qu. Median Mean 3rd Qu. Max.
574 // 0.0 9.0 69.0 131.5 206.0 1278.0
575 // summary(edges*log2(edges))
576 // Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
577 // 0.00 28.53 347.10 427.60 787.20 1286.00 2.00
578 fn sort_edges(&mut self) {
579 if self.active_edges.is_none() {
580 return;
581 }
582
583 let mut swapped;
584 loop {
585 swapped = false;
586 let mut edge = self.active_edges.unwrap();
587 let mut next_edge = unsafe { edge.as_mut() }.next;
588 let mut prev = &mut self.active_edges as *mut _;
589 while let Some(mut next_ptr) = next_edge {
590 let next = unsafe { next_ptr.as_mut() };
591 if unsafe { edge.as_mut() }.fullx > next.fullx {
592 // swap edge and next
593 unsafe { edge.as_mut() }.next = next.next;
594 next.next = Some(edge);
595 unsafe { (*prev) = Some(next_ptr) };
596 swapped = true;
597 }
598 prev = (&mut unsafe { edge.as_mut() }.next) as *mut _;
599 edge = next_ptr;
600 next_edge = unsafe { edge.as_mut() }.next;
601 }
602 if !swapped {
603 break;
604 }
605 }
606 }
607
608 pub fn rasterize(&mut self, blitter: &mut dyn RasterBlitter, winding_mode: Winding) {
609 let start = int_to_dot2(self.bounds_top).max(0);
610 let end = int_to_dot2(self.bounds_bottom).min(self.height);
611
612 self.cur_y = start;
613 while self.cur_y < end {
614 // we do 4x4 super-sampling so we need
615 // to scan 4 times before painting a line of pixels
616 for _ in 0..4 {
617 // insert the new edges into the sorted list
618 self.insert_starting_edges();
619 // scan over the edge list producing a list of spans
620 self.scan_edges(blitter, winding_mode);
621 // step all of the edges to the next scanline
622 // dropping the ones that end
623 self.step_edges();
624 // sort the remaining edges
625 self.sort_edges();
626 self.cur_y += 1;
627 }
628 }
629 }
630
631 pub fn get_bounds(&self) -> IntRect {
632 intrect(self.bounds_left.max(0),
633 self.bounds_top.max(0),
634 self.bounds_right.min(dot2_to_int(self.width)),
635 self.bounds_bottom.min(dot2_to_int(self.height)))
636 }
637
638 pub fn reset(&mut self) {
639 if self.bounds_bottom < self.bounds_top {
640 debug_assert_eq!(self.active_edges, None);
641 for e in &mut self.edge_starts {
642 debug_assert_eq!(*e, None);
643 }
644 debug_assert_eq!(self.bounds_bottom, 0);
645 debug_assert_eq!(self.bounds_right, 0);
646 debug_assert_eq!(self.bounds_top, dot2_to_int(self.height));
647 debug_assert_eq!(self.bounds_left, dot2_to_int(self.width));
648 // Currently we allocate an edge in the arena even if we don't
649 // end up putting it in the edge_starts list. Avoiding that
650 // would let us avoiding having to reinitialize the arena
651 self.edge_arena = Arena::new();
652 return;
653 }
654 let start = int_to_dot2(self.bounds_top).max(0) as usize;
655 let end = int_to_dot2(self.bounds_bottom).min(self.height) as usize;
656 self.active_edges = None;
657 for e in &mut self.edge_starts[start..end] {
658 *e = None;
659 }
660 self.edge_arena = Arena::new();
661 self.bounds_bottom = 0;
662 self.bounds_right = 0;
663 self.bounds_top = dot2_to_int(self.height);
664 self.bounds_left = dot2_to_int(self.width);
665 }
666}