ifc_lite_processing/appearance/
page.rs1use super::{atlas_plan::{plan_sampled_appearance, AtlasSampler}, page_atlas, page_raster::{composite, Raster}, source::Source, *};
5#[cfg(test)]
6use super::atlas_plan::encode_png;
7#[cfg(test)]
8use sha2::{Digest, Sha256};
9
10struct PageSampler<'a> { page: Raster<'a>, triangle: Option<usize>, uv: [[f64; 2]; 3] }
11impl AtlasSampler for PageSampler<'_> {
12 fn begin_item(&mut self, _: &mut Source<'_>, _: u32, _: &AppearanceItem, _: &[[f64; 3]], _: &[[u32; 3]], _: &crate::types::mesh::MeshData) -> Result<(), String> {
13 self.triangle = None; Ok(())
14 }
15 fn reserve_pixels(&mut self, _: usize) -> Result<(), String> { Ok(()) }
16 fn sample(&mut self, item: &AppearanceItem, triangle: usize, weights: [f64; 3], _: bool, background: [f64; 4]) -> Result<[u8; 4], String> {
17 if self.triangle != Some(triangle) {
18 let indices = item.tex_coord_index.get(triangle).ok_or("Missing projected triangle")?;
19 for (i, index) in indices.iter().enumerate() {
20 self.uv[i] = *item.tex_coords.get(index.checked_sub(1).ok_or("Zero projected index")? as usize).ok_or("Invalid projected corner")?;
21 }
22 self.triangle = Some(triangle);
23 }
24 Ok(composite(self.page, page_atlas::interpolate(self.uv, weights), background))
25 }
26}
27pub fn plan_page_appearance(bytes: &[u8], request: &PageAppearanceRequest, rgba: &[u8]) -> Result<PageAppearancePlan, String> {
30 let spec = &request.appearance;
31 if spec.product_ids.is_empty() { return Err("Appearance scope must contain 1..10000 products".into()); }
32 if !matches!(spec.mapping, Mapping::Planar { .. }) || spec.repeat_s || spec.repeat_t {
33 return Err("Page appearance requires non-repeating planar mapping".into());
34 }
35 let page = Raster::supplied(&request.page, rgba)?;
36 let mut sampler = PageSampler { page, triangle: None, uv: [[0.; 2]; 3] };
37 plan_sampled_appearance(bytes, spec, &request.source_images, rgba, request.texels_per_metre, &mut sampler)
38}
39
40#[cfg(test)]
41#[path = "page_tests.rs"]
42mod tests;