blob: 85f78c0bf36618ca6238bdae05512630259ba609 [file] [log] [blame] [edit]
// Copyright 2023 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
use vello_encoding::{ConfigUniform, PathSegment, Tile};
use super::{CMD_COLOR, CMD_END, CMD_FILL, CMD_JUMP, CMD_SOLID, CpuTexture, PTCL_INITIAL_ALLOC};
// These should also move into a common area
const TILE_WIDTH: usize = 16;
const TILE_HEIGHT: usize = 16;
const TILE_SIZE: usize = TILE_WIDTH * TILE_HEIGHT;
fn read_color(ptcl: &[u32], offset: u32) -> u32 {
ptcl[(offset + 1) as usize]
}
struct CmdFill {
size_and_rule: u32,
seg_data: u32,
backdrop: i32,
}
fn read_fill(ptcl: &[u32], offset: u32) -> CmdFill {
let size_and_rule = ptcl[(offset + 1) as usize];
let seg_data = ptcl[(offset + 2) as usize];
let backdrop = ptcl[(offset + 3) as usize] as i32;
CmdFill {
size_and_rule,
seg_data,
backdrop,
}
}
fn unpack4x8unorm(x: u32) -> [f32; 4] {
let mut result = [0.0; 4];
for i in 0..4 {
result[i] = ((x >> (i * 8)) & 0xff) as f32 * (1.0 / 255.0);
}
result
}
fn pack4x8unorm(x: [f32; 4]) -> u32 {
let mut result = 0;
for i in 0..4 {
let byte = (x[i].clamp(0.0, 1.0) * 255.0).round() as u32;
result |= byte << (i * 8);
}
result
}
fn fill_path(area: &mut [f32], segments: &[PathSegment], fill: &CmdFill, x_tile: f32, y_tile: f32) {
let n_segs = fill.size_and_rule >> 1;
let even_odd = (fill.size_and_rule & 1) != 0;
let backdrop_f = fill.backdrop as f32;
for a in area.iter_mut() {
*a = backdrop_f;
}
for segment in &segments[fill.seg_data as usize..][..n_segs as usize] {
let delta = [
segment.point1[0] - segment.point0[0],
segment.point1[1] - segment.point0[1],
];
for yi in 0..TILE_HEIGHT {
let y = segment.point0[1] - (y_tile + yi as f32);
let y0 = y.clamp(0.0, 1.0);
let y1 = (y + delta[1]).clamp(0.0, 1.0);
let dy = y0 - y1;
let y_edge =
delta[0].signum() * (y_tile + yi as f32 - segment.y_edge + 1.0).clamp(0.0, 1.0);
if dy != 0.0 {
let vec_y_recip = delta[1].recip();
let t0 = (y0 - y) * vec_y_recip;
let t1 = (y1 - y) * vec_y_recip;
let startx = segment.point0[0] - x_tile;
let x0 = startx + t0 * delta[0];
let x1 = startx + t1 * delta[0];
let xmin0 = x0.min(x1);
let xmax0 = x0.max(x1);
for i in 0..TILE_WIDTH {
let i_f = i as f32;
let xmin = (xmin0 - i_f).min(1.0) - 1.0e-6;
let xmax = xmax0 - i_f;
let b = xmax.min(1.0);
let c = b.max(0.0);
let d = xmin.max(0.0);
let a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin);
area[yi * TILE_WIDTH + i] += y_edge + a * dy;
}
} else if y_edge != 0.0 {
for i in 0..TILE_WIDTH {
area[yi * TILE_WIDTH + i] += y_edge;
}
}
}
}
if even_odd {
for a in area.iter_mut() {
{
*a = (*a - 2.0 * (0.5 * *a).round()).abs();
}
}
} else {
for a in area.iter_mut() {
{
*a = a.abs().min(1.0);
}
}
}
}
#[expect(unused, reason = "Draft code as textures not wired up")]
fn fine_main(
config: &ConfigUniform,
tiles: &[Tile],
segments: &[PathSegment],
output: &mut CpuTexture,
ptcl: &[u32],
info: &[u32],
// TODO: image texture resources
// TODO: masks?
) {
let width_in_tiles = config.width_in_tiles;
let height_in_tiles = config.height_in_tiles;
let n_tiles = width_in_tiles * height_in_tiles;
let mut area = vec![0.0_f32; TILE_SIZE];
let mut rgba = vec![[0.0_f32; 4]; TILE_SIZE];
for tile_ix in 0..n_tiles {
rgba.fill([0.0; 4]);
area.fill(0.0);
let tile_x = tile_ix % width_in_tiles;
let tile_y = tile_ix / width_in_tiles;
let mut cmd_ix = tile_ix * PTCL_INITIAL_ALLOC;
// skip over blend stack allocation
cmd_ix += 1;
loop {
let tag = ptcl[cmd_ix as usize];
if tag == CMD_END {
break;
}
match tag {
CMD_FILL => {
let fill = read_fill(ptcl, cmd_ix);
// x0 and y0 will go away when we do tile-relative coords
let x0 = (tile_x as usize * TILE_WIDTH) as f32;
let y0 = (tile_y as usize * TILE_HEIGHT) as f32;
fill_path(&mut area, segments, &fill, x0, y0);
cmd_ix += 4;
}
CMD_SOLID => {
area.fill(1.0);
cmd_ix += 2;
}
CMD_COLOR => {
let color = read_color(ptcl, cmd_ix);
let fg = unpack4x8unorm(color);
let fg = [fg[3], fg[2], fg[1], fg[0]];
for i in 0..TILE_SIZE {
let ai = area[i];
let fg_i = [fg[0] * ai, fg[1] * ai, fg[2] * ai, fg[3] * ai];
for j in 0..4 {
rgba[i][j] = rgba[i][j] * (1.0 - fg_i[3]) + fg_i[j];
}
}
cmd_ix += 2;
}
CMD_JUMP => {
cmd_ix = ptcl[(cmd_ix + 1) as usize];
}
_ => todo!("unhandled ptcl command {tag}"),
}
}
// Write tile (in rgba)
for y in 0..TILE_HEIGHT {
let base =
output.width * (tile_y as usize * TILE_HEIGHT + y) + tile_x as usize * TILE_WIDTH;
for x in 0..TILE_WIDTH {
let rgba32 = pack4x8unorm(rgba[y * TILE_WIDTH + x]);
output.pixels[base + x] = rgba32;
}
}
}
}