open-standards/wai/wai-vector/src/place.rs
Transaction Science 5b0f2abde0
All checks were successful
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 2m16s
standards workspaces build + test / aac-rs (pull_request) Successful in 1m35s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 51s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 34s
standards workspaces build + test / map-rs (pull_request) Successful in 31s
standards workspaces build + test / mesh-rs (pull_request) Successful in 30s
standards workspaces build + test / openpay (pull_request) Successful in 1m40s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 7m35s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 27s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 2m6s
wai-vector vector graphics / linux-aarch64 (pull_request) Successful in 25s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 3m28s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 8m4s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 12m12s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 34s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 4m9s
wai-vector vector graphics / macos-arm64 (pull_request) Successful in 31s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 6s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 5s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 5s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m15s
standards workspaces build + test / eoc-rs (pull_request) Successful in 2m6s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m35s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m15s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m16s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m22s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m19s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m26s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 4m14s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m24s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 16s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 8m43s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 10s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 31s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 54s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 2m3s
WAI section references / cited sections exist (pull_request) Successful in 10s
wai-vector vector graphics / linux-x86_64 (pull_request) Successful in 24s
wai-vector vector graphics / wasm32-wasip2 (wasmtime) (pull_request) Successful in 21s
wai-vector vector graphics / wasm32 under Node (pull_request) Successful in 19s
wai-vector vector graphics / fuzz, peak allocation, comparison count (pull_request) Successful in 14s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 4s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 16m51s
wai-vector: clippy-clean, the empty frame range built without a reversed literal; no generated byte changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 04:29:28 -04:00

