surpass/rasterizer/
mod.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use rayon::prelude::*;
6
7use crate::{Lines, PIXEL_SHIFT, PIXEL_WIDTH};
8
9mod grouped_iter;
10mod pixel_segment;
11
12use grouped_iter::GroupedIter;
13pub use pixel_segment::{bit_field_lens, search_last_by_key, PixelSegment, PixelSegmentUnpacked};
14
15// This finds the ith term in the ordered union of two sequences:
16//
17// 1. a * x + c
18// 2. b * x + d
19//
20// It works by estimating the amount of items that came from sequence 1 and
21// sequence 2 such that the next item will be the ith. This results in two
22// indices from each sequence. The final step is to simply pick the smaller one
23// which naturally comes next.
24fn find(i: i32, sum_recip: f32, cd: f32, a: f32, b: f32, c: f32, d: f32) -> f32 {
25    const BIAS: f32 = -0.000_000_5;
26
27    let i = i as f32;
28
29    let ja = if b.is_finite() { (b.mul_add(i, -cd).mul_add(sum_recip, BIAS)).ceil() } else { i };
30    let jb = if a.is_finite() { (a.mul_add(i, cd).mul_add(sum_recip, BIAS)).ceil() } else { i };
31
32    let guess_a = a.mul_add(ja, c);
33    let guess_b = b.mul_add(jb, d);
34
35    guess_a.min(guess_b)
36}
37
38fn round(v: f32) -> i32 {
39    unsafe { (v + 0.5).floor().to_int_unchecked() }
40}
41
42#[derive(Debug, Default)]
43pub struct Rasterizer<const TW: usize, const TH: usize> {
44    segments: Vec<PixelSegment<TW, TH>>,
45}
46
47impl<const TW: usize, const TH: usize> Rasterizer<TW, TH> {
48    pub fn new() -> Self {
49        Self::default()
50    }
51
52    pub fn segments(&self) -> &[PixelSegment<TW, TH>] {
53        self.segments.as_slice()
54    }
55
56    #[inline(never)]
57    pub fn rasterize(&mut self, lines: &Lines) {
58        // Shard the workload into set of similar output size in PixelSegment.
59        let iter = GroupedIter::new(&lines.lengths);
60
61        iter.into_par_iter()
62            .with_min_len(256)
63            .map(|(li, pi)| {
64                let a = lines.a[li as usize];
65                let b = lines.b[li as usize];
66                let c = lines.c[li as usize];
67                let d = lines.d[li as usize];
68
69                let i = pi as i32 - i32::from(c != 0.0) - i32::from(d != 0.0);
70
71                let i0 = i;
72                let i1 = i + 1;
73
74                let sum_recip = (a + b).recip();
75                let cd = c - d;
76
77                let t0 = find(i0, sum_recip, cd, a, b, c, d).max(0.0);
78                let t1 = find(i1, sum_recip, cd, a, b, c, d).min(1.0);
79
80                let x0f = t0.mul_add(lines.dx[li as usize], lines.x0[li as usize]);
81                let y0f = t0.mul_add(lines.dy[li as usize], lines.y0[li as usize]);
82                let x1f = t1.mul_add(lines.dx[li as usize], lines.x0[li as usize]);
83                let y1f = t1.mul_add(lines.dy[li as usize], lines.y0[li as usize]);
84
85                let x0_sub = round(x0f);
86                let x1_sub = round(x1f);
87                let y0_sub = round(y0f);
88                let y1_sub = round(y1f);
89
90                let border_x = x0_sub.min(x1_sub) >> PIXEL_SHIFT;
91                let border_y = y0_sub.min(y1_sub) >> PIXEL_SHIFT;
92
93                let tile_x = (border_x >> TW.trailing_zeros() as i32) as i16;
94                let tile_y = (border_y >> TH.trailing_zeros() as i32) as i16;
95                let local_x = (border_x & (TW - 1) as i32) as u8;
96                let local_y = (border_y & (TH - 1) as i32) as u8;
97
98                let border = (border_x << PIXEL_SHIFT) + PIXEL_WIDTH as i32;
99                let height = y1_sub - y0_sub;
100
101                let double_area_multiplier =
102                    ((x1_sub - x0_sub).abs() + 2 * (border - x0_sub.max(x1_sub))) as u8;
103                let cover = height as i8;
104
105                PixelSegment::new(
106                    lines.orders[li as usize],
107                    tile_x,
108                    tile_y,
109                    local_x,
110                    local_y,
111                    double_area_multiplier,
112                    cover,
113                )
114            })
115            .collect_into_vec(&mut self.segments);
116    }
117
118    const BIT_FIELD_LENGTH: [usize; 7] = bit_field_lens::<TW, TH>();
119
120    #[inline]
121    pub fn sort(&mut self) {
122        // Sort by (tile_y, tile_x, layer_id).
123        let offset = Self::BIT_FIELD_LENGTH[3]
124            + Self::BIT_FIELD_LENGTH[4]
125            + Self::BIT_FIELD_LENGTH[5]
126            + Self::BIT_FIELD_LENGTH[6];
127        self.segments.par_sort_unstable_by_key(|segment| {
128            let segment: u64 = segment.into();
129            segment >> offset
130        });
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    use crate::{GeomId, Layer, LinesBuilder, Order, Point, TILE_HEIGHT, TILE_WIDTH};
139
140    fn segments(p0: Point, p1: Point) -> Vec<PixelSegment<TILE_WIDTH, TILE_HEIGHT>> {
141        let mut builder = LinesBuilder::new();
142        builder.push(GeomId::default(), [p0, p1]);
143        let lines = builder
144            .build(|_| Some(Layer { order: Some(Order::new(0).unwrap()), ..Default::default() }));
145
146        let mut rasterizer = Rasterizer::default();
147        rasterizer.rasterize(&lines);
148
149        rasterizer.segments().to_vec()
150    }
151
152    fn areas_and_covers(segments: &[PixelSegment<TILE_WIDTH, TILE_HEIGHT>]) -> Vec<(i16, i8)> {
153        segments
154            .iter()
155            .map(|&segment| {
156                let segment: PixelSegmentUnpacked = segment.into();
157                (segment.double_area, segment.cover)
158            })
159            .collect()
160    }
161
162    #[test]
163    fn area_cover_octant_1() {
164        assert_eq!(
165            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(3.0, 2.0))),
166            [(11 * 16, 11), (5 * 8 + 2 * (5 * 8), 5), (5 * 8, 5), (11 * 16, 11)],
167        );
168    }
169
170    #[test]
171    fn area_cover_octant_2() {
172        assert_eq!(
173            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(2.0, 3.0))),
174            [(16 * 11 + 2 * (16 * 5), 16), (8 * 5, 8), (8 * 5 + 2 * (8 * 11), 8), (16 * 11, 16)],
175        );
176    }
177
178    #[test]
179    fn area_cover_octant_3() {
180        assert_eq!(
181            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(-2.0, 3.0))),
182            [(16 * 11, 16), (8 * 5 + 2 * (8 * 11), 8), (8 * 5, 8), (16 * 11 + 2 * (16 * 5), 16)],
183        );
184    }
185
186    #[test]
187    fn area_cover_octant_4() {
188        assert_eq!(
189            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(-3.0, 2.0))),
190            [(11 * 16, 11), (5 * 8, 5), (5 * 8 + 2 * (5 * 8), 5), (11 * 16, 11)],
191        );
192    }
193
194    #[test]
195    fn area_cover_octant_5() {
196        assert_eq!(
197            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(-3.0, -2.0))),
198            [(-(11 * 16), -11), (-(5 * 8), -5), (-(5 * 8 + 2 * (5 * 8)), -5), (-(11 * 16), -11)],
199        );
200    }
201
202    #[test]
203    fn area_cover_octant_6() {
204        assert_eq!(
205            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(-2.0, -3.0))),
206            [
207                (-(16 * 11), -16),
208                (-(8 * 5 + 2 * (8 * 11)), -8),
209                (-(8 * 5), -8),
210                (-(16 * 11 + 2 * (16 * 5)), -16),
211            ],
212        );
213    }
214
215    #[test]
216    fn area_cover_octant_7() {
217        assert_eq!(
218            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(2.0, -3.0))),
219            [
220                (-(16 * 11 + 2 * (16 * 5)), -16),
221                (-(8 * 5), -8),
222                (-(8 * 5 + 2 * (8 * 11)), -8),
223                (-(16 * 11), -16),
224            ],
225        );
226    }
227
228    #[test]
229    fn area_cover_octant_8() {
230        assert_eq!(
231            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(3.0, -2.0))),
232            [(-(11 * 16), -11), (-(5 * 8 + 2 * (5 * 8)), -5), (-(5 * 8), -5), (-(11 * 16), -11)],
233        );
234    }
235
236    #[test]
237    fn area_cover_axis_0() {
238        assert_eq!(areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(1.0, 0.0))), []);
239    }
240
241    #[test]
242    fn area_cover_axis_45() {
243        assert_eq!(
244            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(1.0, 1.0))),
245            [(16 * 16, 16)],
246        );
247    }
248
249    #[test]
250    fn area_cover_axis_90() {
251        assert_eq!(
252            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(0.0, 1.0))),
253            [(2 * 16 * 16, 16)],
254        );
255    }
256
257    #[test]
258    fn area_cover_axis_135() {
259        assert_eq!(
260            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(-1.0, 1.0))),
261            [(16 * 16, 16)],
262        );
263    }
264
265    #[test]
266    fn area_cover_axis_180() {
267        assert_eq!(areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(-1.0, 0.0))), []);
268    }
269
270    #[test]
271    fn area_cover_axis_225() {
272        assert_eq!(
273            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(-1.0, -1.0))),
274            [(-(16 * 16), -16)],
275        );
276    }
277
278    #[test]
279    fn area_cover_axis_270() {
280        assert_eq!(
281            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(0.0, -1.0))),
282            [(2 * -(16 * 16), -16)],
283        );
284    }
285
286    #[test]
287    fn area_cover_axis_315() {
288        assert_eq!(
289            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(-1.0, -1.0))),
290            [(-(16 * 16), -16)],
291        );
292    }
293
294    fn tiles(segments: &[PixelSegment<TILE_WIDTH, TILE_HEIGHT>]) -> Vec<(i16, i16, u8, u8)> {
295        segments
296            .iter()
297            .map(|&segment| {
298                let segment: PixelSegmentUnpacked = segment.into();
299                (segment.tile_x, segment.tile_y, segment.local_x, segment.local_y)
300            })
301            .collect()
302    }
303
304    #[test]
305    fn tile_octant_1() {
306        assert_eq!(
307            tiles(&segments(
308                Point::new(TILE_WIDTH as f32, TILE_HEIGHT as f32),
309                Point::new(TILE_WIDTH as f32 + 3.0, TILE_HEIGHT as f32 + 2.0),
310            )),
311            [(1, 1, 0, 0), (1, 1, 1, 0), (1, 1, 1, 1), (1, 1, 2, 1)],
312        );
313    }
314
315    #[test]
316    fn tile_octant_2() {
317        assert_eq!(
318            tiles(&segments(
319                Point::new(TILE_WIDTH as f32, TILE_HEIGHT as f32),
320                Point::new(TILE_WIDTH as f32 + 2.0, TILE_HEIGHT as f32 + 3.0),
321            )),
322            [(1, 1, 0, 0), (1, 1, 0, 1), (1, 1, 1, 1), (1, 1, 1, 2)],
323        );
324    }
325
326    #[test]
327    fn tile_octant_3() {
328        assert_eq!(
329            tiles(&segments(
330                Point::new(-(TILE_WIDTH as f32), TILE_HEIGHT as f32),
331                Point::new(-(TILE_WIDTH as f32) - 2.0, TILE_HEIGHT as f32 + 3.0),
332            )),
333            [
334                (-1, 1, TILE_WIDTH as u8 - 1, 0),
335                (-1, 1, TILE_WIDTH as u8 - 1, 1),
336                (-1, 1, TILE_WIDTH as u8 - 2, 1),
337                (-1, 1, TILE_WIDTH as u8 - 2, 2),
338            ],
339        );
340    }
341
342    #[test]
343    fn tile_octant_4() {
344        assert_eq!(
345            tiles(&segments(
346                Point::new(-(TILE_WIDTH as f32), TILE_HEIGHT as f32),
347                Point::new(-(TILE_WIDTH as f32) - 3.0, TILE_HEIGHT as f32 + 2.0),
348            )),
349            [
350                (-1, 1, TILE_WIDTH as u8 - 1, 0),
351                (-1, 1, TILE_WIDTH as u8 - 2, 0),
352                (-1, 1, TILE_WIDTH as u8 - 2, 1),
353                (-1, 1, TILE_WIDTH as u8 - 3, 1),
354            ],
355        );
356    }
357
358    #[test]
359    fn tile_octant_5() {
360        assert_eq!(
361            tiles(&segments(
362                Point::new(-(TILE_WIDTH as f32), -(TILE_HEIGHT as f32)),
363                Point::new(-(TILE_WIDTH as f32) - 3.0, -(TILE_HEIGHT as f32) - 2.0),
364            )),
365            [
366                (-1, -1, TILE_WIDTH as u8 - 1, TILE_HEIGHT as u8 - 1),
367                (-1, -1, TILE_WIDTH as u8 - 2, TILE_HEIGHT as u8 - 1),
368                (-1, -1, TILE_WIDTH as u8 - 2, TILE_HEIGHT as u8 - 2),
369                (-1, -1, TILE_WIDTH as u8 - 3, TILE_HEIGHT as u8 - 2),
370            ],
371        );
372    }
373
374    #[test]
375    fn tile_octant_6() {
376        assert_eq!(
377            tiles(&segments(
378                Point::new(-(TILE_WIDTH as f32), -(TILE_HEIGHT as f32)),
379                Point::new(-(TILE_WIDTH as f32) - 2.0, -(TILE_HEIGHT as f32) - 3.0),
380            )),
381            [
382                (-1, -1, TILE_WIDTH as u8 - 1, TILE_HEIGHT as u8 - 1),
383                (-1, -1, TILE_WIDTH as u8 - 1, TILE_HEIGHT as u8 - 2),
384                (-1, -1, TILE_WIDTH as u8 - 2, TILE_HEIGHT as u8 - 2),
385                (-1, -1, TILE_WIDTH as u8 - 2, TILE_HEIGHT as u8 - 3),
386            ],
387        );
388    }
389
390    #[test]
391    fn tile_octant_7() {
392        assert_eq!(
393            tiles(&segments(
394                Point::new(TILE_WIDTH as f32, -(TILE_HEIGHT as f32)),
395                Point::new(TILE_WIDTH as f32 + 2.0, -(TILE_HEIGHT as f32) - 3.0),
396            )),
397            [
398                (1, -1, 0, TILE_HEIGHT as u8 - 1),
399                (1, -1, 0, TILE_HEIGHT as u8 - 2),
400                (1, -1, 1, TILE_HEIGHT as u8 - 2),
401                (1, -1, 1, TILE_HEIGHT as u8 - 3),
402            ],
403        );
404    }
405
406    #[test]
407    fn tile_octant_8() {
408        assert_eq!(
409            tiles(&segments(
410                Point::new(TILE_WIDTH as f32, -(TILE_HEIGHT as f32)),
411                Point::new(TILE_WIDTH as f32 + 3.0, -(TILE_HEIGHT as f32) - 2.0),
412            )),
413            [
414                (1, -1, 0, TILE_HEIGHT as u8 - 1),
415                (1, -1, 1, TILE_HEIGHT as u8 - 1),
416                (1, -1, 1, TILE_HEIGHT as u8 - 2),
417                (1, -1, 2, TILE_HEIGHT as u8 - 2),
418            ],
419        );
420    }
421
422    #[test]
423    fn start_and_end_not_on_pixel_border() {
424        assert_eq!(
425            areas_and_covers(&segments(Point::new(0.5, 0.25), Point::new(4.0, 2.0)))[0],
426            (4 * 8, 4),
427        );
428
429        assert_eq!(
430            areas_and_covers(&segments(Point::new(0.0, 0.0), Point::new(3.5, 1.75)))[4],
431            (4 * 8 + 2 * (4 * 8), 4),
432        );
433    }
434}