Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no comparison (pull_request) Has been cancelled
no-vanity-metrics / WAI public text names no company or product (pull_request) Has been cancelled
standards workspaces build + test / jouleclaw-rs (pull_request) Has been cancelled
standards workspaces build + test / aac-rs (pull_request) Has been cancelled
standards workspaces build + test / arl-rs (pull_request) Has been cancelled
standards workspaces build + test / eoc-rs (pull_request) Has been cancelled
standards workspaces build + test / jcp-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-ui-rs (pull_request) Has been cancelled
standards workspaces build + test / joulecontract-rs (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (pull_request) Has been cancelled
standards workspaces build + test / joulehook-rs (pull_request) Has been cancelled
standards workspaces build + test / map-rs (pull_request) Has been cancelled
standards workspaces build + test / mesh-rs (pull_request) Has been cancelled
standards workspaces build + test / openpay (pull_request) Has been cancelled
standards workspaces build + test / proof-rs (pull_request) Has been cancelled
standards workspaces build + test / sandbox-rs (pull_request) Has been cancelled
standards workspaces build + test / sense-rs (pull_request) Has been cancelled
standards workspaces build + test / smart-byte-rs (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI section references / cited sections exist (pull_request) Has been cancelled
wai-vector vector graphics / linux-aarch64 (pull_request) Has been cancelled
wai-vector vector graphics / linux-x86_64 (pull_request) Has been cancelled
wai-vector vector graphics / macos-arm64 (pull_request) Has been cancelled
wai-vector vector graphics / wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
wai-vector vector graphics / wasm32 under Node (pull_request) Has been cancelled
wai-vector vector graphics / fuzz, peak allocation, comparison count (pull_request) Has been cancelled
workflow hygiene / every workflow supersedes its own queue (pull_request) Has been cancelled
The D7-2 cleanup (no generated byte changes), with the fuzz seed conflict resolved to D7-2's literal; the raster's chunked loops on as_chunks, collapsed conditionals, and no placeholder bindings. Co-Authored-By: Claude Opus 5.5 <[email protected]>
90 lines
3 KiB
Rust
90 lines
3 KiB
Rust
//! RS7 `vec_composite`, RS8 `vec_group` and RS9 `vec_unpremul` (§3.9, §2.9).
|
||
//!
|
||
//! A fill: `p′ = div255(p·o)` once per item per frame; `s = div255(p′·α)` at each pixel;
|
||
//! then source-over, `T ← s + div255(T·(255 − s_A))`. A group's pop:
|
||
//! `k = div255(m·o_g)`, `s = div255(G·k)`, then source-over into the parent. The order
|
||
//! is part of the definition. Every value stays below 2^24, and `c ≤ a ≤ 255` holds
|
||
//! throughout with no clamp.
|
||
|
||
use alloc::vec;
|
||
use alloc::vec::Vec;
|
||
|
||
use crate::arith::{div255, unpremul};
|
||
|
||
/// A premultiplied RGBA8 target of `w × h` pixels, row-major.
|
||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||
pub struct Target {
|
||
pub w: usize,
|
||
pub h: usize,
|
||
pub px: Vec<u8>,
|
||
}
|
||
|
||
impl Target {
|
||
/// A target cleared to `(0, 0, 0, 0)`.
|
||
pub fn clear(w: usize, h: usize) -> Target {
|
||
Target { w, h, px: vec![0; 4 * w * h] }
|
||
}
|
||
}
|
||
|
||
/// A fill's paint after its opacity: `p′_ch = div255(p_ch·o)`.
|
||
#[inline]
|
||
pub fn paint_opacity(p: [u8; 4], o: u8) -> [u32; 4] {
|
||
p.map(|c| div255(u32::from(c) * u32::from(o)))
|
||
}
|
||
|
||
/// Source-over of `s` into the pixel `t`.
|
||
#[inline]
|
||
pub fn src_over(t: &mut [u8], s: [u32; 4]) {
|
||
let inv = 255 - s[3];
|
||
for ch in 0..4 {
|
||
t[ch] = (s[ch] + div255(u32::from(t[ch]) * inv)) as u8;
|
||
}
|
||
}
|
||
|
||
/// RS7: composite a paint `p′` at each pixel of a box, with the coverage `alpha(c, r)`
|
||
/// at canvas pixel `(c, r)`.
|
||
pub fn composite(t: &mut Target, p: [u32; 4], c0: i64, r0: i64, bw: usize, bh: usize, alpha: impl Fn(usize, usize) -> u8) {
|
||
for r in 0..bh {
|
||
let y = r0 as usize + r;
|
||
for c in 0..bw {
|
||
let a = u32::from(alpha(c, r));
|
||
if a == 0 {
|
||
continue;
|
||
}
|
||
let s = p.map(|ch| div255(ch * a));
|
||
let i = 4 * (y * t.w + c0 as usize + c);
|
||
src_over(&mut t.px[i..i + 4], s);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// RS8: pop a group's target `g` into its parent, with its clip's coverage `mask(x, y)`
|
||
/// (255 where it has no clip) and its opacity `o_g`.
|
||
pub fn group_pop(parent: &mut Target, g: &Target, o_g: u8, mask: impl Fn(usize, usize) -> u8) {
|
||
for y in 0..g.h {
|
||
for x in 0..g.w {
|
||
let i = 4 * (y * g.w + x);
|
||
if g.px[i + 3] == 0 {
|
||
// G = (0, 0, 0, 0): s = 0, and source-over of 0 leaves the parent.
|
||
continue;
|
||
}
|
||
let k = div255(u32::from(mask(x, y)) * u32::from(o_g));
|
||
let s = [0, 1, 2, 3].map(|ch| div255(u32::from(g.px[i + ch]) * k));
|
||
src_over(&mut parent.px[i..i + 4], s);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// RS9: `RGBA8S`, the straight-alpha view of an `RGBA8P` buffer (§2.9).
|
||
pub fn unpremul_buffer(p: &[u8]) -> Vec<u8> {
|
||
let mut out = Vec::with_capacity(p.len());
|
||
for px in p.as_chunks::<4>().0 {
|
||
let a = px[3];
|
||
if a == 0 {
|
||
out.extend_from_slice(&[0, 0, 0, 0]);
|
||
} else {
|
||
out.extend_from_slice(&[unpremul(px[0], a), unpremul(px[1], a), unpremul(px[2], a), a]);
|
||
}
|
||
}
|
||
out
|
||
}
|