334 lines
13 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Placement (§3.3), the load-time interval check (§3.4(a)) and the per-frame cost
//! (§3.4(b)), on the driver, in `i64`.
//!
//! The interval check is this interval computation, not the true range of the
//! device coordinates: every sink refuses the same documents.
use alloc::vec;
use alloc::vec::Vec;
use crate::arith::{floor_div, r_half_up, r_rat};
use crate::bounds::*;
use crate::refuse::{Code, Refusal, refuse};
use crate::wos1::Wos1;
use crate::wvg1::{Doc, Item, KIND_BEGIN, KIND_END, KIND_PATH, KIND_RUN, M00, M11, OP, TX, TY};
// ---------------------------------------------------------------- placement (§3.3)
/// The composed linear map `L_ij = R_rat(M_ij·s·w, dw·2^16)`, in Q16 device subpixels
/// per outline unit.
pub fn linear_map(m: [i64; 4], s: i64, w: i64, dw: i64) -> [i64; 4] {
let den = dw << 16;
m.map(|mij| r_rat(mij * s * w, den))
}
/// The origin of a glyph record at run-relative `(gx, gy)` design subpixels, for the
/// translation sum `o` and matrix `m`.
pub fn origin(o: [i64; 2], m: [i64; 4], g: [i64; 2], w: i64, dw: i64) -> [i64; 2] {
let den = dw << 16;
[
r_rat(((o[0] << 16) + m[0] * g[0] + m[1] * g[1]) * w, den),
r_rat(((o[1] << 16) + m[2] * g[0] + m[3] * g[1]) * w, den),
]
}
/// The point term `(R_half_up(L00·px + L01·py, 16), R_half_up(L10·px + L11·py, 16))`.
pub fn point_offset(l: [i64; 4], p: [i64; 2]) -> [i64; 2] {
[r_half_up(l[0] * p[0] + l[1] * p[1], 16), r_half_up(l[2] * p[0] + l[3] * p[1], 16)]
}
/// The identity matrix, and the default of each slot when absent.
pub const IDENTITY: [i64; 4] = [65_536, 0, 0, 65_536];
pub const SLOT_DEFAULT: [i64; 7] = [65_536, 0, 0, 65_536, 0, 0, 255];
// ---------------------------------------------------------------- intervals (§3.4(a))
/// A closed integer interval.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Iv {
pub lo: i64,
pub hi: i64,
}
impl Iv {
pub const fn point(v: i64) -> Iv {
Iv { lo: v, hi: v }
}
pub fn plus(self, o: Iv) -> Iv {
Iv { lo: self.lo + o.lo, hi: self.hi + o.hi }
}
/// The interval times a scalar: the interval between the two end products.
pub fn scale(self, k: i64) -> Iv {
let (a, b) = (self.lo * k, self.hi * k);
Iv { lo: a.min(b), hi: a.max(b) }
}
/// The corner hull of two intervals' product.
pub fn hull(self, o: Iv) -> Iv {
let c = [self.lo * o.lo, self.lo * o.hi, self.hi * o.lo, self.hi * o.hi];
Iv { lo: c.iter().copied().min().unwrap_or(0), hi: c.iter().copied().max().unwrap_or(0) }
}
/// A non-decreasing function applied at each end.
pub fn map(self, f: impl Fn(i64) -> i64) -> Iv {
Iv { lo: f(self.lo), hi: f(self.hi) }
}
}
/// What the interval check and the cost need of each item, independent of the size.
pub struct Scene<'a> {
pub d: &'a Doc,
pub sets: &'a [Wos1],
/// Each track's `[min, max]` over its key values.
pub track_iv: Vec<Iv>,
/// For each fill item, `O`'s interval per axis: its own translation plus every
/// enclosing group's. For a group begin, its clip's: the group's own plus every
/// enclosing group's.
pub item_o: Vec<[Iv; 2]>,
/// Each item's effective window, `[a, b)` with `a < b`, or `None` when empty.
pub item_win: Vec<Option<(i64, i64)>>,
}
impl<'a> Scene<'a> {
pub fn new(d: &'a Doc, sets: &'a [Wos1]) -> Scene<'a> {
let track_iv = (0..d.tracks.len())
.map(|t| {
let ks = d.track_keys(t);
Iv {
lo: ks.iter().map(|k| i64::from(k.value)).min().unwrap_or(0),
hi: ks.iter().map(|k| i64::from(k.value)).max().unwrap_or(0),
}
})
.collect();
let mut s = Scene { d, sets, track_iv, item_o: Vec::with_capacity(d.items.len()), item_win: Vec::with_capacity(d.items.len()) };
let doc_win = (i64::from(d.header.t0), i64::from(d.header.t0) + i64::from(d.header.dur));
// Stacks of the enclosing groups' summed translation intervals and windows.
let mut o_stack: Vec<[Iv; 2]> = vec![[Iv::point(0); 2]];
let mut w_stack: Vec<(i64, i64)> = vec![doc_win];
for it in &d.items {
let acc = *o_stack.last().unwrap_or(&[Iv::point(0); 2]);
let win = *w_stack.last().unwrap_or(&doc_win);
if it.kind == KIND_END {
if o_stack.len() > 1 {
o_stack.pop();
w_stack.pop();
}
s.item_o.push([Iv::point(0); 2]);
s.item_win.push(None);
continue;
}
let own = [acc[0].plus(s.slot(it, TX)), acc[1].plus(s.slot(it, TY))];
let w = (win.0.max(i64::from(it.t_on)), win.1.min(i64::from(it.t_off)));
s.item_o.push(own);
s.item_win.push(if w.0 < w.1 { Some(w) } else { None });
if it.kind == KIND_BEGIN {
o_stack.push(own);
w_stack.push(w);
}
}
s
}
/// A slot's interval: its track's key range, or its default as one point.
pub fn slot(&self, it: &Item, s: usize) -> Iv {
match it.slots[s] {
NONE => Iv::point(SLOT_DEFAULT[s]),
t => self.track_iv[t as usize],
}
}
/// The matrix intervals of a fill item (the identity for a clip).
pub fn matrix(&self, it: &Item) -> [Iv; 4] {
if it.kind == KIND_BEGIN {
IDENTITY.map(Iv::point)
} else {
[self.slot(it, M00), self.slot(it, M00 + 1), self.slot(it, M00 + 2), self.slot(it, M11)]
}
}
}
/// The device interval of one fill instance on each axis (§3.4(a) step 4).
pub fn device_iv(o: [Iv; 2], m: [Iv; 4], l: [Iv; 4], g: [i64; 2], bx: [i16; 4], w: i64, dw: i64) -> [Iv; 2] {
let den = dw << 16;
let bxx = Iv { lo: i64::from(bx[0]), hi: i64::from(bx[2]) };
let bxy = Iv { lo: i64::from(bx[1]), hi: i64::from(bx[3]) };
let mut out = [Iv::point(0); 2];
for ax in 0..2 {
let (m1, m2) = (m[2 * ax], m[2 * ax + 1]);
let (l1, l2) = (l[2 * ax], l[2 * ax + 1]);
let num = o[ax].scale(1 << 16).plus(m1.scale(g[0])).plus(m2.scale(g[1]));
let org = num.map(|e| r_rat(e * w, den));
let pt = l1.hull(bxx).plus(l2.hull(bxy)).map(|e| r_half_up(e, 16));
out[ax] = org.plus(pt);
}
out
}
/// `L`'s interval from `M`'s, entry by entry.
pub fn linear_iv(m: [Iv; 4], s: i64, w: i64, dw: i64) -> [Iv; 4] {
let den = dw << 16;
m.map(|mi| mi.map(|e| r_rat(e * s * w, den)))
}
/// A fill instance: its item, its device interval, and its stored point count.
#[derive(Clone, Copy, Debug)]
pub struct Instance {
pub item: usize,
pub dev: [Iv; 2],
pub points: u64,
}
/// Every fill instance at width `w`, in item order (a group's clip at its begin), with
/// a run's glyph records in order.
pub fn instances(sc: &Scene, w: i64, mut f: impl FnMut(Instance)) {
let d = sc.d;
let dw = i64::from(d.header.design_w);
for (i, it) in d.items.iter().enumerate() {
let s = i64::from(it.unit_scale);
match it.kind {
KIND_BEGIN if it.reference != NONE => {
let m = sc.matrix(it);
let l = linear_iv(m, s, w, dw);
let p = &d.paths[it.reference as usize];
let dev = device_iv(sc.item_o[i], m, l, [0, 0], p.bbox, w, dw);
f(Instance { item: i, dev, points: u64::from(p.n_points) });
}
KIND_PATH => {
let m = sc.matrix(it);
let l = linear_iv(m, s, w, dw);
let p = &d.paths[it.reference as usize];
let dev = device_iv(sc.item_o[i], m, l, [0, 0], p.bbox, w, dw);
f(Instance { item: i, dev, points: u64::from(p.n_points) });
}
KIND_RUN => {
let m = sc.matrix(it);
let l = linear_iv(m, s, w, dw);
let set = &sc.sets[it.reference as usize];
for g in d.run_glyphs(i) {
let gl = set.glyph(g.gid).expect("step 7 found every glyph");
let bx = [gl.xmin, gl.ymin, gl.xmax, gl.ymax];
let dev = device_iv(sc.item_o[i], m, l, [i64::from(g.x), i64::from(g.y)], bx, w, dw);
f(Instance { item: i, dev, points: u64::from(gl.n_points) });
}
}
_ => {}
}
}
}
/// Step 8: every end of every device interval lies in `[−2^22, 2^22 − 1]`, at every
/// listed size, for every fill instance whether or not it is ever active.
pub fn interval_check(d: &Doc, sets: &[Wos1]) -> Result<(), Refusal> {
let sc = Scene::new(d, sets);
for (z, size) in d.sizes.iter().enumerate() {
let mut bad = false;
instances(&sc, i64::from(size.w), |inst| {
for iv in inst.dev {
if iv.lo < DEVICE_MIN || iv.hi > DEVICE_MAX {
bad = true;
}
}
});
if bad {
return refuse(Code::GeometryRange, "8", "a device interval leaves [-2^22, 2^22 - 1]", z as u64);
}
}
Ok(())
}
// ---------------------------------------------------------------- the cost (§3.4(b))
/// The clipped pixel box of a device interval: `(c_lo, c_hi, bw, r_lo, r_hi, bh)`.
pub fn pixel_box(dev: [Iv; 2], w: i64, h: i64) -> (i64, i64, i64, i64, i64, i64) {
let c_lo = floor_div(dev[0].lo, 256).max(0);
let c_hi = floor_div(dev[0].hi, 256).min(w - 1);
let r_lo = floor_div(dev[1].lo, 256).max(0);
let r_hi = floor_div(dev[1].hi, 256).min(h - 1);
(c_lo, c_hi, (c_hi - c_lo + 1).max(0), r_lo, r_hi, (r_hi - r_lo + 1).max(0))
}
/// A fill's cost `c(f) = p·(2^10 + bw + bh) + bw·bh`.
pub fn fill_cost(p: u64, bw: u64, bh: u64) -> u64 {
p * (1024 + bw + bh) + bw * bh
}
/// Each item's cost at size `(w, h)` (§3.4(b) step 3).
pub fn item_costs(sc: &Scene, w: i64, h: i64) -> Vec<u64> {
let d = sc.d;
let mut cost = vec![0u64; d.items.len()];
// A run's hull over its glyph records whose box is not empty.
let mut hull: Vec<Option<(i64, i64, i64, i64)>> = vec![None; d.items.len()];
for (i, it) in d.items.iter().enumerate() {
if it.kind == KIND_BEGIN {
cost[i] = (w * h) as u64;
}
}
instances(sc, w, |inst| {
let (c_lo, c_hi, bw, r_lo, r_hi, bh) = pixel_box(inst.dev, w, h);
cost[inst.item] += fill_cost(inst.points, bw as u64, bh as u64);
if d.items[inst.item].kind == KIND_RUN && bw > 0 && bh > 0 {
let e = &mut hull[inst.item];
*e = Some(match *e {
None => (c_lo, c_hi, r_lo, r_hi),
Some((a, b, c, dd)) => (a.min(c_lo), b.max(c_hi), c.min(r_lo), dd.max(r_hi)),
});
}
});
for (i, hl) in hull.iter().enumerate() {
if let Some((c_lo, c_hi, r_lo, r_hi)) = *hl {
cost[i] += ((c_hi - c_lo + 1) * (r_hi - r_lo + 1)) as u64;
}
}
cost
}
/// `cost(z) = w·h + max over every integer tick of the summed costs of the items whose
/// effective window holds it`, by sorting the windows' edges, ends before starts at
/// equal ticks.
pub fn size_cost(sc: &Scene, w: i64, h: i64) -> u64 {
let costs = item_costs(sc, w, h);
let mut edges: Vec<(i64, u8, u64)> = Vec::new();
for (i, c) in costs.iter().enumerate() {
if let (Some((a, b)), true) = (sc.item_win[i], *c > 0) {
edges.push((a, 1, *c));
edges.push((b, 0, *c));
}
}
edges.sort_unstable();
let (mut cur, mut best) = (0u64, 0u64);
for (_, start, c) in edges {
if start == 1 {
cur += c;
best = best.max(cur);
} else {
cur -= c;
}
}
(w * h) as u64 + best
}
/// Every listed size's cost, in `SIZE` order.
pub fn costs(d: &Doc, sets: &[Wos1]) -> Vec<u64> {
let sc = Scene::new(d, sets);
d.sizes.iter().map(|z| size_cost(&sc, i64::from(z.w), i64::from(z.h))).collect()
}
/// Step 9: for each listed size in order, `too_large` above the ceiling, otherwise
/// `payload_malformed` when it differs from `SIZE.cost`.
pub fn check_costs(d: &Doc, sets: &[Wos1]) -> Result<Vec<u64>, Refusal> {
let all = costs(d, sets);
for (z, (&c, size)) in all.iter().zip(&d.sizes).enumerate() {
if c > COST_CEILING {
return refuse(Code::TooLarge, "9", "cost above 2^32 - 1", z as u64);
}
if c != u64::from(size.cost) {
return refuse(Code::PayloadMalformed, "9", "SIZE.cost differs from the cost", z as u64);
}
}
Ok(all)
}
/// The slot index of an item's opacity, for callers outside this module.
pub const OPACITY_SLOT: usize = OP;
/// Whether an item is a fill.
pub fn is_fill(it: &Item) -> bool {
it.kind == KIND_PATH || it.kind == KIND_RUN
}