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}