| // Copyright 2022 The Vello authors |
| // SPDX-License-Identifier: Apache-2.0 OR MIT |
| |
| //! Create a lookup table of half-plane sample masks. |
| |
| // Width is number of discrete translations |
| const MASK_WIDTH: usize = 32; |
| // Height is the number of discrete slopes |
| const MASK_HEIGHT: usize = 32; |
| |
| const PATTERN: [u8; 8] = [0, 5, 3, 7, 1, 4, 6, 2]; |
| |
| fn one_mask(slope: f64, mut translation: f64, is_pos: bool) -> u8 { |
| if is_pos { |
| translation = 1. - translation; |
| } |
| let mut result = 0; |
| for (i, item) in PATTERN.iter().enumerate() { |
| let mut y = (i as f64 + 0.5) * 0.125; |
| let x = (*item as f64 + 0.5) * 0.125; |
| if !is_pos { |
| y = 1. - y; |
| } |
| if (x - (1.0 - translation)) * (1. - slope) - (y - translation) * slope >= 0. { |
| result |= 1 << i; |
| } |
| } |
| result |
| } |
| |
| /// Make a lookup table of half-plane masks. |
| /// |
| /// The table is organized into two blocks each with MASK_HEIGHT/2 slopes. |
| /// The first block is negative slopes (x decreases as y increates), |
| /// the second as positive. |
| pub fn make_mask_lut() -> Vec<u8> { |
| (0..MASK_WIDTH * MASK_HEIGHT) |
| .map(|i| { |
| const HALF_HEIGHT: usize = MASK_HEIGHT / 2; |
| let u = i % MASK_WIDTH; |
| let v = i / MASK_WIDTH; |
| let is_pos = v >= HALF_HEIGHT; |
| let y = ((v % HALF_HEIGHT) as f64 + 0.5) * (1.0 / HALF_HEIGHT as f64); |
| let x = (u as f64 + 0.5) * (1.0 / MASK_WIDTH as f64); |
| one_mask(y, x, is_pos) |
| }) |
| .collect() |
| } |
| |
| // Width is number of discrete translations |
| const MASK16_WIDTH: usize = 64; |
| // Height is the number of discrete slopes |
| const MASK16_HEIGHT: usize = 64; |
| |
| // This is based on the [D3D11 standard sample pattern]. |
| // |
| // [D3D11 standard sample pattern]: https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_standard_multisample_quality_levels |
| const PATTERN_16: [u8; 16] = [1, 8, 4, 11, 15, 7, 3, 12, 0, 9, 5, 13, 2, 10, 6, 14]; |
| |
| fn one_mask_16(slope: f64, mut translation: f64, is_pos: bool) -> u16 { |
| if is_pos { |
| translation = 1. - translation; |
| } |
| let mut result = 0; |
| for (i, item) in PATTERN_16.iter().enumerate() { |
| let mut y = (i as f64 + 0.5) * 0.0625; |
| let x = (*item as f64 + 0.5) * 0.0625; |
| if !is_pos { |
| y = 1. - y; |
| } |
| if (x - (1.0 - translation)) * (1. - slope) - (y - translation) * slope >= 0. { |
| result |= 1 << i; |
| } |
| } |
| result |
| } |
| |
| /// Make a lookup table of half-plane masks. |
| /// |
| /// The table is organized into two blocks each with MASK16_HEIGHT/2 slopes. |
| /// The first block is negative slopes (x decreases as y increates), |
| /// the second as positive. |
| pub fn make_mask_lut_16() -> Vec<u8> { |
| let v16 = (0..MASK16_WIDTH * MASK16_HEIGHT) |
| .map(|i| { |
| const HALF_HEIGHT: usize = MASK16_HEIGHT / 2; |
| let u = i % MASK16_WIDTH; |
| let v = i / MASK16_WIDTH; |
| let is_pos = v >= HALF_HEIGHT; |
| let y = ((v % HALF_HEIGHT) as f64 + 0.5) * (1.0 / HALF_HEIGHT as f64); |
| let x = (u as f64 + 0.5) * (1.0 / MASK16_WIDTH as f64); |
| one_mask_16(y, x, is_pos) |
| }) |
| .collect::<Vec<_>>(); |
| // This annoyingly makes another copy. We can avoid that by pushing two |
| // bytes per iteration of the above loop. |
| bytemuck::cast_slice(&v16).into() |
| } |