|
1 | 1 | # Imageflow3 Context Handoff |
2 | 2 |
|
3 | | -## Current State (2026-03-26) |
| 3 | +## State: 141/188 tests passing (47 failures, 4 skipped) |
4 | 4 |
|
5 | | -**Branch**: `imageflow3`, **139/188 passing** (49 divergences between v2 and zen backends). |
| 5 | +Branch `imageflow3`. Both v2 and zen backends run against shared checksums. |
6 | 6 |
|
7 | | -## Test Architecture |
8 | | - |
9 | | -Tests run **both backends** (v2 + zen) against **one shared checksum set**. |
10 | | -V2 runs first and sets the baseline. Zen must match within the test's tolerance. |
11 | | -Divergences are bugs to fix, not baselines to fork. |
| 7 | +## Test commands |
12 | 8 |
|
13 | 9 | ```bash |
14 | | -# Run all tests (both backends) |
15 | | -just test |
| 10 | +just test # both backends, shared checksums |
| 11 | +just test-filter NAME # filter by test name |
| 12 | +ZENPIPE_TRACE=1 just test-filter NAME # with pipeline trace |
| 13 | +``` |
16 | 14 |
|
17 | | -# With auto-accept for new baselines |
18 | | -just test-update |
| 15 | +justfile passes `--features "zen-default,c-codecs"`. |
19 | 16 |
|
20 | | -# justfile passes --features "zen-default,c-codecs" |
21 | | -``` |
| 17 | +## Proven facts (with tests) |
22 | 18 |
|
23 | | -`Context.force_backend` controls which engine runs. `Backend::V2` or `Backend::Zen`. |
24 | | -`backends_to_test()` in test infrastructure returns both when `zen-pipeline` feature is on. |
| 19 | +### JPEG decoder parity (test: `jpeg_decoder_parity.rs`) |
| 20 | +- sRGB JPEG (Canon 5D): **delta=0** between mozjpeg and zenjpeg. Pixel-identical. |
| 21 | +- Rec.2020 PQ JPEG: **delta=122**, 100% pixels differ. Not IDCT — color matrix. |
| 22 | +- Cause unverified. Needs investigation in zenjpeg's YCbCr→RGB path for wide-gamut. |
25 | 23 |
|
26 | | -## Tracing |
| 24 | +### CMS transform parity (test: `moxcms/tests/rgb_vs_rgba_layout.rs`) |
| 25 | +- Layout::Rgb vs Layout::Rgba: **identical** output from moxcms. |
| 26 | +- Direct `new_srgb()` vs ICC-roundtripped sRGB destination: **identical**. |
| 27 | +- CICP vs no-CICP on destination: **identical**. |
| 28 | +- Same ICC bytes (hash verified) go to both v2 and zen CMS paths. |
27 | 29 |
|
28 | | -```bash |
29 | | -ZENPIPE_TRACE=1 just test-filter test_name # text trace to stderr |
30 | | -ZENPIPE_TRACE=svg just test-filter test_name # also write /tmp/zenpipe_trace.svg |
31 | | -``` |
| 30 | +### ICC profile extraction (zenjpeg `tests/icc_extraction.rs`) |
| 31 | +- `extract_icc_profile()` works correctly on all test images. |
| 32 | +- `or_else` fallback fix (commit `0355bf1d`) resolved extras/parser priority. |
| 33 | + |
| 34 | +### Linear matte compositing (zenpixels-convert) |
| 35 | +- `matte_composite()` now blends in linear light using LUT-based sRGB↔linear. |
| 36 | +- All 3 matte compositing tests pass (hardcoded pixel checks match v2). |
| 37 | + |
| 38 | +## 47 failures by category |
| 39 | + |
| 40 | +### Wide-gamut JPEG decoder difference (15 tests) |
| 41 | +Rec.2020, P3, AdobeRGB, ProPhoto JPEGs. Delta=122 before CMS. |
| 42 | +sRGB JPEGs have delta=0 — decoders agree on sRGB, disagree on wide-gamut. |
32 | 43 |
|
33 | | -## 49 Remaining Divergences (by category) |
| 44 | +Tests: `icc_rec2020_decode_{1,2}`, `icc_display_p3_decode_{1,2,3}`, |
| 45 | +`icc_adobe_rgb_decode_{1,2}`, `icc_prophoto_decode`, `icc_gray_gamma22_decode`, |
| 46 | +`icc_repro_{imagemagick,libvips,pillow,sharp}_icc`, `jpeg_icc2_color_profile` |
34 | 47 |
|
35 | | -### ICC color management (18 tests) |
36 | | -All `format=png` no-resize tests. Zen skips the identity resize (correct behavior) |
37 | | -but v2 runs it, causing sRGB→linear→sRGB roundtrip that slightly alters pixels. |
38 | | -The zen output is MORE correct. These need the v2 baselines updated to match zen, |
39 | | -or the tolerance adjusted to accept the roundtrip difference. |
| 48 | +### sRGB JPEG + CMS path difference (4 tests) |
| 49 | +sRGB JPEG decoder output is identical (delta=0), but something in the |
| 50 | +CMS skip/apply path differs. Delta=49-56 in final output. |
40 | 51 |
|
41 | | -Tests: test_icc_srgb_canon_5d, test_icc_srgb_sony_a7rv, test_icc_display_p3_decode_{1,2,3}, |
42 | | -test_icc_adobe_rgb_decode_{1,2}, test_icc_rec2020_decode_{1,2}, test_icc_prophoto_decode, |
43 | | -test_icc_gray_gamma22_decode, test_icc_repro_{imagemagick,libvips,pillow,sharp}_icc, |
44 | | -test_icc_display_p3_resize_filter, test_icc_p3_crop_and_resize, test_icc_p3_to_jpeg_roundtrip, |
45 | | -test_icc_p3_to_webp |
| 52 | +Tests: `icc_srgb_canon_5d`, `icc_srgb_sony_a7rv`, |
| 53 | +`icc_display_p3_resize_filter`, `icc_p3_crop_and_resize` |
46 | 54 |
|
47 | 55 | ### Round corners (9 tests) |
48 | | -Anti-aliasing model differs: v2 uses volumetric_offset=0.56419 with quadrant-based |
49 | | -rendering including circle mode centering. Zen implementation matches the v2 algorithm |
50 | | -for standard corners (similarity 85+) but circle mode on non-square canvases needs |
51 | | -the v2's quadrant offset logic. |
52 | | - |
53 | | -Tests: test_round_corners_{small,large,custom_pixels,custom_percent,excessive_radius, |
54 | | -circle_wide_canvas,circle_tall_canvas,command_string}, test_round_image_corners_transparent |
55 | | - |
56 | | -### Transparent PNG/WebP format handling (7 tests) |
57 | | -Alpha channel handling differences between zen and v2 codec paths. |
58 | | - |
59 | | -Tests: test_transparent_png_to_{png,jpeg,png_rounded_corners,jpeg_constrain}, |
60 | | -test_transparent_webp_to_webp, test_webp_to_webp_quality, test_problematic_png_lossy |
61 | | - |
62 | | -### WebP scaling with alpha (3 tests) |
63 | | -Alpha channel initialization differs for WebP decode → resize path. |
64 | | - |
65 | | -Tests: webp_{lossless,lossy}_alpha_decode_and_scale, webp_lossy_noalpha_decode_and_scale |
66 | | - |
67 | | -### CMYK JPEG decode (2 tests) |
68 | | -Different CMYK→RGB conversion path. |
69 | | - |
70 | | -Tests: decode_cmyk_jpeg, decode_rgb_with_cmyk_profile_jpeg |
71 | | - |
72 | | -### Matte compositing (2 tests) |
73 | | -Hardcoded pixel value checks — zen alpha compositing math differs from v2. |
74 | | - |
75 | | -Tests: test_matte_compositing_{no_double_division,mixed_alpha} |
76 | | - |
77 | | -### Pngquant (2 tests) |
78 | | -Zen encoder doesn't support pngquant-style quantized PNG yet. |
79 | | - |
80 | | -Tests: test_encode_pngquant_{command,fallback_command} |
81 | | - |
82 | | -### Other (6 tests) |
83 | | -- test_jpeg_crop: JPEG IDCT difference |
84 | | -- test_branching_crop_whitespace: DAG mode crop |
85 | | -- test_png_cicp_bt709_transfer_causes_transform: CICP color |
86 | | -- smoke_test_corrupt_jpeg: zen decoder more tolerant |
87 | | -- test_trim_whitespace: whitespace detection threshold |
88 | | - |
89 | | -## Architecture (zen bridge) |
90 | | - |
91 | | -### What lives where |
92 | | -| Crate | Owns | |
93 | | -|-------|------| |
94 | | -| zenresize | resize (forced w×h), constrain (layout-aware) | |
95 | | -| zenlayout | crop, orient, flip, rotate, expand_canvas, constrain, region, smart_crop | |
96 | | -| zenfilters | color filters (saturation, contrast, brightness, etc.) | |
97 | | -| zenblend | RoundedRectMask, blend modes, mask primitives | |
98 | | -| zenpipe | crop_whitespace, fill_rect, remove_alpha, round_corners, pipeline tracing | |
99 | | -| zencodecs | format selection, encode/decode dispatch, mozjpeg preset config | |
100 | | -| zennode | NodeInstance/NodeDef traits, KvPairs, registry | |
101 | | - |
102 | | -### Imageflow zen bridge files |
103 | | -| File | Lines | Purpose | |
104 | | -|------|-------|---------| |
105 | | -| execute.rs | ~1500 | Pipeline orchestration, decode, encode, RIAPI expansion | |
106 | | -| translate.rs | ~580 | v2 Node → zennode NodeInstance (uses zen registry, no custom wrappers) | |
107 | | -| converter.rs | ~216 | NodeConverter for zenfilters, expand_canvas, region | |
108 | | -| preset_map.rs | ~332 | v2 EncoderPreset → zencodecs CodecIntent | |
109 | | -| riapi.rs | ~162 | Dual RIAPI parser (legacy Ir4Expand + zen-native) | |
110 | | -| context_bridge.rs | ~166 | v2 JSON API bridge | |
111 | | -| captured.rs | ~24 | Bitmap capture data | |
| 56 | +V2 uses volumetric_offset=0.56419 + quadrant-based rendering. |
| 57 | +Zen matches standard corners (sim 85+) but circle mode on non-square |
| 58 | +canvases needs v2's quadrant offset logic. Per-corner radii unsupported. |
| 59 | + |
| 60 | +Tests: `round_corners_{small,large,custom_pixels,custom_percent, |
| 61 | +excessive_radius,circle_wide_canvas,circle_tall_canvas,command_string}`, |
| 62 | +`round_image_corners_transparent` |
| 63 | + |
| 64 | +### PNG/WebP encode defaults (6 tests) |
| 65 | +`with_generic_quality()` overrides `with_lossless()` in zenpng. |
| 66 | +WebP lossless hint propagation fixed (preset_map order). |
| 67 | +JPEG matte compositing added to `stream_encode`. |
| 68 | + |
| 69 | +Tests: `transparent_png_to_png_rounded_corners`, `transparent_png_to_jpeg`, |
| 70 | +`transparent_png_to_jpeg_constrain`, `transparent_webp_to_webp` (sim 98.9), |
| 71 | +`matte_transparent_png`, `webp_to_webp_quality` |
| 72 | + |
| 73 | +### WebP alpha / ExpandCanvas (3 tests) |
| 74 | +ExpandCanvas fills with transparent [0,0,0,0] on opaque source. |
| 75 | + |
| 76 | +Tests: `webp_{lossless,lossy}_alpha_decode_and_scale`, |
| 77 | +`webp_lossy_noalpha_decode_and_scale` |
| 78 | + |
| 79 | +### Other (10 tests) |
| 80 | +- `decode_cmyk_jpeg`, `decode_rgb_with_cmyk_profile_jpeg` — CMYK path |
| 81 | +- `jpeg_crop`, `crop_with_preshrink` — JPEG crop/IDCT |
| 82 | +- `problematic_png_lossy` — pngquant palette |
| 83 | +- `pngquant_command`, `pngquant_fallback_command` — pngquant hints |
| 84 | +- `png_cicp_bt709_transfer` — CICP assertion |
| 85 | +- `branching_crop_whitespace` — DAG mode |
| 86 | +- `smoke_test_corrupt_jpeg` — zen decoder more tolerant |
| 87 | +- `icc_p3_to_{jpeg_roundtrip,webp}` — re-encode quality |
| 88 | +- `trim_whitespace` — border detection |
| 89 | +- `rot_90_*`, `jpeg_simple_rot_90` — rotation |
| 90 | + |
| 91 | +## Architecture |
| 92 | + |
| 93 | +### CmsMode (on ExecutionSecurity) |
| 94 | +- `Imageflow2Compat` (default): skip sRGB-like ICC on decode (desc heuristic) |
| 95 | +- `SceneReferred`: strict sRGB detection, preserve wide gamut |
| 96 | + |
| 97 | +### Backend (on Context) |
| 98 | +- `Context.force_backend = Some(Backend::V2 | Backend::Zen)` for runtime selection |
| 99 | +- Tests iterate both backends via `backends_to_test()` |
| 100 | + |
| 101 | +### Zen bridge (imageflow_core/src/zen/) |
| 102 | +- `translate.rs` (580 lines) — v2 Node → zennode via registry (no custom wrappers) |
| 103 | +- `converter.rs` (216 lines) — ZenFilters, ExpandCanvas, Region converters |
| 104 | +- `execute.rs` (~1500 lines) — orchestration, CMS, encode |
| 105 | +- `preset_map.rs` (~340 lines) — v2 presets → CodecIntent |
| 106 | + |
| 107 | +### Zen node ownership |
| 108 | +- zenresize: resize, constrain |
| 109 | +- zenpipe: crop_whitespace, fill_rect, remove_alpha, round_corners |
| 110 | +- zenblend: RoundedRectMask (used by round_corners) |
| 111 | +- zenlayout: crop, orient, flip, rotate, expand_canvas, region |
| 112 | + |
| 113 | +### Patches (Cargo.toml [patch.crates-io]) |
| 114 | +- zenpixels, zenpixels-convert (local) |
| 115 | +- zencodec (local — has SourceColor::is_srgb, icc_profile_is_srgb) |
| 116 | +- zenjpeg (local — has ICC extraction fallback fix) |
| 117 | +- moxcms (local — has PR #152 #153 fixes) |
| 118 | + |
| 119 | +### Pipeline tracing |
| 120 | +`ZENPIPE_TRACE=1|full|svg` — 4-layer trace (RIAPI, Bridge, Graph, Execution). |
| 121 | +Tracer facade: zero-alloc when inactive. |
0 commit comments