diff --git a/Cargo.lock b/Cargo.lock index b5ccc0785..32e48ce33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2241,8 +2241,6 @@ dependencies = [ [[package]] name = "hayro-jbig2" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69374b3668dd45aeb3d3145cda68f2c7b4f223aaa2511e67d076f1c7d741388d" dependencies = [ "fearless_simd", "hayro-ccitt", diff --git a/Cargo.toml b/Cargo.toml index 530252c21..f171e38ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,4 +87,5 @@ codegen-units = 1 hayro-interpret = { path = "vendor/hayro-interpret" } hayro = { path = "vendor/hayro" } hayro-syntax = { path = "vendor/hayro-syntax" } +hayro-jbig2 = { path = "vendor/hayro-jbig2" } lopdf = { path = "vendor/lopdf" } diff --git a/crates/render/src/raster.rs b/crates/render/src/raster.rs index 74a4a3746..ee72306fa 100644 --- a/crates/render/src/raster.rs +++ b/crates/render/src/raster.rs @@ -921,6 +921,57 @@ trailer << /Root 1 0 R >> assert!(ops >= 200 * 4, "every image and its q/cm/Q are read: {ops}"); } + /// From the nightly `cargo xtask fuzz`: a JBIG2 image whose stream declares a page and a + /// generic region of 65535 × 65535 pixels (each side within JBIG2's limit) behind a small + /// /Width and /Height: decoding its 4.3 billion pixels one by one took minutes. Vendored + /// hayro-jbig2 patch: at most 2^28 pixels per bitmap, and 2^26 per symbol dictionary. + #[test] + fn jbig2_regions_of_billions_of_pixels_are_refused() { + // Embedded JBIG2 segments (ISO 14492 §7.2): page information, then an immediate generic + // region, both 65535 × 65535, followed by arithmetic-coded data. + let mut jbig2 = Vec::new(); + let mut segment = |number: u32, kind: u8, data: &[u8]| { + jbig2.extend_from_slice(&number.to_be_bytes()); + jbig2.extend_from_slice(&[kind, 0, 1]); + jbig2.extend_from_slice(&u32::try_from(data.len()).unwrap().to_be_bytes()); + jbig2.extend_from_slice(data); + }; + let side = 65535u32.to_be_bytes(); + let mut page = Vec::new(); + page.extend_from_slice(&side); + page.extend_from_slice(&side); + page.extend_from_slice(&[0; 8]); // resolution + page.extend_from_slice(&[0, 0, 0]); // flags, striping + segment(0, 48, &page); + let mut region = Vec::new(); + region.extend_from_slice(&side); + region.extend_from_slice(&side); + region.extend_from_slice(&[0; 9]); // x, y, combination operator + region.push(0); // arithmetic coding, template 0 + region.extend_from_slice(&[3, 0xff, 0xfd, 0xff, 2, 0xfe, 0xfe, 0xfe]); // AT pixels + region.extend_from_slice(&[0x5a; 64]); + segment(1, 38, ®ion); + let content = b"q 20 0 0 20 5 5 cm /Im1 Do Q 1 0 0 rg 0 0 4 4 re f"; + let mut pdf = b"%PDF-1.7\n1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj\n2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj\n3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 40 40] /Contents 4 0 R /Resources << /XObject << /Im1 5 0 R >> >> >> endobj\n".to_vec(); + pdf.extend_from_slice(format!("4 0 obj << /Length {} >> stream\n", content.len()).as_bytes()); + pdf.extend_from_slice(content); + pdf.extend_from_slice(b"\nendstream endobj\n"); + pdf.extend_from_slice( + format!("5 0 obj << /Type /XObject /Subtype /Image /Width 8 /Height 8 /BitsPerComponent 1 /ColorSpace /DeviceGray /Filter /JBIG2Decode /Length {} >> stream\n", jbig2.len()) + .as_bytes(), + ); + pdf.extend_from_slice(&jbig2); + pdf.extend_from_slice(b"\nendstream endobj\ntrailer << /Root 1 0 R >>\n%%EOF\n"); + let (tx, rx) = std::sync::mpsc::channel(); + std::thread::spawn(move || { + let mut r = PageRenderer::new(Arc::new(pdf), RenderConfig::default()); + let _ = tx.send(r.render(RenderRequest { page: 0, kind: RequestKind::Pixels, tile: None, scale: 1.0, tag: 0 })); + }); + let page = rx.recv_timeout(std::time::Duration::from_secs(20)).expect("a huge JBIG2 region must not stall the renderer"); + assert!(page.error.is_none(), "{:?}", page.error); + assert_eq!(&page.rgba[((38 * 40 + 1) * 4)..][..4], &[255, 0, 0, 255], "the rest of the page draws"); + } + /// From `cargo xtask fuzz`: an inline image claiming /W 4294967295 over four bytes of data /// hung in resampling (vendored hayro patch: `MAX_IMAGE_PIXELS`). #[test] diff --git a/vendor/README.md b/vendor/README.md index a8a848d39..9b295873a 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -10,6 +10,7 @@ the fix. Vendoring copyleft code is never allowed (plan/adr/0001). | hayro-interpret | 0.7.0 | Apache-2.0 OR MIT | (1) select `/AP /N` appearance states via `/AS` (checkboxes, radios, stateful icons) and honour the NoView flag; (2) Type3 Unicode fallback to `/Encoding` glyph names, then printable ASCII; (3) `Type3Glyph::advance_width`; (4) `InterpreterSettings::ocg_overrides` for viewer layer toggles; (5) soft masks whose group lacks `/CS` were dropped (Chrome gradient text); (6) paint nesting cap `MAX_PAINT_NESTING` for tiling patterns and Type 3 glyphs (self-referencing content overflowed the stack); (7) CID width ranges in `/W` and `/W2` clamped to `MAX_CID` (a `0 4294967295 w` range hung); (8) `InterpreterSettings::hide_comments` skips markup annotations but keeps widgets and links (View ▸ Hide all comments); (9) `image_size_ok`: images and masks over 2^28 pixels are not decoded (a fuzzed `/W 4294967295` stencil mask allocated 4 GiB); (10) `MAX_NESTED_PAINTS`: nested paints (forms, Type 3 glyphs and tiling patterns inside one another) share a per-page budget of 50,000 (a glyph showing eight glyphs of its own font, sixteen deep, hung) | (1) `raster::tests::widget_appearance_states`; (2, 3) `xtask text-oracle` on corpus/pdfjs issue918; (4) `raster::tests::layer_override_hides_content`; (5) `raster::tests::soft_mask_without_group_colour_space_is_applied`; (6) `raster::tests::self_referencing_tiling_pattern_terminates`, `raster::tests::self_referencing_type3_glyph_terminates`; (7) `raster::tests::huge_cid_width_ranges_terminate`; (8) `raster::tests::hiding_comments_keeps_fields`; (9) `raster::tests::absurd_mask_and_fax_sizes_are_refused_before_decoding`; (10) `raster::tests::type3_glyphs_that_fan_out_into_themselves_finish` | | hayro | 0.7.1 | Apache-2.0 OR MIT | (1) `RenderSettings::{x_offset, y_offset}` to render one tile of a large page; (2) images over `MAX_IMAGE_PIXELS` (2^28) are skipped (a fuzzed `/W 4294967295` hung in resampling); (3) `tiling_cell_scale` keeps a tiling pattern's cell pixmap within 3000 px a side whatever its `/XStep`/`/YStep` (a fuzzed step far beyond `/BBox` allocated 2 GB); (4) stroke widths clamped to 4 × (canvas width + height) device pixels (a fuzzed `/LW 9223372036854775807` allocated 10 GB in stroke expansion) | (1) `raster::tests::tiles_match_full_render`; (2) `raster::tests::absurd_image_dimensions_are_skipped`; (3) `raster::tests::tiling_cells_stay_small_whatever_the_step`; (4) `raster::tests::absurd_predictor_columns_and_line_widths_render` | +| hayro-jbig2 | 0.3.0 | Apache-2.0 OR MIT | (1) at most 2^28 pixels per bitmap, page bitmap, halftone grid and grayscale image (each side could be 65535: 4.3 billion pixels decoded one by one); (2) at most 2^26 symbol pixels per symbol dictionary (a fuzzed dictionary decoded for minutes) | (1) `raster::tests::jbig2_regions_of_billions_of_pixels_are_refused`; (2) the nightly `cargo xtask fuzz` finding, which now finishes in about a second | | lopdf | 0.45.0 | MIT | (1) PNG predictor rows longer than the data are refused before their buffers are allocated (a fuzzed `/Columns 4294967295` allocated two 4 GiB rows); (2) predictor row widths use saturating arithmetic | (1) `inspect::tests::png_predictor_rows_longer_than_the_data_are_refused_before_allocating` | | hayro-syntax | 0.7.2 | Apache-2.0 OR MIT | (1) Page-tree cycle guard in `resolve_pages` (visited set + depth cap): a `/Kids` loop among compressed objects overflowed the stack and aborted the process; (2) `ccitt_size_ok`: CCITT images wider than 2^20 columns or over 2^28 pixels are not decoded (a fuzzed `/Columns 4294967295` allocated 4 GiB); (3) Flate/LZW predictor row widths are saturating `usize`, a zero width is refused and a row longer than the data is not allocated (a fuzzed `/Columns 9223372036854775807` wrapped to a 2^61-byte allocation and aborted); (4) the inline-image end-of-data check (`EI`) doesn't nest inside its own look-ahead and examines at most 256 candidates and steps (50 images with "EI" in their data ran for over ten minutes) | (1) `cos/tests/writer.rs::page_tree_loops_in_object_streams_do_not_crash_readers`; (2) `raster::tests::absurd_mask_and_fax_sizes_are_refused_before_decoding`; (3) `raster::tests::absurd_predictor_columns_and_line_widths_render`; (4) `raster::tests::inline_images_with_ei_in_their_data_parse_in_linear_time` | diff --git a/vendor/hayro-jbig2/.gitignore b/vendor/hayro-jbig2/.gitignore new file mode 100644 index 000000000..c9c20c270 --- /dev/null +++ b/vendor/hayro-jbig2/.gitignore @@ -0,0 +1,2 @@ +test-inputs/ +snapshots/ \ No newline at end of file diff --git a/vendor/hayro-jbig2/Cargo.lock b/vendor/hayro-jbig2/Cargo.lock new file mode 100644 index 000000000..058a72899 --- /dev/null +++ b/vendor/hayro-jbig2/Cargo.lock @@ -0,0 +1,824 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fearless_simd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76258897e51fd156ee03b6246ea53f3e0eb395d0b327e9961c4fc4c8b2fa151a" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hayro-ccitt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f4d0e94ddd48749f06bbe4e5389fb9799a0c45bcaf00495042076ef05e3241a" + +[[package]] +name = "hayro-jbig2" +version = "0.3.0" +dependencies = [ + "criterion", + "fearless_simd", + "hayro-ccitt", + "image", + "indicatif", + "rayon", + "serde", + "serde_json", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png", +] + +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "rayon", + "unicode-width", + "web-time", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.184" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/vendor/hayro-jbig2/Cargo.toml b/vendor/hayro-jbig2/Cargo.toml new file mode 100644 index 000000000..1efdc40b0 --- /dev/null +++ b/vendor/hayro-jbig2/Cargo.toml @@ -0,0 +1,129 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +name = "hayro-jbig2" +version = "0.3.0" +authors = ["Laurenz Stampfl "] +build = false +exclude = [ + "/tests/", + "/manifest_*.json", + "/sync.py", + "/generate_huffman_tables.py", + "/benches/", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A memory-safe, pure-Rust JBIG2 decoder." +readme = "README.md" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/LaurenzV/hayro" +resolver = "2" + +[features] +default = [ + "std", + "image", + "simd", +] +image = [ + "dep:image", + "std", +] +simd = [ + "fearless_simd", + "std", +] +std = [] + +[lib] +name = "hayro_jbig2" +path = "src/lib.rs" + +[[example]] +name = "jbig2png" +path = "examples/jbig2png.rs" + +[dependencies.fearless_simd] +version = "0.4.0" +optional = true + +[dependencies.hayro-ccitt] +version = "0.3.0" +default-features = false + +[dependencies.image] +version = "0.25" +optional = true +default-features = false + +[dev-dependencies.criterion] +version = "0.5" +features = ["html_reports"] + +[dev-dependencies.image] +version = "0.25" +features = ["png"] +default-features = false + +[dev-dependencies.indicatif] +version = "0.17" +features = ["rayon"] + +[dev-dependencies.rayon] +version = "1.11" + +[dev-dependencies.serde] +version = "1" +features = ["derive"] + +[dev-dependencies.serde_json] +version = "1" + +[lints.clippy] +collection_is_never_read = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +default_trait_access = "warn" +doc_markdown = "warn" +fn_to_numeric_cast_any = "warn" +infinite_loop = "warn" +large_stack_arrays = "warn" +mismatching_type_param_order = "warn" +negative_feature_names = "warn" +redundant_feature_names = "warn" +same_functions_in_if_condition = "warn" +semicolon_if_nothing_returned = "warn" +todo = "warn" +too_many_arguments = "allow" +unseparated_literal_suffix = "warn" +use_self = "warn" +wildcard_dependencies = "warn" + +[lints.rust] +elided_lifetimes_in_paths = "warn" +keyword_idents_2024 = "forbid" +missing_docs = "warn" +non_ascii_idents = "forbid" +non_local_definitions = "forbid" +trivial_numeric_casts = "warn" +unnameable_types = "warn" +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "forbid" +unused_import_braces = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" diff --git a/vendor/hayro-jbig2/Cargo.toml.orig b/vendor/hayro-jbig2/Cargo.toml.orig new file mode 100644 index 000000000..a264dbd01 --- /dev/null +++ b/vendor/hayro-jbig2/Cargo.toml.orig @@ -0,0 +1,41 @@ +[package] +name = "hayro-jbig2" +version = "0.3.0" +description = "A memory-safe, pure-Rust JBIG2 decoder." +authors = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } +readme = "README.md" +exclude = ["/tests/", "/manifest_*.json", "/sync.py", "/generate_huffman_tables.py", "/benches/"] + +[features] +default = ["std", "image", "simd"] +std = [] +simd = ["fearless_simd", "std"] +image = ["dep:image", "std"] + +[dependencies] +fearless_simd = { workspace = true, optional = true } +hayro-ccitt = { workspace = true } +image = { workspace = true, default-features = false, optional = true } + +[lints] +workspace = true + +[dev-dependencies] +criterion = { version = "0.5", features = ["html_reports"] } +image = { workspace = true, default-features = false, features = ["png"] } +indicatif = { version = "0.17", features = ["rayon"] } +rayon = "1.11" +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +[[test]] +name = "asset_suite" +path = "tests/mod.rs" +harness = false + +[[bench]] +name = "decode" +harness = false diff --git a/vendor/hayro-jbig2/LICENSE-APACHE b/vendor/hayro-jbig2/LICENSE-APACHE new file mode 100644 index 000000000..4947287f7 --- /dev/null +++ b/vendor/hayro-jbig2/LICENSE-APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/vendor/hayro-jbig2/LICENSE-MIT b/vendor/hayro-jbig2/LICENSE-MIT new file mode 100644 index 000000000..ae3742de4 --- /dev/null +++ b/vendor/hayro-jbig2/LICENSE-MIT @@ -0,0 +1,7 @@ +Copyright (c) The Hayro Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/hayro-jbig2/README.md b/vendor/hayro-jbig2/README.md new file mode 100644 index 000000000..096340142 --- /dev/null +++ b/vendor/hayro-jbig2/README.md @@ -0,0 +1,30 @@ +# hayro-jbig2 + +[![Crates.io](https://img.shields.io/crates/v/hayro-jbig2.svg)](https://crates.io/crates/hayro-jbig2) +[![Documentation](https://docs.rs/hayro-jbig2/badge.svg)](https://docs.rs/hayro-jbig2) + +A memory-safe, pure-Rust JBIG2 decoder. + +`hayro-jbig2` decodes JBIG2 images as specified in ITU-T T.88 (also known as +ISO/IEC 14492). JBIG2 is a bi-level image compression standard commonly used +in PDF documents for compressing scanned text documents. + +## Status + +This crate is currently under development. + +## Safety +By default, the crate has the `simd` feature enabled, which uses the +[`fearless_simd`](https://github.com/linebender/fearless_simd) crate to accelerate decoding of JBIG2 images with halftoning. +If you want to eliminate any usage of unsafe in this crate as well as its dependencies, you can simply disable this +feature and still get very good performance in the vast majority of cases. Unsafe code is forbidden +via a crate-level attribute. + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/vendor/hayro-jbig2/examples/jbig2png.rs b/vendor/hayro-jbig2/examples/jbig2png.rs new file mode 100644 index 000000000..5ea652626 --- /dev/null +++ b/vendor/hayro-jbig2/examples/jbig2png.rs @@ -0,0 +1,64 @@ +//! This example shows you how to convert a JBIG2 image into a PNG file. + +#![allow(missing_docs)] + +use std::process::ExitCode; + +use image::{GrayImage, ImageDecoder}; + +fn main() -> ExitCode { + let args: Vec = std::env::args().collect(); + + if args.len() != 3 { + eprintln!("Usage: {} ", args[0]); + + return ExitCode::FAILURE; + } + + let input_path = &args[1]; + let output_path = &args[2]; + + let data = match std::fs::read(input_path) { + Ok(data) => data, + Err(err) => { + eprintln!("Failed to read input file: {err}"); + + return ExitCode::FAILURE; + } + }; + + let image = match hayro_jbig2::Image::new(&data) { + Ok(image) => image, + Err(err) => { + eprintln!("Failed to parse JBIG2: {err}"); + + return ExitCode::FAILURE; + } + }; + + let (width, height) = image.dimensions(); + println!("Decoded: {width}x{height} image"); + + let mut buf = vec![0_u8; image.total_bytes() as usize]; + if let Err(err) = image.read_image(&mut buf) { + eprintln!("Failed to decode JBIG2: {err}"); + + return ExitCode::FAILURE; + } + + let Some(gray) = GrayImage::from_raw(width, height, buf) else { + eprintln!("Internal error: Buffer size mismatch"); + + return ExitCode::FAILURE; + }; + + if let Err(err) = gray.save(output_path) { + eprintln!("Failed to save PNG: {err}"); + + return ExitCode::FAILURE; + } + + eprintln!("Saved: {output_path}"); + + ExitCode::SUCCESS +} diff --git a/vendor/hayro-jbig2/src/arithmetic_decoder.rs b/vendor/hayro-jbig2/src/arithmetic_decoder.rs new file mode 100644 index 000000000..8e6adfb5a --- /dev/null +++ b/vendor/hayro-jbig2/src/arithmetic_decoder.rs @@ -0,0 +1,398 @@ +// Note: This is a copy of the arithmetic decoder from `hayro-jpeg2000`, +// keep the two in sync. + +//! The arithmetic decoder, described in Annex C. +//! +//! The arithmetic decoder keeps track of some state and continuously receives +//! context labels as input, each time yielding a new bit from the original data +//! as output. + +pub(crate) struct ArithmeticDecoder<'a> { + /// The underlying encoded data. + data: &'a [u8], + /// The C-register (see Table C.1). + c: u32, + /// The A-register (see Table C.1). + a: u32, + /// The pointer to the current byte. + base_pointer: u32, + /// The bit shift counter. + shift_count: u32, +} + +impl<'a> ArithmeticDecoder<'a> { + pub(crate) fn new(data: &'a [u8]) -> Self { + let mut decoder = ArithmeticDecoder { + data, + c: 0, + a: 0, + base_pointer: 0, + shift_count: 0, + }; + + decoder.initialize(); + + decoder + } + + /// Read the next bit using the given context label. + #[inline(always)] + pub(crate) fn read_bit(&mut self, context: &mut ArithmeticDecoderContext) -> u32 { + self.decode(context) + } + + /// The INITDEC procedure from C.3.5. + /// + /// We use the version from Annex G in . + fn initialize(&mut self) { + self.c = ((self.current_byte() as u32) ^ 0xff) << 16; + self.read_byte(); + + self.c <<= 7; + self.shift_count -= 7; + self.a = 0x8000; + } + + /// The BYTEIN procedure from C.3.4. + /// + /// We use the version from Annex G from . + #[inline(always)] + fn read_byte(&mut self) { + if self.current_byte() == 0xff { + let b1 = self.next_byte(); + + if b1 > 0x8f { + self.shift_count = 8; + } else { + self.base_pointer += 1; + self.c = self + .c + .wrapping_add(0xfe00) + .wrapping_sub((self.current_byte() as u32) << 9); + self.shift_count = 7; + } + } else { + self.base_pointer += 1; + self.c = self + .c + .wrapping_add(0xff00) + .wrapping_sub((self.current_byte() as u32) << 8); + self.shift_count = 8; + } + } + + /// The RENORMD procedure from C.3.3. + #[inline(always)] + fn renormalize(&mut self) { + // Original code: + // loop { + // if self.shift_count == 0 { + // self.read_byte(); + // } + // + // self.a <<= 1; + // self.c <<= 1; + // self.shift_count -= 1; + // + // if self.a & 0x8000 != 0 { + // break; + // } + // } + + // Optimization: Batch shifts. + while self.a & 0x8000 == 0 { + if self.shift_count == 0 { + self.read_byte(); + } + + let shifts_needed = self.a.leading_zeros() - 16; + let batch = shifts_needed.min(self.shift_count); + self.a <<= batch; + self.c <<= batch; + self.shift_count -= batch; + } + } + + /// The `LPS_EXCHANGE` procedure from C.3.2. + #[inline(always)] + #[allow(unused, reason = "for reference")] + fn exchange_lps(&mut self, context: &mut ArithmeticDecoderContext, qe_entry: &QeData) -> u32 { + // Original code: + // let d; + // + // if self.a < qe_entry.qe { + // self.a = qe_entry.qe; + // d = context.mps; + // context.index = qe_entry.nmps; + // } else { + // self.a = qe_entry.qe; + // d = 1 - context.mps; + // + // if qe_entry.switch { + // context.mps = 1 - context.mps; + // } + // + // context.index = qe_entry.nlps; + // } + + // Branchless version, shows better performance. + + let cond = (self.a < qe_entry.qe) as u32; + let inv_cond = 1 - cond; + + self.a = qe_entry.qe; + // d = if cond { mps } else { 1 - mps } + let d = context.mps() ^ inv_cond; + // flip mps only when !cond && switch + context.xor_mps(inv_cond & (qe_entry.switch as u32)); + // index = if cond { nmps } else { nlps } + let cond_u8 = cond as u8; + let inv_cond_u8 = inv_cond as u8; + context.set_index(cond_u8 * qe_entry.nmps + inv_cond_u8 * qe_entry.nlps); + + d + } + + /// The `MPS_EXCHANGE` procedure from C.3.2. + #[inline(always)] + #[allow(unused, reason = "for reference")] + fn exchange_mps(&mut self, context: &mut ArithmeticDecoderContext, qe_entry: &QeData) -> u32 { + // Original code: + // let d; + // + // if self.a < qe_entry.qe { + // d = 1 - context.mps; + // + // if qe_entry.switch { + // context.mps = 1 - context.mps; + // } + // + // context.index = qe_entry.nlps; + // } else { + // d = context.mps; + // context.index = qe_entry.nmps; + // } + + // Branchless version, shows better performance. + let cond = (self.a < qe_entry.qe) as u32; + let inv_cond = 1 - cond; + // d = if cond { 1 - mps } else { mps } + let d = context.mps() ^ cond; + // flip mps only when cond && switch + context.xor_mps(cond & (qe_entry.switch as u32)); + // index = if cond { nlps } else { nmps } + let cond_u8 = cond as u8; + let inv_cond_u8 = inv_cond as u8; + context.set_index(cond_u8 * qe_entry.nlps + inv_cond_u8 * qe_entry.nmps); + d + } + + /// The DECODE procedure from C.3.2. + /// + /// We use the version from Annex G from . + #[inline(always)] + pub(crate) fn decode(&mut self, context: &mut ArithmeticDecoderContext) -> u32 { + let qe_entry = &QE_TABLE[context.index() as usize]; + + self.a -= qe_entry.qe; + + // Old code: + // let d; + // + // if (self.c >> 16) < self.a { + // if self.a & 0x8000 == 0 { + // d = self.exchange_mps(context, qe_entry); + // self.renormalize(); + // } else { + // d = context.mps(); + // } + // } else { + // self.c -= self.a << 16; + // + // d = self.exchange_lps(context, qe_entry); + // self.renormalize(); + // } + // + // d + + // This is a faster version that reduces branching, which has shown + // itself to be the main limiting factor for better performance. + // We short-circuit the case where just the most probably symbol is + // returned, and otherwise use a code path that works for both, + // MPS_EXCHANGE and LPS_EXCHANGE. + + if (self.c >> 16) < self.a && self.a & 0x8000 != 0 { + return context.mps(); + } + + // Unified branchless MPS_EXCHANGE / LPS_EXCHANGE. + // + // Compare with exchange_mps and exchange_lps above — the only + // difference between them is that LPS flips the role of cond: + // exchange_mps: d = mps ^ cond, flip when cond, index = cond*nlps + inv*nmps + // exchange_lps: d = mps ^ inv_cond, flip when inv_cond, index = cond*nmps + inv*nlps + // + // This is equivalent to XOR-ing cond with is_lps, so we can handle + // both paths with a single branchless computation. + // + // As can be seen above, renormalization is always performed. + let is_lps = ((self.c >> 16) >= self.a) as u32; + + // LPS: C -= A << 16 (no-op when MPS). + let lps_mask = is_lps.wrapping_neg(); // 0xFFFF_FFFF if LPS, 0 if MPS + self.c -= (self.a << 16) & lps_mask; + + // Same condition as in exchange_mps / exchange_lps. + let cond = (self.a < qe_entry.qe) as u32; + + // LPS: a = qe (no-op when MPS, a stays as a - qe). + self.a = (self.a & !lps_mask) | (qe_entry.qe & lps_mask); + + // exchange_mps: d = mps ^ cond → cond ^ 0 + // exchange_lps: d = mps ^ inv_cond → cond ^ 1 + // unified: d = mps ^ (cond ^ is_lps) + let d = context.mps() ^ cond ^ is_lps; + + // exchange_mps: flip mps when cond & switch → (cond ^ 0) & switch + // exchange_lps: flip mps when inv_cond & switch → (cond ^ 1) & switch + // unified: flip mps when (cond ^ is_lps) & switch + context.xor_mps((cond ^ is_lps) & (qe_entry.switch as u32)); + + // exchange_mps: index = cond * nlps + inv_cond * nmps + // exchange_lps: index = cond * nmps + inv_cond * nlps (swapped) + // unified: the result is always exactly nmps or nlps — + // pick nlps when (cond ^ is_lps) == 1, nmps otherwise. + let pick_nlps = ((cond ^ is_lps) as u8).wrapping_neg(); // 0xFF or 0x00 + context.set_index(qe_entry.nmps ^ ((qe_entry.nmps ^ qe_entry.nlps) & pick_nlps)); + + self.renormalize(); + + d + } + + #[inline(always)] + fn current_byte(&self) -> u8 { + self.data + .get(self.base_pointer as usize) + .copied() + // "The number of bytes corresponding to the coding passes is + // specified in the packet header. Often at that point there are + // more symbols to be decoded. Therefore, the decoder shall extend + // the input bit stream to the arithmetic coder with 0xFF bytes, + // as necessary, until all symbols have been decoded." + .unwrap_or(0xFF) + } + + #[inline(always)] + fn next_byte(&self) -> u8 { + self.data + .get((self.base_pointer + 1) as usize) + .copied() + .unwrap_or(0xFF) + } +} + +// Previously, we stored the context as 2 u32's, but doing it with a bit-packed +// u8 seems to be slightly better (though it doesn't make that huge of a +// difference). +/// Bits 0-6 = index (0-46). +/// Bit 7 = mps (0 or 1). +#[derive(Copy, Clone, Debug, Default)] +pub(crate) struct ArithmeticDecoderContext(u8); + +impl ArithmeticDecoderContext { + #[inline(always)] + pub(crate) fn index(self) -> u32 { + (self.0 & 0x7F) as u32 + } + + #[inline(always)] + pub(crate) fn mps(self) -> u32 { + (self.0 >> 7) as u32 + } + + #[inline(always)] + fn set_index(&mut self, index: u8) { + self.0 = (self.0 & 0x80) | index; + } + + #[inline(always)] + fn xor_mps(&mut self, val: u32) { + self.0 ^= ((val & 1) << 7) as u8; + } +} + +#[derive(Debug, Clone, Copy)] +struct QeData { + qe: u32, + nmps: u8, + nlps: u8, + switch: bool, +} + +macro_rules! qe { + ($($qe:expr, $nmps:expr, $nlps:expr, $switch:expr),+ $(,)?) => { + [ + $( + QeData { + qe: $qe, + nmps: $nmps, + nlps: $nlps, + switch: $switch, + } + ),+ + ] + }; +} + +/// QE values and associated data from Table C.2. +#[rustfmt::skip] +static QE_TABLE: [QeData; 47] = qe!( + 0x5601, 1, 1, true, + 0x3401, 2, 6, false, + 0x1801, 3, 9, false, + 0x0AC1, 4, 12, false, + 0x0521, 5, 29, false, + 0x0221, 38, 33, false, + 0x5601, 7, 6, true, + 0x5401, 8, 14, false, + 0x4801, 9, 14, false, + 0x3801, 10, 14, false, + 0x3001, 11, 17, false, + 0x2401, 12, 18, false, + 0x1C01, 13, 20, false, + 0x1601, 29, 21, false, + 0x5601, 15, 14, true, + 0x5401, 16, 14, false, + 0x5101, 17, 15, false, + 0x4801, 18, 16, false, + 0x3801, 19, 17, false, + 0x3401, 20, 18, false, + 0x3001, 21, 19, false, + 0x2801, 22, 19, false, + 0x2401, 23, 20, false, + 0x2201, 24, 21, false, + 0x1C01, 25, 22, false, + 0x1801, 26, 23, false, + 0x1601, 27, 24, false, + 0x1401, 28, 25, false, + 0x1201, 29, 26, false, + 0x1101, 30, 27, false, + 0x0AC1, 31, 28, false, + 0x09C1, 32, 29, false, + 0x08A1, 33, 30, false, + 0x0521, 34, 31, false, + 0x0441, 35, 32, false, + 0x02A1, 36, 33, false, + 0x0221, 37, 34, false, + 0x0141, 38, 35, false, + 0x0111, 39, 36, false, + 0x0085, 40, 37, false, + 0x0049, 41, 38, false, + 0x0025, 42, 39, false, + 0x0015, 43, 40, false, + 0x0009, 44, 41, false, + 0x0005, 45, 42, false, + 0x0001, 45, 43, false, + 0x5601, 46, 46, false, +); diff --git a/vendor/hayro-jbig2/src/bitmap.rs b/vendor/hayro-jbig2/src/bitmap.rs new file mode 100644 index 000000000..7643860c0 --- /dev/null +++ b/vendor/hayro-jbig2/src/bitmap.rs @@ -0,0 +1,227 @@ +//! Bitmap representation for JBIG2 decoding. +//! +//! "The variable whose value is the result of this decoding procedure is shown +//! in Table 3." (6.2.3) +//! +//! "GBREG - The decoded region bitmap." (Table 3) + +use crate::decode::CombinationOperator; +use crate::error::{OverflowError, Result, bail}; +use alloc::vec; +use alloc::vec::Vec; + +// To guard better against malicious files, we limit bitmap dimensions +// to `u16::MAX`, which should be more than enough. +pub(crate) const MAX_DIMENSION: u32 = u16::MAX as u32; + +/// PrintCraft patch: the most pixels one bitmap may have (2^28, the renderer's +/// `MAX_IMAGE_PIXELS`). Each side alone may be 65535, so a single region could be 4.3 billion +/// pixels, decoded one by one. +pub(crate) const MAX_PIXELS: u64 = 1 << 28; + +/// The underlying word type for packed pixel storage. +/// Change this to `u32` to switch back to 32-bit words. +pub(crate) type Word = u32; + +pub(crate) const WORD_BITS: u32 = Word::BITS; +pub(crate) const WORD_SHIFT: u32 = WORD_BITS - 1; + +/// A decoded bitmap with position information. +/// +/// "Pixels decoded by the MMR decoder having the value 'black' shall be treated +/// as having the value 1. Pixels decoded by the MMR decoder having the value +/// 'white' shall be treated as having the value 0." (6.2.6) +#[derive(Debug, Clone, Default)] +pub(crate) struct Bitmap { + /// Width in pixels. + pub(crate) width: u32, + /// Height in pixels. + pub(crate) height: u32, + /// Number of words per row. + pub(crate) stride: u32, + /// Packed pixel data, one bit per pixel, row-major order. + /// Each row is padded to a word boundary. + pub(crate) data: Vec, + /// "This four-byte field gives the horizontal offset in pixels of the bitmap + /// encoded in this segment relative to the page bitmap." (7.4.1.3) + pub(crate) x_location: u32, + /// "This four-byte field gives the vertical offset in pixels of the bitmap + /// encoded in this segment relative to the page bitmap." (7.4.1.4) + pub(crate) y_location: u32, +} + +impl Bitmap { + /// Create a new bitmap filled with white pixels (false). + /// + /// The bitmap is positioned at (0, 0). + pub(crate) fn new(width: u32, height: u32) -> Result { + Self::new_with(width, height, 0, 0, false) + } + + /// Create a new bitmap with full configuration. + pub(crate) fn new_with( + width: u32, + height: u32, + x_location: u32, + y_location: u32, + default_pixel: bool, + ) -> Result { + if width > MAX_DIMENSION || height > MAX_DIMENSION { + bail!(OverflowError::BitmapDimension); + } + // PrintCraft patch: see `MAX_PIXELS`. + if u64::from(width) * u64::from(height) > MAX_PIXELS { + bail!(OverflowError::BitmapDimension); + } + + let stride = width.div_ceil(WORD_BITS); + let default_word: Word = if default_pixel { !0 } else { 0 }; + let data = vec![default_word; stride as usize * height as usize]; + Ok(Self { + width, + height, + stride, + data, + x_location, + y_location, + }) + } + + pub(crate) fn reinitialize( + &mut self, + width: u32, + height: u32, + default_pixel: bool, + ) -> Result<()> { + if width > MAX_DIMENSION || height > MAX_DIMENSION { + return Err(OverflowError::BitmapDimension.into()); + } + // PrintCraft patch: see `MAX_PIXELS` (the page bitmap is sized here). + if u64::from(width) * u64::from(height) > MAX_PIXELS { + return Err(OverflowError::BitmapDimension.into()); + } + + let stride = width.div_ceil(WORD_BITS); + let default_word: Word = if default_pixel { !0 } else { 0 }; + self.width = width; + self.height = height; + self.stride = stride; + self.x_location = 0; + self.y_location = 0; + self.data.clear(); + self.data + .resize(stride as usize * height as usize, default_word); + + Ok(()) + } + + /// Get a pixel value at (x, y). + #[inline] + pub(crate) fn get_pixel(&self, x: u32, y: u32) -> u8 { + if x >= self.width || y >= self.height { + return 0; + } + + let word_idx = (y * self.stride + x / WORD_BITS) as usize; + let bit_pos = WORD_SHIFT - (x % WORD_BITS); + ((self.data[word_idx] >> bit_pos) & 1) as u8 + } + + /// Set a pixel value at (x, y). + #[inline] + pub(crate) fn set_pixel(&mut self, x: u32, y: u32, value: u8) { + if x >= self.width || y >= self.height { + return; + } + + let word_idx = (y * self.stride + x / WORD_BITS) as usize; + let bit_pos = WORD_SHIFT - (x % WORD_BITS); + self.data[word_idx] |= (value as Word) << bit_pos; + } + + /// Combine another bitmap into this one at a specific location. + /// + /// "These operators describe how the segment's bitmap is to be combined with + /// the page bitmap." (7.4.1.5) + /// + /// Pixels outside the destination bitmap are ignored. + pub(crate) fn combine(&mut self, other: &Self, x: i32, y: i32, operator: CombinationOperator) { + // TODO: Figure out how we can optimize this, especially since this is a + // bottleneck for symbols/patterns. + let dest_x_start = x.max(0); + let dest_x_end = (x + other.width as i32).min(self.width as i32); + + if dest_x_start >= dest_x_end { + return; + } + + let src_x_start = (dest_x_start - x) as u32; + let dest_x_start = dest_x_start as u32; + let dest_x_end = dest_x_end as u32; + + for src_y in 0..other.height { + let dest_y = y + src_y as i32; + if dest_y < 0 || dest_y >= self.height as i32 { + continue; + } + + let dest_y = dest_y as u32; + + let first_word = dest_x_start / WORD_BITS; + let last_word = (dest_x_end - 1) / WORD_BITS; + + for word_idx in first_word..=last_word { + let word_start_x = word_idx * WORD_BITS; + let word_end_x = word_start_x + WORD_BITS; + + let px_start = dest_x_start.max(word_start_x); + let px_end = dest_x_end.min(word_end_x); + + let bit_start = px_start - word_start_x; + let bit_end = px_end - word_start_x; + + let mask: Word = if bit_end == WORD_BITS { + !0 >> bit_start + } else { + (!0 >> bit_start) & !(!0 >> bit_end) + }; + + let src_x_for_range = src_x_start + (px_start - dest_x_start); + let src_word_idx = src_x_for_range / WORD_BITS; + let src_bit_offset = src_x_for_range % WORD_BITS; + + let src_word1 = other.get_word(src_y, src_word_idx); + let src_word2 = other.get_word(src_y, src_word_idx + 1); + + let src_raw = if src_bit_offset == 0 { + src_word1 + } else { + (src_word1 << src_bit_offset) | (src_word2 >> (WORD_BITS - src_bit_offset)) + }; + let src_aligned = src_raw >> bit_start; + + let dest_idx = (dest_y * self.stride + word_idx) as usize; + let dest_word = self.data[dest_idx]; + + let result = match operator { + CombinationOperator::Or => dest_word | src_aligned, + CombinationOperator::And => dest_word & src_aligned, + CombinationOperator::Xor => dest_word ^ src_aligned, + CombinationOperator::Xnor => !(dest_word ^ src_aligned), + CombinationOperator::Replace => src_aligned, + }; + + self.data[dest_idx] = (dest_word & !mask) | (result & mask); + } + } + } + + #[inline] + pub(crate) fn get_word(&self, row: u32, word_idx: u32) -> Word { + if row >= self.height || word_idx >= self.stride { + return 0; + } + + self.data[(row * self.stride + word_idx) as usize] + } +} diff --git a/vendor/hayro-jbig2/src/decode/generic.rs b/vendor/hayro-jbig2/src/decode/generic.rs new file mode 100644 index 000000000..a788163f2 --- /dev/null +++ b/vendor/hayro-jbig2/src/decode/generic.rs @@ -0,0 +1,792 @@ +//! Generic region segment parsing and decoding (7.4.6, 6.2). + +use super::{ + AdaptiveTemplatePixel, RegionBitmap, RegionSegmentInfo, Template, parse_region_segment_info, +}; +use crate::ScratchBuffers; +use crate::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext}; +use crate::bitmap::{Bitmap, WORD_BITS, WORD_SHIFT, Word}; +use crate::error::{ParseError, RegionError, Result, TemplateError, bail}; +use crate::reader::Reader; + +/// Generic region decoding procedure (6.2). +pub(crate) fn decode( + header: &GenericRegionHeader<'_>, + ctx: &mut ScratchBuffers, +) -> Result { + let mut bitmap = Bitmap::new_with( + header.region_info.width, + header.region_info.height, + header.region_info.x_location, + header.region_info.y_location, + false, + )?; + + decode_into(header, &mut bitmap, ctx)?; + + Ok(RegionBitmap { + bitmap, + combination_operator: header.region_info.combination_operator, + }) +} + +pub(crate) fn decode_into( + header: &GenericRegionHeader<'_>, + bitmap: &mut Bitmap, + ctx: &mut ScratchBuffers, +) -> Result<()> { + let data = header.data; + + if header.mmr { + // "6.2.6 Decoding using MMR coding" + let _ = decode_bitmap_mmr(bitmap, data)?; + } else { + let mut decoder = ArithmeticDecoder::new(data); + ctx.contexts.clear(); + ctx.contexts.resize( + 1 << header.template.context_bits(), + ArithmeticDecoderContext::default(), + ); + + // "6.2.5 Decoding using a template and arithmetic coding" + decode_bitmap_arithmetic_coding( + bitmap, + &mut decoder, + &mut ctx.contexts, + header.template, + header.tpgdon, + &header.adaptive_template_pixels, + )?; + } + + Ok(()) +} + +/// Parsed generic region segment header (7.4.6.1). +#[derive(Debug, Clone)] +pub(crate) struct GenericRegionHeader<'a> { + pub(crate) region_info: RegionSegmentInfo, + pub(crate) mmr: bool, + pub(crate) template: Template, + pub(crate) tpgdon: bool, + pub(crate) adaptive_template_pixels: [AdaptiveTemplatePixel; 4], + pub(crate) data: &'a [u8], +} + +/// Parse a generic region segment header (7.4.6.1). +pub(crate) fn parse<'a>( + reader: &mut Reader<'a>, + had_unknown_length: bool, +) -> Result> { + let mut region_info = parse_region_segment_info(reader)?; + let flags = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + let mmr = flags & 0x01 != 0; + let template = Template::from_byte(flags >> 1); + let tpgdon = flags & 0x08 != 0; + let ext_template = flags & 0x10 != 0; + let adaptive_template_pixels = if mmr { + [AdaptiveTemplatePixel::default(); 4] + } else { + parse_adaptive_template_pixels(reader, template, ext_template)? + }; + let mut data = reader.tail().ok_or(ParseError::UnexpectedEof)?; + + // "As a special case, as noted in 7.2.7, an immediate generic region segment + // may have an unknown length. In this case, it also indicates the height of + // the generic region (i.e. the number of rows that have been decoded in this + // segment; it must be no greater than the region segment bitmap height value + // in the segment's region segment information field." (7.4.6.4) + if had_unknown_length { + // Length has already been validated during segment parsing. + let (head, tail) = data.split_at(data.len() - 4); + let row_count = u32::from_be_bytes(tail.try_into().unwrap()); + + if row_count > region_info.height { + bail!(RegionError::InvalidDimension); + } + + region_info.height = row_count; + data = head; + } + + Ok(GenericRegionHeader { + region_info, + mmr, + template, + tpgdon, + adaptive_template_pixels, + data, + }) +} + +/// Parse adaptive template pixel positions (7.4.6.3). +pub(crate) fn parse_adaptive_template_pixels( + reader: &mut Reader<'_>, + template: Template, + // TODO: Find a test with this flag. + _ext_template: bool, +) -> Result<[AdaptiveTemplatePixel; 4]> { + let num_pixels = template.adaptive_template_pixels() as usize; + + let mut pixels = [AdaptiveTemplatePixel::default(); 4]; + + for pixel in pixels.iter_mut().take(num_pixels) { + let x = reader.read_byte().ok_or(ParseError::UnexpectedEof)? as i8; + let y = reader.read_byte().ok_or(ParseError::UnexpectedEof)? as i8; + + // Validate AT pixel location (6.2.5.4, Figure 7). + // AT pixels must reference already-decoded pixels: + // - y must be <= 0 (current row or above) + // - if y == 0, x must be < 0 (strictly to the left of current pixel) + if y > 0 || (y == 0 && x >= 0) { + bail!(TemplateError::InvalidAtPixel); + } + + *pixel = AdaptiveTemplatePixel { x, y }; + } + + Ok(pixels) +} + +/// Whether the adaptive template pixels correspond to the default ones. +/// See Table 5. +fn has_default_at_pixels(template: Template, at_pixels: &[AdaptiveTemplatePixel; 4]) -> bool { + match template { + Template::Template0 => { + at_pixels[0].x == 3 + && at_pixels[0].y == -1 + && at_pixels[1].x == -3 + && at_pixels[1].y == -1 + && at_pixels[2].x == 2 + && at_pixels[2].y == -2 + && at_pixels[3].x == -2 + && at_pixels[3].y == -2 + } + Template::Template1 => at_pixels[0].x == 3 && at_pixels[0].y == -1, + Template::Template2 => at_pixels[0].x == 2 && at_pixels[0].y == -1, + Template::Template3 => at_pixels[0].x == 2 && at_pixels[0].y == -1, + } +} + +/// Decode a bitmap using MMR coding (6.2.6). +pub(crate) fn decode_bitmap_mmr(bitmap: &mut Bitmap, data: &[u8]) -> Result { + /// A decoder sink that writes decoded pixels into a `Bitmap`. + struct BitmapDecoder<'a> { + bitmap: &'a mut Bitmap, + x: u32, + y: u32, + /// Precomputed start index into `bitmap.data` for the current row. + row_start: usize, + /// Accumulator for pixels emitted by `push_pixel`. + buf: u8, + /// Number of bits accumulated in `buf`. + buf_len: u8, + } + + impl<'a> BitmapDecoder<'a> { + fn new(bitmap: &'a mut Bitmap) -> Self { + Self { + bitmap, + x: 0, + y: 0, + row_start: 0, + buf: 0, + buf_len: 0, + } + } + + #[inline] + fn flush_buf(&mut self) { + if self.buf_len == 0 { + return; + } + + let start_x = self.x - self.buf_len as u32; + if start_x < self.bitmap.width { + let word_idx = (start_x / WORD_BITS) as usize; + let bit_in_word = start_x % WORD_BITS; + let shift = WORD_SHIFT - bit_in_word - (self.buf_len as u32 - 1); + self.bitmap.data[self.row_start + word_idx] |= (self.buf as Word) << shift; + } + self.buf = 0; + self.buf_len = 0; + } + } + + impl hayro_ccitt::Decoder for BitmapDecoder<'_> { + #[inline] + fn push_pixel(&mut self, white: bool) { + self.buf = (self.buf << 1) | white as u8; + self.buf_len += 1; + self.x += 1; + + if self.buf_len == 8 { + self.flush_buf(); + } + } + + #[inline] + fn push_pixel_chunk(&mut self, white: bool, chunk_count: u32) { + const WORD_BYTES: usize = (WORD_BITS / 8) as usize; + const BYTE_MASKS: [Word; WORD_BYTES] = { + #[allow(trivial_numeric_casts)] + let mut masks = [0 as Word; WORD_BYTES]; + let mut i = 0; + while i < WORD_BYTES { + #[allow(trivial_numeric_casts)] + { + masks[i] = (0xFF as Word) << ((WORD_BYTES - 1 - i) * 8); + } + i += 1; + } + masks + }; + + let row_start = self.row_start; + let end_x = (self.x + chunk_count * 8).min(self.bitmap.width); + let white_mask = (white as Word).wrapping_neg(); + + let start = (self.x / 8) as usize; + let end = (end_x / 8) as usize; + let first_full = start.div_ceil(WORD_BYTES); + let last_full = end / WORD_BYTES; + + for b in start..(first_full * WORD_BYTES).min(end) { + self.bitmap.data[row_start + b / WORD_BYTES] |= + BYTE_MASKS[b % WORD_BYTES] & white_mask; + } + + if last_full > first_full { + self.bitmap.data[row_start + first_full..row_start + last_full].fill(white_mask); + } + + for b in (first_full.max(last_full) * WORD_BYTES)..end { + self.bitmap.data[row_start + b / WORD_BYTES] |= + BYTE_MASKS[b % WORD_BYTES] & white_mask; + } + + self.x = end_x; + } + + #[inline] + fn next_line(&mut self) { + self.flush_buf(); + + self.x = 0; + self.y += 1; + self.row_start = (self.y * self.bitmap.stride) as usize; + } + } + + let width = bitmap.width; + let height = bitmap.height; + let mut decoder = BitmapDecoder::new(bitmap); + + let settings = hayro_ccitt::DecodeSettings { + columns: width, + rows: height, + // "If the number of bytes contained in the encoded bitmap is known in + // advance, then it is permissible for the data stream not to contain + // an EOFB" (6.2.6). But it _can_ contain it, which is what this + // flag indicates. + end_of_block: true, + end_of_line: false, + rows_are_byte_aligned: false, + encoding: hayro_ccitt::EncodingMode::Group4, + // "Pixels decoded by the MMR decoder having the value 'black' shall be + // treated as having the value 1. Pixels decoded by the MMR decoder + // having the value 'white' shall be treated as having the value 0." + // (6.2.6) + // + // hayro-ccitt uses 1 for white, 0 for black by default, so we need to + // invert to match JBIG2 convention. + invert_black: true, + }; + + // "An invocation of the generic region decoding procedure with MMR equal to + // 1 shall consume an integral number of bytes, beginning and ending on a + // byte boundary. This may involve skipping over some bits in the last byte + // read." (6.2.6) + // + // hayro-ccitt already aligns to the byte boundary before returning, so + // nothing else to do here. + let mut context = hayro_ccitt::DecoderContext::new(settings); + Ok(hayro_ccitt::decode(data, &mut decoder, &mut context) + .map_err(|_| RegionError::InvalidMmrData)?) +} + +// I'm not sure why, but I was getting very weird codegen (with bad performance) +// when attempting to do this via generics. Hence why we use a macro for that. +macro_rules! decode_loop { + ($bitmap:expr, $decoder:expr, $contexts:expr, $ctx_gatherer:expr, + $tpgdon:expr, $sltp_context:expr, $gather:expr) => {{ + let bitmap: &mut Bitmap = $bitmap; + let decoder: &mut ArithmeticDecoder<'_> = $decoder; + let contexts: &mut [ArithmeticDecoderContext] = $contexts; + let ctx_gatherer: &mut ContextGatherer<'_> = $ctx_gatherer; + let width = bitmap.width; + let height = bitmap.height; + + // "1) Set: LTP = 0" (6.2.5.7) + let mut ltp = false; + + // "3) Decode each row as follows:" (6.2.5.7) + for y in 0..height { + // "b) If TPGDON is 1, then decode a bit using the arithmetic entropy + // coder" (6.2.5.7) + if $tpgdon { + let sltp = decoder.read_bit(&mut contexts[$sltp_context as usize]); + // "Let SLTP be the value of this bit. Set: LTP = LTP XOR SLTP" (6.2.5.7) + ltp = ltp != (sltp != 0); + } + + // "c) If LTP = 1 then set every pixel of the current row of GBREG equal + // to the corresponding pixel of the row immediately above." (6.2.5.7) + if ltp { + // If y == 0, pixels remain the same. + if y > 0 { + let stride = bitmap.stride as usize; + let src = (y as usize - 1) * stride; + bitmap + .data + .copy_within(src..src + stride, y as usize * stride); + } + } else { + // "d) If LTP = 0 then, from left to right, decode each pixel of the + // current row of GBREG." (6.2.5.7) + ctx_gatherer.start_row(bitmap, y); + + for x in 0..width { + ctx_gatherer.maybe_reload_buffers(bitmap, x); + let context_bits = ($gather)(ctx_gatherer, bitmap, x) as usize; + let pixel = decoder.read_bit(&mut contexts[context_bits]); + let value = pixel as u8; + bitmap.set_pixel(x, y, value); + ctx_gatherer.update_current_row(x, value); + } + } + } + }}; +} + +/// Decode a bitmap using arithmetic coding (6.2.5). +pub(crate) fn decode_bitmap_arithmetic_coding( + bitmap: &mut Bitmap, + decoder: &mut ArithmeticDecoder<'_>, + contexts: &mut [ArithmeticDecoderContext], + template: Template, + tpgdon: bool, + adaptive_template_pixels: &[AdaptiveTemplatePixel; 4], +) -> Result<()> { + let mut ctx_gatherer = ContextGatherer::new(template, adaptive_template_pixels); + + // See Figure 8 - 11. + let sltp_context: u16 = match template { + Template::Template0 => 0b1001101100100101, + Template::Template1 => 0b0011110010101, + Template::Template2 => 0b0011100101, + Template::Template3 => 0b0110010101, + }; + + if ctx_gatherer.use_default_at { + match template { + Template::Template0 => decode_loop!( + bitmap, + decoder, + contexts, + &mut ctx_gatherer, + tpgdon, + sltp_context, + ContextGatherer::gather_template0_default + ), + Template::Template1 => decode_loop!( + bitmap, + decoder, + contexts, + &mut ctx_gatherer, + tpgdon, + sltp_context, + ContextGatherer::gather_template1_default + ), + Template::Template2 => decode_loop!( + bitmap, + decoder, + contexts, + &mut ctx_gatherer, + tpgdon, + sltp_context, + ContextGatherer::gather_template2_default + ), + Template::Template3 => decode_loop!( + bitmap, + decoder, + contexts, + &mut ctx_gatherer, + tpgdon, + sltp_context, + ContextGatherer::gather_template3_default + ), + } + } else { + match template { + Template::Template0 => decode_loop!( + bitmap, + decoder, + contexts, + &mut ctx_gatherer, + tpgdon, + sltp_context, + ContextGatherer::gather_template0_custom + ), + Template::Template1 => decode_loop!( + bitmap, + decoder, + contexts, + &mut ctx_gatherer, + tpgdon, + sltp_context, + ContextGatherer::gather_template1_custom + ), + Template::Template2 => decode_loop!( + bitmap, + decoder, + contexts, + &mut ctx_gatherer, + tpgdon, + sltp_context, + ContextGatherer::gather_template2_custom + ), + Template::Template3 => decode_loop!( + bitmap, + decoder, + contexts, + &mut ctx_gatherer, + tpgdon, + sltp_context, + ContextGatherer::gather_template3_custom + ), + } + } + + Ok(()) +} + +pub(crate) struct ContextGatherer<'a> { + template: Template, + at_pixels: &'a [AdaptiveTemplatePixel; 4], + use_default_at: bool, + /// Used in `maybe_reload_buffers` to determine how far to the right + /// we might access pixels. + max_right: u32, + /// Current position. + cur_y: u32, + cur_x: u32, + /// Pre-fetched pixel buffers for rows y-2, y-1, y. + buf_m2: Word, + buf_m1: Word, + buf_cur: Word, + /// The current context for all 3 rows. + ctx: u16, +} + +impl<'a> ContextGatherer<'a> { + pub(crate) fn new(template: Template, at_pixels: &'a [AdaptiveTemplatePixel; 4]) -> Self { + let use_default_at = has_default_at_pixels(template, at_pixels); + let max_right = match template { + Template::Template0 | Template::Template1 => { + if use_default_at { + 3 + } else { + 2 + } + } + Template::Template2 | Template::Template3 => { + if use_default_at { + 2 + } else { + 1 + } + } + }; + Self { + template, + at_pixels, + use_default_at, + max_right, + cur_y: 0, + buf_m2: 0, + buf_m1: 0, + buf_cur: 0, + cur_x: 0, + ctx: 0, + } + } + + const SHIFT_MASK_T0: u16 = 0b0110_0011_1100_1110; + const SHIFT_MASK_T1: u16 = 0b0001_1101_1110_0110; + const SHIFT_MASK_T2: u16 = 0b0000_0011_0111_0010; + const SHIFT_MASK_T3: u16 = 0b0000_0011_1100_1110; + + const SHIFT_MASK_T0_DEFAULT: u16 = 0b1111_0111_1110_1110; + const SHIFT_MASK_T1_DEFAULT: u16 = 0b0001_1101_1111_0110; + const SHIFT_MASK_T2_DEFAULT: u16 = 0b0000_0011_0111_1010; + const SHIFT_MASK_T3_DEFAULT: u16 = 0b0000_0011_1110_1110; + + pub(crate) fn start_row(&mut self, bitmap: &Bitmap, y: u32) { + self.cur_y = y; + self.cur_x = 0; + + self.buf_m2 = if y >= 2 { + Self::load_word(bitmap, y - 2, 0) + } else { + 0 + }; + self.buf_m1 = if y >= 1 { + Self::load_word(bitmap, y - 1, 0) + } else { + 0 + }; + self.buf_cur = 0; + + // Start initializing the contexts. Note that this won't load all initial + // pixels yet, those will only be loaded after our first call to `gather`. + // See 6.2.5.3 for the pixel positions. + self.ctx = if self.use_default_at { + self.init_context_default() + } else { + self.init_context_custom() + }; + } + + #[inline] + fn init_context_custom(&self) -> u16 { + match self.template { + Template::Template0 => { + let m2 = Self::get_buf_pixel(self.buf_m2, 0); + let m1 = (Self::get_buf_pixel(self.buf_m1, 0) << 1) + | Self::get_buf_pixel(self.buf_m1, 1); + (m2 << 12) | (m1 << 5) + } + Template::Template1 => { + let m2 = (Self::get_buf_pixel(self.buf_m2, 0) << 1) + | Self::get_buf_pixel(self.buf_m2, 1); + let m1 = (Self::get_buf_pixel(self.buf_m1, 0) << 1) + | Self::get_buf_pixel(self.buf_m1, 1); + (m2 << 9) | (m1 << 4) + } + Template::Template2 => { + let m2 = Self::get_buf_pixel(self.buf_m2, 0); + let m1 = Self::get_buf_pixel(self.buf_m1, 0); + (m2 << 7) | (m1 << 3) + } + Template::Template3 => { + let m1 = Self::get_buf_pixel(self.buf_m1, 0); + m1 << 5 + } + } + } + + #[inline] + fn init_context_default(&self) -> u16 { + match self.template { + Template::Template0 => { + (Self::get_buf_pixel(self.buf_m2, 0) << 12) + | (Self::get_buf_pixel(self.buf_m2, 1) << 11) + | (Self::get_buf_pixel(self.buf_m1, 0) << 6) + | (Self::get_buf_pixel(self.buf_m1, 1) << 5) + | (Self::get_buf_pixel(self.buf_m1, 2) << 4) + } + Template::Template1 => { + (Self::get_buf_pixel(self.buf_m2, 0) << 10) + | (Self::get_buf_pixel(self.buf_m2, 1) << 9) + | (Self::get_buf_pixel(self.buf_m1, 0) << 5) + | (Self::get_buf_pixel(self.buf_m1, 1) << 4) + | (Self::get_buf_pixel(self.buf_m1, 2) << 3) + } + Template::Template2 => { + (Self::get_buf_pixel(self.buf_m2, 0) << 7) + | (Self::get_buf_pixel(self.buf_m1, 0) << 3) + | (Self::get_buf_pixel(self.buf_m1, 1) << 2) + } + Template::Template3 => { + (Self::get_buf_pixel(self.buf_m1, 0) << 5) + | (Self::get_buf_pixel(self.buf_m1, 1) << 4) + } + } + } + + #[inline(always)] + pub(crate) fn load_word(bitmap: &Bitmap, row_y: u32, start_x: u32) -> Word { + let word_idx = start_x / WORD_BITS; + + if start_x.is_multiple_of(WORD_BITS) { + bitmap.get_word(row_y, word_idx) + } else { + let bit_offset = start_x % WORD_BITS; + let word1 = bitmap.get_word(row_y, word_idx); + let word2 = bitmap.get_word(row_y, word_idx + 1); + (word1 << bit_offset) | (word2 >> (WORD_BITS - bit_offset)) + } + } + + #[inline] + pub(crate) fn get_buf_pixel(buf: Word, pos: u32) -> u16 { + if pos < WORD_BITS { + ((buf >> (WORD_SHIFT - pos)) & 1) as u16 + } else { + 0 + } + } + + #[inline(always)] + pub(crate) fn maybe_reload_buffers(&mut self, bitmap: &Bitmap, x: u32) { + if x + self.max_right >= self.cur_x + WORD_BITS { + let new_start = x.saturating_sub(4); + self.cur_x = new_start; + self.buf_m2 = if self.cur_y >= 2 { + Self::load_word(bitmap, self.cur_y - 2, new_start) + } else { + 0 + }; + self.buf_m1 = if self.cur_y >= 1 { + Self::load_word(bitmap, self.cur_y - 1, new_start) + } else { + 0 + }; + self.buf_cur = Self::load_word(bitmap, self.cur_y, new_start); + } + } + + #[inline(always)] + fn gather_template0_custom(&mut self, bitmap: &Bitmap, x: u32) -> u16 { + let bx = x - self.cur_x; + let xi = x as i32; + let yi = self.cur_y as i32; + + let new_pixels = (Self::get_buf_pixel(self.buf_m2, bx + 1) << 12) + | (Self::get_buf_pixel(self.buf_m1, bx + 2) << 5) + | Self::get_buf_pixel(self.buf_cur, bx.wrapping_sub(1)) + // Note that the cast from i32 to u32 is deliberate here: Negative positions + // should be resolved to 0, by casting negative i32 to u32 we end up with + // a very large positive number which is guaranteed to be OOB. The same + // applies to all other occurrences of this pattern (also in `generic_refinement.rs`). + | ((bitmap.get_pixel((xi + self.at_pixels[3].x as i32) as u32, (yi + self.at_pixels[3].y as i32) as u32) as u16) << 15) + | ((bitmap.get_pixel((xi + self.at_pixels[2].x as i32) as u32, (yi + self.at_pixels[2].y as i32) as u32) as u16) << 11) + | ((bitmap.get_pixel((xi + self.at_pixels[1].x as i32) as u32, (yi + self.at_pixels[1].y as i32) as u32) as u16) << 10) + | ((bitmap.get_pixel((xi + self.at_pixels[0].x as i32) as u32, (yi + self.at_pixels[0].y as i32) as u32) as u16) << 4); + + self.ctx = ((self.ctx << 1) & Self::SHIFT_MASK_T0) | new_pixels; + self.ctx + } + + #[inline(always)] + fn gather_template1_custom(&mut self, bitmap: &Bitmap, x: u32) -> u16 { + let bx = x - self.cur_x; + let xi = x as i32; + let yi = self.cur_y as i32; + + let new_pixels = (Self::get_buf_pixel(self.buf_m2, bx + 2) << 9) + | (Self::get_buf_pixel(self.buf_m1, bx + 2) << 4) + | Self::get_buf_pixel(self.buf_cur, bx.wrapping_sub(1)) + | ((bitmap.get_pixel( + (xi + self.at_pixels[0].x as i32) as u32, + (yi + self.at_pixels[0].y as i32) as u32, + ) as u16) + << 3); + + self.ctx = ((self.ctx << 1) & Self::SHIFT_MASK_T1) | new_pixels; + self.ctx + } + + #[inline(always)] + fn gather_template2_custom(&mut self, bitmap: &Bitmap, x: u32) -> u16 { + let bx = x - self.cur_x; + let xi = x as i32; + let yi = self.cur_y as i32; + + let new_pixels = (Self::get_buf_pixel(self.buf_m2, bx + 1) << 7) + | (Self::get_buf_pixel(self.buf_m1, bx + 1) << 3) + | Self::get_buf_pixel(self.buf_cur, bx.wrapping_sub(1)) + | ((bitmap.get_pixel( + (xi + self.at_pixels[0].x as i32) as u32, + (yi + self.at_pixels[0].y as i32) as u32, + ) as u16) + << 2); + + self.ctx = ((self.ctx << 1) & Self::SHIFT_MASK_T2) | new_pixels; + self.ctx + } + + #[inline(always)] + fn gather_template3_custom(&mut self, bitmap: &Bitmap, x: u32) -> u16 { + let bx = x - self.cur_x; + let xi = x as i32; + let yi = self.cur_y as i32; + + let new_pixels = (Self::get_buf_pixel(self.buf_m1, bx + 1) << 5) + | Self::get_buf_pixel(self.buf_cur, bx.wrapping_sub(1)) + | ((bitmap.get_pixel( + (xi + self.at_pixels[0].x as i32) as u32, + (yi + self.at_pixels[0].y as i32) as u32, + ) as u16) + << 4); + + self.ctx = ((self.ctx << 1) & Self::SHIFT_MASK_T3) | new_pixels; + self.ctx + } + + #[inline(always)] + pub(crate) fn gather_template0_default(&mut self, _bitmap: &Bitmap, x: u32) -> u16 { + let bx = x - self.cur_x; + let new_pixels = (Self::get_buf_pixel(self.buf_m2, bx + 2) << 11) + | (Self::get_buf_pixel(self.buf_m1, bx + 3) << 4) + | Self::get_buf_pixel(self.buf_cur, bx.wrapping_sub(1)); + + self.ctx = ((self.ctx << 1) & Self::SHIFT_MASK_T0_DEFAULT) | new_pixels; + self.ctx + } + + #[inline(always)] + pub(crate) fn gather_template1_default(&mut self, _bitmap: &Bitmap, x: u32) -> u16 { + let bx = x - self.cur_x; + let new_pixels = (Self::get_buf_pixel(self.buf_m2, bx + 2) << 9) + | (Self::get_buf_pixel(self.buf_m1, bx + 3) << 3) + | Self::get_buf_pixel(self.buf_cur, bx.wrapping_sub(1)); + + self.ctx = ((self.ctx << 1) & Self::SHIFT_MASK_T1_DEFAULT) | new_pixels; + self.ctx + } + + #[inline(always)] + pub(crate) fn gather_template2_default(&mut self, _bitmap: &Bitmap, x: u32) -> u16 { + let bx = x - self.cur_x; + let new_pixels = (Self::get_buf_pixel(self.buf_m2, bx + 1) << 7) + | (Self::get_buf_pixel(self.buf_m1, bx + 2) << 2) + | Self::get_buf_pixel(self.buf_cur, bx.wrapping_sub(1)); + + self.ctx = ((self.ctx << 1) & Self::SHIFT_MASK_T2_DEFAULT) | new_pixels; + self.ctx + } + + #[inline(always)] + pub(crate) fn gather_template3_default(&mut self, _bitmap: &Bitmap, x: u32) -> u16 { + let bx = x - self.cur_x; + let new_pixels = (Self::get_buf_pixel(self.buf_m1, bx + 2) << 4) + | Self::get_buf_pixel(self.buf_cur, bx.wrapping_sub(1)); + + self.ctx = ((self.ctx << 1) & Self::SHIFT_MASK_T3_DEFAULT) | new_pixels; + self.ctx + } + + /// Note: The caller must ensure that `gather` has been called for this `x` + /// first. + #[inline(always)] + pub(crate) fn update_current_row(&mut self, x: u32, value: u8) { + debug_assert!(x >= self.cur_x && x < self.cur_x + WORD_BITS); + + let bit_pos = WORD_SHIFT - (x - self.cur_x); + #[allow(trivial_numeric_casts)] + let mask = (1 as Word) << bit_pos; + self.buf_cur = (self.buf_cur & !mask) | ((value as Word) << bit_pos); + } +} diff --git a/vendor/hayro-jbig2/src/decode/generic_refinement.rs b/vendor/hayro-jbig2/src/decode/generic_refinement.rs new file mode 100644 index 000000000..a566b8de5 --- /dev/null +++ b/vendor/hayro-jbig2/src/decode/generic_refinement.rs @@ -0,0 +1,507 @@ +//! Generic refinement region segment parsing and decoding (7.4.7, 6.3). + +use alloc::vec::Vec; + +use super::{ + AdaptiveTemplatePixel, RefinementTemplate, RegionBitmap, RegionSegmentInfo, + parse_refinement_at_pixels, parse_region_segment_info, +}; +use crate::ScratchBuffers; +use crate::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext}; +use crate::bitmap::{Bitmap, WORD_BITS, WORD_SHIFT, Word}; +use crate::decode::generic::ContextGatherer; +use crate::error::{OverflowError, ParseError, RegionError, Result, bail}; +use crate::reader::Reader; + +/// Generic refinement region decoding procedure (6.3). +pub(crate) fn decode( + header: &GenericRefinementRegionHeader<'_>, + reference: &Bitmap, + ctx: &mut ScratchBuffers, +) -> Result { + let mut region = Bitmap::new_with( + header.region_info.width, + header.region_info.height, + header.region_info.x_location, + header.region_info.y_location, + false, + )?; + + decode_into(header, reference, &mut region, ctx)?; + + Ok(RegionBitmap { + bitmap: region, + combination_operator: header.region_info.combination_operator, + }) +} + +/// Decode a generic refinement region directly into an existing bitmap. +pub(crate) fn decode_into( + header: &GenericRefinementRegionHeader<'_>, + reference: &Bitmap, + region: &mut Bitmap, + ctx: &mut ScratchBuffers, +) -> Result<()> { + let data = header.data; + + // Validate that the region fits within the reference bitmap. + // When referring to another segment, dimensions must match exactly (7.4.7.5). + // When using the page bitmap as reference, the region must fit within the page. + if header.region_info.width > reference.width || header.region_info.height > reference.height { + bail!(RegionError::InvalidDimension); + } + + let reference_dx = i32::try_from(reference.x_location) + .ok() + .and_then(|r: i32| { + i32::try_from(header.region_info.x_location) + .ok() + .and_then(|h| r.checked_sub(h)) + }) + .ok_or(OverflowError::ReferenceOffset)?; + let reference_dy = i32::try_from(reference.y_location) + .ok() + .and_then(|r: i32| { + i32::try_from(header.region_info.y_location) + .ok() + .and_then(|h| r.checked_sub(h)) + }) + .ok_or(OverflowError::ReferenceOffset)?; + + let mut decoder = ArithmeticDecoder::new(data); + let num_context_bits = header.template.context_bits(); + ctx.contexts.clear(); + ctx.contexts + .resize(1 << num_context_bits, ArithmeticDecoderContext::default()); + + decode_bitmap( + &mut decoder, + &mut ctx.contexts, + region, + reference, + reference_dx, + reference_dy, + header.template, + &header.adaptive_template_pixels, + header.tpgron, + )?; + + Ok(()) +} + +/// Parsed generic refinement region segment header (7.4.7.1). +#[derive(Debug, Clone)] +pub(crate) struct GenericRefinementRegionHeader<'a> { + pub(crate) region_info: RegionSegmentInfo, + pub(crate) template: RefinementTemplate, + pub(crate) tpgron: bool, + pub(crate) adaptive_template_pixels: Vec, + pub(crate) data: &'a [u8], +} + +/// Parse a generic refinement region segment header (7.4.7.1). +pub(crate) fn parse<'a>(reader: &mut Reader<'a>) -> Result> { + let region_info = parse_region_segment_info(reader)?; + let flags = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + let template = RefinementTemplate::from_byte(flags); + let tpgron = flags & 0x02 != 0; + let adaptive_template_pixels = if template == RefinementTemplate::Template0 { + parse_refinement_at_pixels(reader)? + } else { + Vec::new() + }; + let data = reader.tail().ok_or(ParseError::UnexpectedEof)?; + + Ok(GenericRefinementRegionHeader { + region_info, + template, + tpgron, + adaptive_template_pixels, + data, + }) +} + +fn has_default_at_pixels(at_pixels: &[AdaptiveTemplatePixel]) -> bool { + at_pixels.len() >= 2 + && at_pixels[0].x == -1 + && at_pixels[0].y == -1 + && at_pixels[1].x == -1 + && at_pixels[1].y == -1 +} + +pub(crate) struct RefinementContextGatherer<'a> { + template: RefinementTemplate, + at_pixels: &'a [AdaptiveTemplatePixel], + use_default_at: bool, + reference_dx: i32, + reference_dy: i32, + reg_y: u32, + reg_cur_x: u32, + reg_m1: Word, + reg_cur: Word, + ref_cur_x: u32, + ref_m1: Word, + ref_cur: Word, + ref_p1: Word, + ctx: u16, +} + +const SHIFT_MASK_T0_DEFAULT: u16 = 0b1_1001_1011_0110; +const SHIFT_MASK_T0_CUSTOM: u16 = 0b0_1000_1011_0110; +const SHIFT_MASK_T1: u16 = 0b11_0001_1010; + +impl<'a> RefinementContextGatherer<'a> { + pub(crate) fn new( + template: RefinementTemplate, + at_pixels: &'a [AdaptiveTemplatePixel], + reference_dx: i32, + reference_dy: i32, + ) -> Self { + let use_default_at = + template == RefinementTemplate::Template0 && has_default_at_pixels(at_pixels); + Self { + template, + at_pixels, + use_default_at, + reference_dx, + reference_dy, + reg_y: 0, + reg_cur_x: 0, + reg_m1: 0, + reg_cur: 0, + ref_cur_x: 0, + ref_m1: 0, + ref_cur: 0, + ref_p1: 0, + ctx: 0, + } + } + + pub(crate) fn start_row(&mut self, region: &Bitmap, reference: &Bitmap, y: u32) { + self.reg_y = y; + let ref_y = y as i32 - self.reference_dy; + // This will on purpose wrap to a very large `u32` for negative values, such that + // they resolve to 0 in `ContextGatherer::load_word`. + let ref_x0 = (-self.reference_dx) as u32; + + self.reg_cur_x = 0; + self.reg_m1 = if y >= 1 { + ContextGatherer::load_word(region, y - 1, 0) + } else { + 0 + }; + self.reg_cur = 0; + + self.ref_cur_x = 0; + self.ref_m1 = ContextGatherer::load_word(reference, (ref_y - 1) as u32, 0); + self.ref_cur = ContextGatherer::load_word(reference, ref_y as u32, 0); + self.ref_p1 = ContextGatherer::load_word(reference, (ref_y + 1) as u32, 0); + + self.ctx = match self.template { + RefinementTemplate::Template1 => { + let rbx = ref_x0.wrapping_sub(self.ref_cur_x); + + let reg_m1_0 = ContextGatherer::get_buf_pixel(self.reg_m1, 0); + let ref_cur_m1 = ContextGatherer::get_buf_pixel(self.ref_cur, rbx.wrapping_sub(1)); + let ref_cur_0 = ContextGatherer::get_buf_pixel(self.ref_cur, rbx); + let ref_p1_0 = ContextGatherer::get_buf_pixel(self.ref_p1, rbx); + + (reg_m1_0 << 7) | (ref_cur_m1 << 3) | (ref_cur_0 << 2) | (ref_p1_0) + } + RefinementTemplate::Template0 if self.use_default_at => { + let rbx = ref_x0.wrapping_sub(self.ref_cur_x); + let reg_m1_0 = ContextGatherer::get_buf_pixel(self.reg_m1, 0); + let ref_m1_m1 = ContextGatherer::get_buf_pixel(self.ref_m1, rbx.wrapping_sub(1)); + let ref_m1_0 = ContextGatherer::get_buf_pixel(self.ref_m1, rbx); + let ref_cur_m1 = ContextGatherer::get_buf_pixel(self.ref_cur, rbx.wrapping_sub(1)); + let ref_cur_0 = ContextGatherer::get_buf_pixel(self.ref_cur, rbx); + let ref_p1_m1 = ContextGatherer::get_buf_pixel(self.ref_p1, rbx.wrapping_sub(1)); + let ref_p1_0 = ContextGatherer::get_buf_pixel(self.ref_p1, rbx); + + (reg_m1_0 << 10) + | (ref_m1_m1 << 7) + | (ref_m1_0 << 6) + | (ref_cur_m1 << 4) + | (ref_cur_0 << 3) + | (ref_p1_m1 << 1) + | ref_p1_0 + } + RefinementTemplate::Template0 => { + let rbx = ref_x0.wrapping_sub(self.ref_cur_x); + let reg_m1_0 = ContextGatherer::get_buf_pixel(self.reg_m1, 0); + let ref_m1_0 = ContextGatherer::get_buf_pixel(self.ref_m1, rbx); + let ref_cur_m1 = ContextGatherer::get_buf_pixel(self.ref_cur, rbx.wrapping_sub(1)); + let ref_cur_0 = ContextGatherer::get_buf_pixel(self.ref_cur, rbx); + let ref_p1_m1 = ContextGatherer::get_buf_pixel(self.ref_p1, rbx.wrapping_sub(1)); + let ref_p1_0 = ContextGatherer::get_buf_pixel(self.ref_p1, rbx); + (reg_m1_0 << 10) + | (ref_m1_0 << 6) + | (ref_cur_m1 << 4) + | (ref_cur_0 << 3) + | (ref_p1_m1 << 1) + | ref_p1_0 + } + }; + } + + #[inline(always)] + pub(crate) fn maybe_reload_buffers(&mut self, region: &Bitmap, reference: &Bitmap, x: u32) { + if x + 1 >= self.reg_cur_x + WORD_BITS { + let new_start = x.saturating_sub(1); + self.reg_cur_x = new_start; + self.reg_m1 = if self.reg_y >= 1 { + ContextGatherer::load_word(region, self.reg_y - 1, new_start) + } else { + 0 + }; + self.reg_cur = ContextGatherer::load_word(region, self.reg_y, new_start); + } + + let ref_x_signed = x as i32 - self.reference_dx; + if ref_x_signed < 0 { + self.ref_cur_x = 0; + self.ref_m1 = 0; + self.ref_cur = 0; + self.ref_p1 = 0; + } else { + let ref_x = ref_x_signed as u32; + + if ref_x + 1 >= self.ref_cur_x + WORD_BITS || ref_x < self.ref_cur_x { + let new_start = ref_x.saturating_sub(1); + let ref_y = self.reg_y as i32 - self.reference_dy; + self.ref_cur_x = new_start; + self.ref_m1 = ContextGatherer::load_word(reference, (ref_y - 1) as u32, new_start); + self.ref_cur = ContextGatherer::load_word(reference, ref_y as u32, new_start); + self.ref_p1 = ContextGatherer::load_word(reference, (ref_y + 1) as u32, new_start); + } + } + } + + #[inline(always)] + pub(crate) fn tpgr_all_same(&self, x: u32) -> bool { + let ref_x = (x as i32 - self.reference_dx) as u32; + let rbx = ref_x.wrapping_sub(self.ref_cur_x); + + #[inline(always)] + fn extract_3bits(buf: Word, rbx: u32) -> u32 { + if rbx == 0 || rbx >= WORD_BITS - 1 { + // Near word boundary, fall back to individual extraction. + let b0 = ContextGatherer::get_buf_pixel(buf, rbx.wrapping_sub(1)) as u32; + let b1 = ContextGatherer::get_buf_pixel(buf, rbx) as u32; + let b2 = ContextGatherer::get_buf_pixel(buf, rbx.wrapping_add(1)) as u32; + (b0 << 2) | (b1 << 1) | b2 + } else { + // Fast path: shift and mask 3 bits at once. + #[allow(trivial_numeric_casts)] + #[allow(clippy::unnecessary_cast)] + { + ((buf >> (WORD_SHIFT - rbx - 1)) & 0b111) as u32 + } + } + } + + let m1 = extract_3bits(self.ref_m1, rbx); + let cur = extract_3bits(self.ref_cur, rbx); + let p1 = extract_3bits(self.ref_p1, rbx); + + // Check that all 9 pixels are the same. + m1 == cur && cur == p1 && (cur == 0 || cur == 0b111) + } + + #[inline(always)] + pub(crate) fn ref_center_pixel(&self, x: u32) -> u8 { + let ref_x = (x as i32 - self.reference_dx) as u32; + let rbx = ref_x.wrapping_sub(self.ref_cur_x); + + ContextGatherer::get_buf_pixel(self.ref_cur, rbx) as u8 + } + + #[inline(always)] + pub(crate) fn update_current_row(&mut self, x: u32, value: u8) { + let bx = x - self.reg_cur_x; + debug_assert!(bx < WORD_BITS); + let bit_pos = WORD_SHIFT - bx; + #[allow(trivial_numeric_casts)] + let mask = (1 as Word) << bit_pos; + self.reg_cur = (self.reg_cur & !mask) | ((value as Word) << bit_pos); + } + + #[inline(always)] + fn gather_template0_default(&mut self, _region: &Bitmap, _reference: &Bitmap, x: u32) -> u16 { + let bx = x - self.reg_cur_x; + let ref_x = (x as i32 - self.reference_dx) as u32; + let rbx = ref_x.wrapping_sub(self.ref_cur_x); + + let new_pixels = (ContextGatherer::get_buf_pixel(self.reg_m1, bx + 1) << 10) + | (ContextGatherer::get_buf_pixel(self.reg_cur, bx.wrapping_sub(1)) << 9) + | (ContextGatherer::get_buf_pixel(self.ref_m1, rbx.wrapping_add(1)) << 6) + | (ContextGatherer::get_buf_pixel(self.ref_cur, rbx.wrapping_add(1)) << 3) + | ContextGatherer::get_buf_pixel(self.ref_p1, rbx.wrapping_add(1)); + + self.ctx = ((self.ctx << 1) & SHIFT_MASK_T0_DEFAULT) | new_pixels; + self.ctx + } + + #[inline(always)] + fn gather_template0_custom(&mut self, region: &Bitmap, reference: &Bitmap, x: u32) -> u16 { + let bx = x - self.reg_cur_x; + let ref_x_i = x as i32 - self.reference_dx; + let rbx = ref_x_i as u32 - self.ref_cur_x; + + let xi = x as i32; + let yi = self.reg_y as i32; + let ref_y = yi - self.reference_dy; + let at1 = self.at_pixels[0]; + let at2 = self.at_pixels[1]; + + let new_pixels = ((region.get_pixel((xi + at1.x as i32) as u32, (yi + at1.y as i32) as u32) + as u16) + << 12) + | (ContextGatherer::get_buf_pixel(self.reg_m1, bx + 1) << 10) + | (ContextGatherer::get_buf_pixel(self.reg_cur, bx.wrapping_sub(1)) << 9) + | ((reference.get_pixel( + (ref_x_i + at2.x as i32) as u32, + (ref_y + at2.y as i32) as u32, + ) as u16) + << 8) + | (ContextGatherer::get_buf_pixel(self.ref_m1, rbx.wrapping_add(1)) << 6) + | (ContextGatherer::get_buf_pixel(self.ref_cur, rbx.wrapping_add(1)) << 3) + | ContextGatherer::get_buf_pixel(self.ref_p1, rbx.wrapping_add(1)); + + self.ctx = ((self.ctx << 1) & SHIFT_MASK_T0_CUSTOM) | new_pixels; + self.ctx + } + + #[inline(always)] + fn gather_template1(&mut self, _region: &Bitmap, _reference: &Bitmap, x: u32) -> u16 { + let bx = x - self.reg_cur_x; + let ref_x = (x as i32 - self.reference_dx) as u32; + let rbx = ref_x.wrapping_sub(self.ref_cur_x); + + let new_pixels = (ContextGatherer::get_buf_pixel(self.reg_m1, bx + 1) << 7) + | (ContextGatherer::get_buf_pixel(self.reg_cur, bx.wrapping_sub(1)) << 6) + | (ContextGatherer::get_buf_pixel(self.ref_m1, rbx) << 5) + | (ContextGatherer::get_buf_pixel(self.ref_cur, rbx.wrapping_add(1)) << 2) + | ContextGatherer::get_buf_pixel(self.ref_p1, rbx.wrapping_add(1)); + + self.ctx = ((self.ctx << 1) & SHIFT_MASK_T1) | new_pixels; + self.ctx + } +} + +/// Decode a refinement bitmap (6.3.5.6). +pub(crate) fn decode_bitmap( + decoder: &mut ArithmeticDecoder<'_>, + contexts: &mut [ArithmeticDecoderContext], + region: &mut Bitmap, + reference: &Bitmap, + reference_dx: i32, + reference_dy: i32, + gr_template: RefinementTemplate, + adaptive_template_pixels: &[AdaptiveTemplatePixel], + tpgron: bool, +) -> Result<()> { + macro_rules! refinement_decode_loop { + ($gatherer:expr, $tpgron:expr, $sltp_context:expr, $gather:expr) => {{ + let gatherer: &mut RefinementContextGatherer<'_> = $gatherer; + let width = region.width; + let height = region.height; + + // "1) Set LTP = 0." (6.3.5.6) + let mut ltp = false; + + // "3) Decode each row as follows:" (6.3.5.6) + for y in 0..height { + // "b) If TPGRON is 1, then decode a bit using the arithmetic entropy + // coder" (6.3.5.6) + if $tpgron { + let sltp = decoder.read_bit(&mut contexts[$sltp_context as usize]); + // "Let SLTP be the value of this bit. Set: LTP = LTP XOR SLTP" + ltp = ltp != (sltp != 0); + } + + gatherer.start_row(region, reference, y); + + // "c) If LTP = 0 then, from left to right, explicitly decode all pixels + // of the current row of GRREG." (6.3.5.6) + if !ltp { + for x in 0..width { + gatherer.maybe_reload_buffers(region, reference, x); + let context = ($gather)(gatherer, region, reference, x) as usize; + let pixel = decoder.read_bit(&mut contexts[context]); + region.set_pixel(x, y, pixel as u8); + gatherer.update_current_row(x, pixel as u8); + } + } else { + // "d) If LTP = 1 then, from left to right, implicitly decode certain + // pixels of the current row of GRREG, and explicitly decode the rest." + // (6.3.5.6) + for x in 0..width { + gatherer.maybe_reload_buffers(region, reference, x); + + let context = ($gather)(gatherer, region, reference, x) as usize; + + // "i) Set TPGRPIX equal to 1 if: + // - TPGRON is 1 AND; + // - a 3 × 3 pixel array in the reference bitmap (Figure 16), + // centred at the location corresponding to the current pixel, + // contains pixels all of the same value." (6.3.5.6) + if gatherer.tpgr_all_same(x) { + // "ii) If TPGRPIX is 1 then implicitly decode the current pixel + // by setting it equal to its predicted value (TPGRVAL)." (6.3.5.6) + // + // "When TPGRPIX is set to 1, set TPGRVAL equal to the current pixel + // predicted value, which is the common value of the nine adjacent + // pixels in the 3 × 3 array." (6.3.5.6) + let val = gatherer.ref_center_pixel(x); + region.set_pixel(x, y, val); + gatherer.update_current_row(x, val); + } else { + // "iii) Otherwise, explicitly decode the current pixel using the + // methodology of steps 3 c) i) through 3 c) iii) above." (6.3.5.6) + let pixel = decoder.read_bit(&mut contexts[context]); + region.set_pixel(x, y, pixel as u8); + gatherer.update_current_row(x, pixel as u8); + } + } + } + } + }}; + } + + let mut gatherer = RefinementContextGatherer::new( + gr_template, + adaptive_template_pixels, + reference_dx, + reference_dy, + ); + + // Context for SLTP depends on template (Figures 14, 15). + let sltp_context: u16 = match gr_template { + RefinementTemplate::Template0 => 0b0000000010000, + RefinementTemplate::Template1 => 0b0000001000, + }; + + match gr_template { + RefinementTemplate::Template0 if gatherer.use_default_at => refinement_decode_loop!( + &mut gatherer, + tpgron, + sltp_context, + RefinementContextGatherer::gather_template0_default + ), + RefinementTemplate::Template0 => refinement_decode_loop!( + &mut gatherer, + tpgron, + sltp_context, + RefinementContextGatherer::gather_template0_custom + ), + RefinementTemplate::Template1 => refinement_decode_loop!( + &mut gatherer, + tpgron, + sltp_context, + RefinementContextGatherer::gather_template1 + ), + } + + Ok(()) +} diff --git a/vendor/hayro-jbig2/src/decode/halftone.rs b/vendor/hayro-jbig2/src/decode/halftone.rs new file mode 100644 index 000000000..f03a15702 --- /dev/null +++ b/vendor/hayro-jbig2/src/decode/halftone.rs @@ -0,0 +1,425 @@ +//! Halftone region segment parsing and decoding (7.4.5, 6.6). + +use alloc::vec; +use alloc::vec::Vec; + +use super::RegionBitmap; +use super::pattern::PatternDictionary; +use super::{CombinationOperator, RegionSegmentInfo, Template, parse_region_segment_info}; +use crate::ScratchBuffers; +use crate::bitmap::{Bitmap, MAX_DIMENSION, WORD_BITS}; +use crate::error::{OverflowError, ParseError, RegionError, Result, bail}; +use crate::gray_scale::{GrayScaleParams, decode_gray_scale_image}; +use crate::reader::Reader; + +/// Decode a halftone region segment (7.4.5.2, 6.6). +pub(crate) fn decode( + header: &HalftoneRegionHeader<'_>, + pattern_dict: &PatternDictionary, + ctx: &mut ScratchBuffers, +) -> Result { + let region = &header.region_info; + + let mut htreg = Bitmap::new_with( + region.width, + region.height, + region.x_location, + region.y_location, + header.flags.initial_pixel_color, + )?; + + decode_into(header, pattern_dict, &mut htreg, ctx)?; + + Ok(RegionBitmap { + bitmap: htreg, + combination_operator: region.combination_operator, + }) +} + +pub(crate) fn decode_into( + header: &HalftoneRegionHeader<'_>, + pattern_dict: &PatternDictionary, + htreg: &mut Bitmap, + ctx: &mut ScratchBuffers, +) -> Result<()> { + let skip_bitmap = if header.flags.enable_skip { + Some(compute_skip_bitmap(header, pattern_dict, htreg)?) + } else { + None + }; + + // "3) Set HBPP to ⌈log₂(HNUMPATS)⌉." (6.6.5) + let bits_per_pixel = (pattern_dict.patterns.len() as u32) + .saturating_sub(1) + .checked_ilog2() + .map_or(1, |n| n + 1); + + // "4) Decode an image GI of size HGW by HGH with HBPP bits per pixel using + // the gray-scale image decoding procedure as described in Annex C." (6.6.5) + let gs_params = GrayScaleParams { + use_mmr: header.flags.mmr, + bits_per_pixel, + width: header.grid_position_and_size.width, + height: header.grid_position_and_size.height, + template: header.flags.template, + skip_mask: skip_bitmap.as_deref(), + }; + let gi = decode_gray_scale_image(header.data, &gs_params, ctx)?; + + // "5) Place sequentially the patterns corresponding to the values in GI into + // HTREG by the procedure described in 6.6.5.2." (6.6.5) + render_patterns(htreg, &gi, header, pattern_dict)?; + + Ok(()) +} + +/// Parse a halftone region segment header (7.4.5.1). +pub(crate) fn parse<'a>(reader: &mut Reader<'a>) -> Result> { + let region_info = parse_region_segment_info(reader)?; + let flags_byte = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + let mmr = flags_byte & 0x01 != 0; + let template = Template::from_byte(flags_byte >> 1); + let enable_skip = flags_byte & 0x08 != 0; + let combination_operator = CombinationOperator::from_value(flags_byte >> 4)?; + let initial_pixel_color = flags_byte & 0x80 != 0; + + let flags = HalftoneRegionFlags { + mmr, + template, + enable_skip, + combination_operator, + initial_pixel_color, + }; + + let grid_width = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + let grid_height = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + + if grid_width > MAX_DIMENSION || grid_height > MAX_DIMENSION { + bail!(OverflowError::GridDimension); + } + // PrintCraft patch: the product is bounded too (see `bitmap::MAX_PIXELS`). + if u64::from(grid_width) * u64::from(grid_height) > crate::bitmap::MAX_PIXELS { + bail!(OverflowError::GridDimension); + } + + // TODO: Check grid offsets as well. + + let grid_horizontal_offset = reader.read_i32().ok_or(ParseError::UnexpectedEof)?; + let grid_vertical_offset = reader.read_i32().ok_or(ParseError::UnexpectedEof)?; + + let grid_position_and_size = HalftoneGridPositionAndSize { + width: grid_width, + height: grid_height, + horizontal_offset: grid_horizontal_offset, + vertical_offset: grid_vertical_offset, + }; + + let grid_x_vector = reader.read_u16().ok_or(ParseError::UnexpectedEof)?; + let grid_y_vector = reader.read_u16().ok_or(ParseError::UnexpectedEof)?; + + let grid_vector = HalftoneGridVector { + x_vector: grid_x_vector, + y_vector: grid_y_vector, + }; + + let data = reader.tail().ok_or(ParseError::UnexpectedEof)?; + + Ok(HalftoneRegionHeader { + region_info, + flags, + grid_position_and_size, + grid_vector, + data, + }) +} + +/// Parsed halftone region segment flags (7.4.5.1.1). +#[derive(Debug, Clone)] +pub(crate) struct HalftoneRegionFlags { + pub(crate) mmr: bool, + pub(crate) template: Template, + pub(crate) enable_skip: bool, + pub(crate) combination_operator: CombinationOperator, + pub(crate) initial_pixel_color: bool, +} + +/// Halftone grid position and size (7.4.5.1.2). +#[derive(Debug, Clone)] +pub(crate) struct HalftoneGridPositionAndSize { + pub(crate) width: u32, + pub(crate) height: u32, + pub(crate) horizontal_offset: i32, + pub(crate) vertical_offset: i32, +} + +/// Halftone grid vector (7.4.5.1.3). +#[derive(Debug, Clone)] +pub(crate) struct HalftoneGridVector { + /// `HRX` - 256 times the horizontal coordinate of the halftone grid vector. + pub(crate) x_vector: u16, + /// `HRY` - 256 times the vertical coordinate of the halftone grid vector. + pub(crate) y_vector: u16, +} + +/// Parsed halftone region segment header (7.4.5.1). +#[derive(Debug, Clone)] +pub(crate) struct HalftoneRegionHeader<'a> { + pub(crate) region_info: RegionSegmentInfo, + pub(crate) flags: HalftoneRegionFlags, + pub(crate) grid_position_and_size: HalftoneGridPositionAndSize, + pub(crate) grid_vector: HalftoneGridVector, + pub(crate) data: &'a [u8], +} + +struct GridCoords { + x: i64, + y: i64, + row_x: i64, + row_y: i64, + hrx: i64, + hry: i64, +} + +impl GridCoords { + fn new(grid: &HalftoneGridPositionAndSize, vector: &HalftoneGridVector) -> Self { + Self { + x: grid.horizontal_offset as i64, + y: grid.vertical_offset as i64, + row_x: grid.horizontal_offset as i64, + row_y: grid.vertical_offset as i64, + hrx: vector.x_vector as i64, + hry: vector.y_vector as i64, + } + } + + #[inline] + fn get(&self) -> (i32, i32) { + ((self.x >> 8) as i32, (self.y >> 8) as i32) + } + + #[inline] + fn advance_col(&mut self) { + self.x += self.hrx; + self.y -= self.hry; + } + + #[inline] + fn advance_row(&mut self) { + self.row_x += self.hry; + self.row_y += self.hrx; + self.x = self.row_x; + self.y = self.row_y; + } +} + +/// Compute the HSKIP bitmap (6.6.5.1). +fn compute_skip_bitmap( + header: &HalftoneRegionHeader<'_>, + pattern_dict: &PatternDictionary, + htreg: &Bitmap, +) -> Result> { + let grid = &header.grid_position_and_size; + let pattern_width = pattern_dict.pattern_width as i32; + let pattern_height = pattern_dict.pattern_height as i32; + let region_width = htreg.width as i32; + let region_height = htreg.height as i32; + + let stride = grid.width.div_ceil(32); + let len = stride + .checked_mul(grid.height) + .ok_or(OverflowError::BitmapDimension)? as usize; + let mut hskip = vec![0_u32; len]; + let mut coords = GridCoords::new(grid, &header.grid_vector); + + for m_g in 0..grid.height { + for n_g in 0..grid.width { + let (x, y) = coords.get(); + + let skip = (x + pattern_width <= 0) + || (x >= region_width) + || (y + pattern_height <= 0) + || (y >= region_height); + + if skip { + let word_idx = (m_g * stride + n_g / 32) as usize; + let bit_pos = 31 - (n_g % 32); + hskip[word_idx] |= 1 << bit_pos; + } + + coords.advance_col(); + } + coords.advance_row(); + } + + Ok(hskip) +} + +/// Render patterns into the target region (6.6.5.2). +fn render_patterns( + region: &mut Bitmap, + gi: &[u32], + header: &HalftoneRegionHeader<'_>, + pattern_dict: &PatternDictionary, +) -> Result<()> { + let grid = &header.grid_position_and_size; + let op = header.flags.combination_operator; + let pw = pattern_dict.pattern_width; + let ph = pattern_dict.pattern_height; + let region_w = region.width as i32; + let region_h = region.height as i32; + let stride = region.stride; + let num_patterns = pattern_dict.patterns.len(); + + // The original pattern mask has 1s in the top `pw` bits. + let original_mask = if pw >= WORD_BITS { + !0_u32 + } else { + !0_u32 << (WORD_BITS - pw) + }; + + macro_rules! blit_rows { + ($data:expr, $shifted:expr, $yu:expr, $first_word:expr, $two:expr, + |$d0:ident, $s0:ident| $op0:expr, |$d1:ident, $s1:ident| $op1:expr) => { + if $two { + for row in 0..ph { + let base = (($yu + row) * stride + $first_word) as usize; + let si = row as usize * 2; + let $d0 = $data[base]; + let $s0 = $shifted[si]; + $data[base] = $op0; + let $d1 = $data[base + 1]; + let $s1 = $shifted[si + 1]; + $data[base + 1] = $op1; + } + } else { + for row in 0..ph { + let base = (($yu + row) * stride + $first_word) as usize; + let $d0 = $data[base]; + let $s0 = $shifted[row as usize * 2]; + $data[base] = $op0; + } + } + }; + } + + let mut coords = GridCoords::new(grid, &header.grid_vector); + let mut gi_idx = 0; + + for _ in 0..grid.height { + for _ in 0..grid.width { + let (x, y) = coords.get(); + + let pattern_index = gi[gi_idx] as usize; + gi_idx += 1; + + if pattern_index >= num_patterns { + bail!(RegionError::GrayScaleOutOfRange); + } + + // Check whether we can use the blit fast path. + let shifted = + if x >= 0 && y >= 0 && x + pw as i32 <= region_w && y + ph as i32 <= region_h { + pattern_dict.shifted_pattern(pattern_index, x as u32 % WORD_BITS) + } else { + None + }; + + if let Some(shifted) = shifted { + let xu = x as u32; + let yu = y as u32; + let first_word = xu / WORD_BITS; + let offset = xu % WORD_BITS; + let two = offset != 0 && first_word + 1 < stride; + + // TODO: It seems our current test suite only triggers for + // `Or`. + + match op { + CombinationOperator::Or => { + blit_rows!( + region.data, + shifted, + yu, + first_word, + two, + |d, s| d | s, + |d, s| d | s + ); + } + CombinationOperator::Xor => { + blit_rows!( + region.data, + shifted, + yu, + first_word, + two, + |d, s| d ^ s, + |d, s| d ^ s + ); + } + CombinationOperator::And => { + let inv_mask0 = !(original_mask >> offset); + let inv_mask1 = if offset == 0 { + !0 + } else { + !(original_mask << (WORD_BITS - offset)) + }; + blit_rows!( + region.data, + shifted, + yu, + first_word, + two, + |d, s| d & (s | inv_mask0), + |d, s| d & (s | inv_mask1) + ); + } + CombinationOperator::Replace => { + let inv_mask0 = !(original_mask >> offset); + let inv_mask1 = if offset == 0 { + !0 + } else { + !(original_mask << (WORD_BITS - offset)) + }; + blit_rows!( + region.data, + shifted, + yu, + first_word, + two, + |d, s| (d & inv_mask0) | s, + |d, s| (d & inv_mask1) | s + ); + } + CombinationOperator::Xnor => { + let mask0 = original_mask >> offset; + let mask1 = if offset == 0 { + 0 + } else { + original_mask << (WORD_BITS - offset) + }; + let inv_mask0 = !mask0; + let inv_mask1 = !mask1; + blit_rows!( + region.data, + shifted, + yu, + first_word, + two, + |d, s| (d & inv_mask0) | (!(d ^ s) & mask0), + |d, s| (d & inv_mask1) | (!(d ^ s) & mask1) + ); + } + } + } else { + let pattern = &pattern_dict.patterns[pattern_index]; + region.combine(pattern, x, y, op); + } + + coords.advance_col(); + } + coords.advance_row(); + } + + Ok(()) +} diff --git a/vendor/hayro-jbig2/src/decode/mod.rs b/vendor/hayro-jbig2/src/decode/mod.rs new file mode 100644 index 000000000..da9e0eccf --- /dev/null +++ b/vendor/hayro-jbig2/src/decode/mod.rs @@ -0,0 +1,236 @@ +//! Region and dictionary segment parsing and decoding. + +pub(crate) mod generic; +pub(crate) mod generic_refinement; +pub(crate) mod halftone; +pub(crate) mod pattern; +pub(crate) mod symbol; +pub(crate) mod text; + +use crate::bitmap::Bitmap; +use crate::decode::RefinementTemplate::{Template0, Template1}; +use crate::error::{ParseError, RegionError, Result, bail, err}; +use crate::reader::Reader; +use alloc::vec; +use alloc::vec::Vec; + +/// A decoded region bitmap with its combination operator. +#[derive(Debug, Clone)] +pub(crate) struct RegionBitmap { + /// The decoded bitmap. + pub(crate) bitmap: Bitmap, + /// How to combine this region with the page bitmap. + pub(crate) combination_operator: CombinationOperator, +} + +/// "These operators describe how the segment's bitmap is to be combined with +/// the page bitmap." (7.4.1.5) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub(crate) enum CombinationOperator { + /// 0 OR + #[default] + Or, + /// 1 AND + And, + /// 2 XOR + Xor, + /// 3 XNOR + Xnor, + /// 4 REPLACE + Replace, +} + +// Not really a default, just used as a dummy placeholder. + +impl CombinationOperator { + pub(crate) fn from_value(value: u8) -> Result { + match value & 0x07 { + 0 => Ok(Self::Or), + 1 => Ok(Self::And), + 2 => Ok(Self::Xor), + 3 => Ok(Self::Xnor), + 4 => Ok(Self::Replace), + _ => err!(RegionError::InvalidCombinationOperator), + } + } +} + +/// Template used for arithmetic of generic regions. +/// +/// - Generic regions: `GBTEMPLATE` (7.4.6.2) +/// - Symbol dictionaries: `SDTEMPLATE` (7.4.2.1.1) +/// - Pattern dictionaries: `HDTEMPLATE` (7.4.4.1.1) +/// - Halftone regions: `HTEMPLATE` (7.4.5.1.1) +/// - Gray-scale images: `GSTEMPLATE` (Annex C) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum Template { + /// Template 0 + /// + /// Context bits: 16 (Figure 3). + Template0 = 0, + /// Template 1 + /// + /// Context bits: 13 (Figure 4). + Template1 = 1, + /// Template 2 + /// + /// Context bits: 10 (Figure 5). + Template2 = 2, + /// Template 3 + /// + /// Context bits: 10 (Figure 6). + Template3 = 3, +} + +impl Template { + pub(crate) fn from_byte(value: u8) -> Self { + match value & 0x03 { + 0 => Self::Template0, + 1 => Self::Template1, + 2 => Self::Template2, + 3 => Self::Template3, + _ => unreachable!(), + } + } + + /// Number of context bits for this template (6.2.5.3). + pub(crate) fn context_bits(self) -> usize { + match self { + Self::Template0 => 16, + Self::Template1 => 13, + Self::Template2 | Self::Template3 => 10, + } + } + + /// Number of adaptive template pixels (6.2.5.3). + pub(crate) fn adaptive_template_pixels(&self) -> u8 { + match self { + Self::Template0 => 4, + Self::Template1 | Self::Template2 | Self::Template3 => 1, + } + } +} + +/// Parsed region segment information field (7.4.1). +/// +/// "A region segment information field contains the following subfields, as +/// shown in Figure 30: +/// - Region segment bitmap width – see 7.4.1.1. +/// - Region segment bitmap height – see 7.4.1.2. +/// - Region segment bitmap X location – see 7.4.1.3. +/// - Region segment bitmap Y location – see 7.4.1.4. +/// - Region segment flags – see 7.4.1.5." (7.4.1) +#[derive(Debug, Clone)] +pub(crate) struct RegionSegmentInfo { + /// "This four-byte field gives the width in pixels of the bitmap encoded + /// in this segment." (7.4.1.1) + pub(crate) width: u32, + /// "This four-byte field gives the height in pixels of the bitmap encoded + /// in this segment." (7.4.1.2) + pub(crate) height: u32, + /// "This four-byte field gives the horizontal offset in pixels of the bitmap + /// encoded in this segment relative to the page bitmap." (7.4.1.3) + pub(crate) x_location: u32, + /// "This four-byte field gives the vertical offset in pixels of the bitmap + /// encoded in this segment relative to the page bitmap." (7.4.1.4) + pub(crate) y_location: u32, + /// "Bits 0-2: External combination operator." (7.4.1.5) + pub(crate) combination_operator: CombinationOperator, + /// "Bit 3: Colour extension flag (COLEXTFLAG). This field specifies whether + /// the region segment is extended to represent coloured bitmap." (7.4.1.5) + pub(crate) _colour_extension: bool, +} + +/// Parse the region segment information field (7.4.1). +pub(crate) fn parse_region_segment_info(reader: &mut Reader<'_>) -> Result { + // 7.4.1.1: Region segment bitmap width + let width = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + // 7.4.1.2: Region segment bitmap height + let height = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + // 7.4.1.3: Region segment bitmap X location + let x_location = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + // 7.4.1.: Region segment bitmap Y location + let y_location = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + + // 7.4.1.5: Region segment flags + let flags = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + + // "Bits 0-2: External combination operator." + let combination_operator = CombinationOperator::from_value(flags)?; + + // "Bit 3: Colour extension flag (COLEXTFLAG)." + let colour_extension = flags & 0x08 != 0; + + // "Bits 4-7: Reserved; must be 0." + if flags & 0xF0 != 0 { + bail!(RegionError::InvalidCombinationOperator); + } + + Ok(RegionSegmentInfo { + width, + height, + x_location, + y_location, + combination_operator, + _colour_extension: colour_extension, + }) +} + +/// Adaptive template pixel position for generic and refinement regions (6.2.5.4, Figure 7). +#[derive(Debug, Clone, Copy, Default)] +pub(crate) struct AdaptiveTemplatePixel { + pub(crate) x: i8, + pub(crate) y: i8, +} + +/// Parse refinement adaptive template pixels (used by symbol dictionary and text region). +/// +/// Used for: +/// - Symbol dictionary refinement AT flags (7.4.2.1.3): SDRATX1/SDRATY1, SDRATX2/SDRATY2 +/// - Text region refinement AT flags (7.4.3.1.3): SBRATX1/SBRATY1, SBRATX2/SBRATY2 +/// - Generic refinement region AT flags (7.4.7.3): GRATX1/GRATY1, GRATX2/GRATY2 +pub(crate) fn parse_refinement_at_pixels( + reader: &mut Reader<'_>, +) -> Result> { + let x1 = reader.read_byte().ok_or(ParseError::UnexpectedEof)? as i8; + let y1 = reader.read_byte().ok_or(ParseError::UnexpectedEof)? as i8; + + let x2 = reader.read_byte().ok_or(ParseError::UnexpectedEof)? as i8; + let y2 = reader.read_byte().ok_or(ParseError::UnexpectedEof)? as i8; + + Ok(vec![ + AdaptiveTemplatePixel { x: x1, y: y1 }, + AdaptiveTemplatePixel { x: x2, y: y2 }, + ]) +} + +/// Template used for refinement arithmetic coding (7.4.7.2). +/// +/// - Generic refinement regions: `GRTEMPLATE` (7.4.7.2) +/// - Symbol dictionaries: `SDRTEMPLATE` (7.4.2.1.1) +/// - Text regions: `SBRTEMPLATE` (7.4.3.1.1) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum RefinementTemplate { + /// Template 0: 13 pixels (6.3.5.3, Figure 12) + Template0 = 0, + /// Template 1: 10 pixels (6.3.5.3, Figure 13) + Template1 = 1, +} + +impl RefinementTemplate { + pub(crate) fn from_byte(value: u8) -> Self { + if value & 0x01 == 0 { + Template0 + } else { + Template1 + } + } + + /// Number of context bits for this template (6.3.5.3). + pub(crate) fn context_bits(&self) -> usize { + match self { + Self::Template0 => 13, + Self::Template1 => 10, + } + } +} diff --git a/vendor/hayro-jbig2/src/decode/pattern.rs b/vendor/hayro-jbig2/src/decode/pattern.rs new file mode 100644 index 000000000..2c15d70b8 --- /dev/null +++ b/vendor/hayro-jbig2/src/decode/pattern.rs @@ -0,0 +1,221 @@ +//! Pattern dictionary segment parsing and decoding (7.4.4, 6.7). + +use super::{AdaptiveTemplatePixel, Template, generic}; +use crate::ScratchBuffers; +use crate::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext}; +use crate::bitmap::{Bitmap, WORD_BITS, Word}; +use crate::error::{OverflowError, ParseError, Result}; +use crate::reader::Reader; +use alloc::vec::Vec; + +/// Decode a pattern dictionary segment (7.4.4.2, 6.7). +pub(crate) fn decode( + header: &PatternDictionaryHeader<'_>, + scratch: &mut ScratchBuffers, +) -> Result { + let pattern_width = header.pattern_width as u32; + let pattern_height = header.pattern_height as u32; + let num_patterns = header + .num_patterns + .checked_add(1) + .ok_or(OverflowError::BitmapDimension)?; + + // "1) Create a bitmap B_HDC. The height of this bitmap is HDPH. The width + // of the bitmap is (GRAYMAX + 1) × HDPW. This bitmap contains all the + // patterns concatenated left to right." (6.7.5) + let collective_width = num_patterns + .checked_mul(pattern_width) + .ok_or(OverflowError::BitmapDimension)?; + + let mut collective_bitmap = Bitmap::new(collective_width, pattern_height)?; + + // "2) Decode the collective bitmap using a generic region decoding procedure + // as described in 6.2." (6.7.5) + if header.mmr { + let _ = generic::decode_bitmap_mmr(&mut collective_bitmap, header.data)?; + } else { + let at_pixels = match header.template { + Template::Template0 => [ + AdaptiveTemplatePixel { + x: -(pattern_width as i8), + y: 0, + }, + AdaptiveTemplatePixel { x: -3, y: -1 }, + AdaptiveTemplatePixel { x: 2, y: -2 }, + AdaptiveTemplatePixel { x: -2, y: -2 }, + ], + Template::Template1 | Template::Template2 | Template::Template3 => [ + AdaptiveTemplatePixel { + x: -(pattern_width as i8), + y: 0, + }, + // Unused. + AdaptiveTemplatePixel::default(), + AdaptiveTemplatePixel::default(), + AdaptiveTemplatePixel::default(), + ], + }; + + let mut decoder = ArithmeticDecoder::new(header.data); + scratch.contexts.clear(); + scratch.contexts.resize( + 1 << header.template.context_bits(), + ArithmeticDecoderContext::default(), + ); + + generic::decode_bitmap_arithmetic_coding( + &mut collective_bitmap, + &mut decoder, + &mut scratch.contexts, + header.template, + false, + &at_pixels, + )?; + } + + // "3) Set: GRAY = 0" (6.7.5) + // "4) While GRAY ≤ GRAYMAX:" (6.7.5) + let mut patterns = Vec::with_capacity(num_patterns as usize); + + let src_stride = collective_bitmap.stride; + + for gray in 0..num_patterns { + // "a) Let the subimage of B_HDC consisting of HPH rows and columns + // HDPW × GRAY through HDPW × (GRAY + 1) − 1 be denoted B_P. Set: + // HDPATS[GRAY] = B_P" (6.7.5)" + let start_x = gray * pattern_width; + let mut pattern = Bitmap::new(pattern_width, pattern_height)?; + + let src_word_idx = start_x / WORD_BITS; + let src_bit_offset = start_x % WORD_BITS; + let dst_stride = pattern.stride; + + for y in 0..pattern_height { + let src_row = (y * src_stride + src_word_idx) as usize; + let dst_row = (y * dst_stride) as usize; + + for w in 0..dst_stride { + let s1 = collective_bitmap.data[src_row + w as usize]; + let s2 = collective_bitmap + .data + .get(src_row + w as usize + 1) + .copied() + .unwrap_or(0); + + let word: Word = if src_bit_offset == 0 { + s1 + } else { + (s1 << src_bit_offset) | (s2 >> (WORD_BITS - src_bit_offset)) + }; + + pattern.data[dst_row + w as usize] = word; + } + + // Clear trailing bits in the last word beyond `pattern_width`. + let trailing = pattern_width % WORD_BITS; + if trailing != 0 { + let last = dst_row + (dst_stride - 1) as usize; + pattern.data[last] &= !0 << (WORD_BITS - trailing); + } + } + + patterns.push(pattern); + } + + // It turns out that when rendering patterns, the `Bitmap::combine` operation + // can often be a huge bottleneck. This is because we need to do a lot of + // fiddling to compose the patterns in the right position. And since + // if a page uses such patterns, there's going to be a lot of them, + // this takes up a lot of time. + // + // The key insight for our optimization is that patterns are usually + // very small. Therefore, we precompute a new version of each pattern + // for all possible 32-bit alignments, which later on allows us to + // very easily blit it into the backdrop, significantly speeding up + // the whole process. + let shifted_patterns = if pattern_width <= WORD_BITS && pattern_height <= WORD_BITS { + let h = pattern_height as usize; + let mut data = Vec::with_capacity(patterns.len() * 32 * h * 2); + for pattern in &patterns { + for k in 0..32_u32 { + for y in 0..h { + let word = pattern.data[y]; + if k == 0 { + data.push(word); + data.push(0); + } else { + data.push(word >> k); + data.push(word << (WORD_BITS - k)); + } + } + } + } + data + } else { + Vec::new() + }; + + Ok(PatternDictionary { + patterns, + pattern_width, + pattern_height, + shifted_patterns, + }) +} + +/// A decoded pattern dictionary. +#[derive(Debug, Clone)] +pub(crate) struct PatternDictionary { + pub(crate) patterns: Vec, + pub(crate) pattern_width: u32, + pub(crate) pattern_height: u32, + shifted_patterns: Vec, +} + +impl PatternDictionary { + #[inline(always)] + pub(crate) fn shifted_pattern(&self, pattern_idx: usize, bit_offset: u32) -> Option<&[Word]> { + let h = self.pattern_height as usize; + let len = h * 2; + let start = (pattern_idx * 32 + bit_offset as usize) * len; + self.shifted_patterns.get(start..start + len) + } +} + +/// Parsed pattern dictionary segment header (7.4.4.1). +#[derive(Debug, Clone)] +pub(crate) struct PatternDictionaryHeader<'a> { + pub(crate) mmr: bool, + pub(crate) template: Template, + /// `HDPW` + pub(crate) pattern_width: u8, + /// `HDPH` + pub(crate) pattern_height: u8, + /// `GRAYMAX` + pub(crate) num_patterns: u32, + pub(crate) data: &'a [u8], +} + +/// Parse a pattern dictionary segment header (7.4.4.1). +pub(crate) fn parse<'a>(reader: &mut Reader<'a>) -> Result> { + let flags_byte = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + let mmr = flags_byte & 0x01 != 0; + let template = Template::from_byte(flags_byte >> 1); + let pattern_width = reader + .read_nonzero_byte() + .ok_or(ParseError::UnexpectedEof)?; + let pattern_height = reader + .read_nonzero_byte() + .ok_or(ParseError::UnexpectedEof)?; + let num_patterns = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + let data = reader.tail().ok_or(ParseError::UnexpectedEof)?; + + Ok(PatternDictionaryHeader { + mmr, + template, + pattern_width, + pattern_height, + num_patterns, + data, + }) +} diff --git a/vendor/hayro-jbig2/src/decode/symbol.rs b/vendor/hayro-jbig2/src/decode/symbol.rs new file mode 100644 index 000000000..90cfd3b8a --- /dev/null +++ b/vendor/hayro-jbig2/src/decode/symbol.rs @@ -0,0 +1,844 @@ +//! Symbol dictionary segment parsing and decoding (7.4.2, 6.5). + +use alloc::vec; +use alloc::vec::Vec; + +use crate::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext}; +use crate::bitmap::{Bitmap, WORD_BITS}; +use crate::decode::generic::{decode_bitmap_mmr, parse_adaptive_template_pixels}; +use crate::decode::text::{ + DecodeContext, ReferenceCorner, TextRegionContexts, TextRegionFlags, TextRegionHeader, + TextRegionHuffmanFlags, decode_with, +}; +use crate::decode::{ + AdaptiveTemplatePixel, CombinationOperator, RefinementTemplate, RegionSegmentInfo, Template, + parse_refinement_at_pixels, +}; +use crate::decode::{generic, generic_refinement}; +use crate::error::{ + DecodeError, HuffmanError, OverflowError, ParseError, Result, SymbolError, bail, +}; +use crate::huffman_table::{HuffmanTable, StandardHuffmanTables}; +use crate::integer_decoder::IntegerDecoder; +use crate::reader::Reader; + +const MAX_SYMBOLS: u32 = u16::MAX as u32; + +/// PrintCraft patch: the most symbol pixels one dictionary may decode (2^26). Symbols are +/// decoded pixel by pixel, and up to 65535 of them of up to 65535 × 65535 each kept a fuzzed +/// file decoding for minutes; real dictionaries hold about a million pixels. +const MAX_DICTIONARY_PIXELS: u64 = 1 << 26; + +/// Decode a symbol dictionary segment (7.4.2, 6.5). +pub(crate) fn decode( + header: &SymbolDictionaryHeader<'_>, + input_symbols: &[&Bitmap], + referred_tables: &[HuffmanTable], + standard_tables: &StandardHuffmanTables, + input_contexts: Option<&RetainedContexts>, +) -> Result { + let num_new_symbols = header.num_new_symbols; + + let mut ctx = SymbolDecodeContext { + a_ctx: ArithmeticContext::new(header.data, header, input_contexts)?, + h_ctx: HuffmanContext::new(header.data, header, referred_tables, standard_tables)?, + symbols: Symbols::new(input_symbols, num_new_symbols as usize), + symbol_widths: Vec::with_capacity(num_new_symbols as usize), + height_class_first_symbol: 0, + symbols_decoded_count: 0, + total_width: 0, + height_class_height: 0, + pixels: 0, + header, + standard_tables, + }; + + let read_height_class_delta = |ctx: &mut SymbolDecodeContext<'_>| { + if ctx.header.flags.use_huffman { + ctx.h_ctx + .height_class_delta_table + .decode(&mut ctx.h_ctx.reader) + } else { + Ok(ctx + .a_ctx + .height_class_delta_decoder + .decode(&mut ctx.a_ctx.decoder)) + } + }; + + let read_symbol_width_delta = |ctx: &mut SymbolDecodeContext<'_>| { + if ctx.header.flags.use_huffman { + ctx.h_ctx + .symbol_width_delta_table + .decode(&mut ctx.h_ctx.reader) + } else { + Ok(ctx + .a_ctx + .symbol_width_delta_decoder + .decode(&mut ctx.a_ctx.decoder)) + } + }; + + while ctx.symbols_decoded_count < num_new_symbols { + let height_class_delta = + read_height_class_delta(&mut ctx)?.ok_or(SymbolError::UnexpectedOob)?; + + ctx.height_class_height = ctx + .height_class_height + .checked_add_signed(height_class_delta) + .ok_or(OverflowError::BitmapDimension)?; + + let mut symbol_width: u32 = 0; + ctx.total_width = 0; + ctx.height_class_first_symbol = ctx.symbols_decoded_count; + + // "If the result of this decoding is OOB then all the symbols + // in this height class have been decoded." + while let Some(width_delta) = read_symbol_width_delta(&mut ctx)? { + // Prevent infinite loop for invalid files. + if ctx.symbols_decoded_count >= num_new_symbols { + bail!(SymbolError::TooManySymbols); + } + + symbol_width = symbol_width + .checked_add_signed(width_delta) + .ok_or(OverflowError::BitmapDimension)?; + ctx.total_width = ctx + .total_width + .checked_add(symbol_width) + .ok_or(OverflowError::BitmapDimension)?; + // PrintCraft patch: see `MAX_DICTIONARY_PIXELS`. + ctx.pixels = ctx + .pixels + .saturating_add(u64::from(symbol_width) * u64::from(ctx.height_class_height)); + if ctx.pixels > MAX_DICTIONARY_PIXELS { + bail!(OverflowError::BitmapDimension); + } + + match (ctx.header.flags.use_huffman, ctx.header.flags.use_refagg) { + (false, false) => { + // Decode a single symbol using a simple generic decoding procedure, + // described in 6.5.8.1. + let mut region = Bitmap::new(symbol_width, ctx.height_class_height)?; + generic::decode_bitmap_arithmetic_coding( + &mut region, + &mut ctx.a_ctx.decoder, + &mut ctx.a_ctx.generic_region_contexts, + ctx.header.flags.template, + false, + &ctx.header.adaptive_template_pixels, + )?; + + ctx.symbols.new.push(region); + } + (true, false) => { + // Decode a single symbol width. We don't actually decode the symbols + // yet, those will be decoded later on from the collective bitmap. + ctx.symbol_widths.push(symbol_width); + } + (_, true) => { + // Also decode a single symbol, but using refinement-aggregation. + // In this case, we can have both, huffman and arithmetic coding. + let symbol = decode_refinement_aggregation_bitmap(&mut ctx, symbol_width)?; + + ctx.symbols.new.push(symbol); + } + } + + ctx.symbols_decoded_count += 1; + } + + if ctx.header.flags.use_huffman && !ctx.header.flags.use_refagg { + // In case we have huffman coding and no refinement-aggregation, we use + // the previously decoded symbol widths to decode the collective bitmap + // and extract the individual symbols from that bitmap. + decode_height_class_collective_bitmap(&mut ctx)?; + } + } + + let exported = export_symbols(&mut ctx)?; + + let retained_contexts = if ctx.header.flags.bitmap_context_retained { + Some(RetainedContexts { + generic_region: ctx.a_ctx.generic_region_contexts, + refinement_region: ctx.a_ctx.refinement_region_contexts, + }) + } else { + None + }; + + Ok(SymbolDictionary { + exported_symbols: exported, + retained_contexts, + }) +} + +#[derive(Debug, Clone)] +pub(crate) struct RetainedContexts { + pub(crate) generic_region: Vec, + pub(crate) refinement_region: Vec, +} + +/// A decoded symbol dictionary segment. +#[derive(Debug, Clone)] +pub(crate) struct SymbolDictionary { + pub(crate) exported_symbols: Vec, + pub(crate) retained_contexts: Option, +} + +/// Decode a symbol bitmap using refinement/aggregate coding (6.5.8.2). +fn decode_refinement_aggregation_bitmap( + ctx: &mut SymbolDecodeContext<'_>, + symbol_width: u32, +) -> Result { + // 6.5.8.2.1 Number of symbol instances in the aggregation. + let aggregation_instance_count = if ctx.header.flags.use_huffman { + ctx.h_ctx + .aggregation_instance_count_table + .decode(&mut ctx.h_ctx.reader)? + } else { + ctx.a_ctx + .aggregation_instance_count_decoder + .decode(&mut ctx.a_ctx.decoder) + } + .ok_or(DecodeError::Symbol(SymbolError::UnexpectedOob))?; + + if aggregation_instance_count == 1 { + decode_refinement_bitmap(ctx, symbol_width) + } else if aggregation_instance_count > 1 { + decode_aggregation_bitmap(ctx, symbol_width, aggregation_instance_count as u32) + } else { + Err(DecodeError::Symbol(SymbolError::Invalid)) + } +} + +/// Decode a refinement bitmap symbol with a single aggregate (6.5.8.2). +fn decode_refinement_bitmap( + ctx: &mut SymbolDecodeContext<'_>, + symbol_width: u32, +) -> Result { + let use_huffman = ctx.header.flags.use_huffman; + let mut symbol_code_length = 32 - (ctx.total_symbols() - 1).leading_zeros(); + + let (symbol_id, refinement_x_offset, refinement_y_offset) = if use_huffman { + // See 6.5.8.2.3, the value should be at least 1 if we use huffman coding. + symbol_code_length = symbol_code_length.max(1); + + let symbol_id = ctx + .h_ctx + .reader + .read_bits(symbol_code_length as u8) + .ok_or(ParseError::UnexpectedEof)? as usize; + + let refinement_x_offset = ctx + .standard_tables + .table_o() + .decode(&mut ctx.h_ctx.reader)? + .ok_or(HuffmanError::UnexpectedOob)?; + + let refinement_y_offset = ctx + .standard_tables + .table_o() + .decode(&mut ctx.h_ctx.reader)? + .ok_or(HuffmanError::UnexpectedOob)?; + + (symbol_id, refinement_x_offset, refinement_y_offset) + } else { + // Note that the contexts should be reused across multiple + // bitmaps in the same symbol dictionary. + let contexts = ctx + .a_ctx + .text_region_contexts + .get_or_insert_with(|| TextRegionContexts::new(symbol_code_length)); + + let symbol_id = contexts.iaid.decode(&mut ctx.a_ctx.decoder) as usize; + + let refinement_x_offset = contexts + .iardx + .decode(&mut ctx.a_ctx.decoder) + .ok_or(SymbolError::UnexpectedOob)?; + + let refinement_y_offset = contexts + .iardy + .decode(&mut ctx.a_ctx.decoder) + .ok_or(SymbolError::UnexpectedOob)?; + + (symbol_id, refinement_x_offset, refinement_y_offset) + }; + + let reference_region = ctx.symbols.get(symbol_id).ok_or(SymbolError::OutOfRange)?; + let mut region = Bitmap::new(symbol_width, ctx.height_class_height)?; + + if use_huffman { + let bitmap_size = ctx + .standard_tables + .table_a() + .decode(&mut ctx.h_ctx.reader)? + .ok_or(HuffmanError::UnexpectedOob)? as usize; + ctx.h_ctx.reader.align(); + + let bitmap_data = ctx + .h_ctx + .reader + .read_bytes(bitmap_size) + .ok_or(ParseError::UnexpectedEof)?; + + let mut bitmap_decoder = ArithmeticDecoder::new(bitmap_data); + + generic_refinement::decode_bitmap( + &mut bitmap_decoder, + &mut ctx.a_ctx.refinement_region_contexts, + &mut region, + reference_region, + refinement_x_offset, + refinement_y_offset, + ctx.header.flags.refinement_template, + &ctx.header.refinement_at_pixels, + false, + )?; + } else { + generic_refinement::decode_bitmap( + &mut ctx.a_ctx.decoder, + &mut ctx.a_ctx.refinement_region_contexts, + &mut region, + reference_region, + refinement_x_offset, + refinement_y_offset, + ctx.header.flags.refinement_template, + &ctx.header.refinement_at_pixels, + false, + )?; + } + + Ok(region) +} + +/// Decode an aggregation bitmap with more than one aggregate (6.5.8.2). +fn decode_aggregation_bitmap( + ctx: &mut SymbolDecodeContext<'_>, + symbol_width: u32, + aggregation_instance_count: u32, +) -> Result { + let use_huffman = ctx.header.flags.use_huffman; + + // Concatenate input and new symbols. + let mut all_symbols: Vec<&Bitmap> = + Vec::with_capacity(ctx.symbols.input.len() + ctx.symbols.new.len()); + all_symbols.extend(ctx.symbols.input.iter().copied()); + for sym in &ctx.symbols.new { + all_symbols.push(sym); + } + + // Set all parameters according to Table 17. + + let symbol_code_length = 32 - (ctx.total_symbols() - 1).leading_zeros(); + + let symbol_id_table = if use_huffman { + Some(HuffmanTable::build_uniform( + ctx.total_symbols(), + symbol_code_length, + )?) + } else { + None + }; + + let huffman_flags = if use_huffman { + Some(TextRegionHuffmanFlags { + first_s_table: 0, + delta_s_table: 0, + delta_t_table: 0, + refinement_width_table: 1, + refinement_height_table: 1, + refinement_y_table: 1, + refinement_x_table: 1, + refinement_size_table: 0, + }) + } else { + None + }; + + let header = TextRegionHeader { + region_info: RegionSegmentInfo { + width: symbol_width, + height: ctx.height_class_height, + x_location: 0, + y_location: 0, + combination_operator: CombinationOperator::Or, + _colour_extension: false, + }, + flags: TextRegionFlags { + use_huffman, + use_refinement: true, + log_strip_size: 0, + reference_corner: ReferenceCorner::TopLeft, + transposed: false, + combination_operator: CombinationOperator::Or, + default_pixel: false, + delta_s_offset: 0, + refinement_template: ctx.header.flags.refinement_template, + }, + huffman_flags, + refinement_at_pixels: ctx.header.refinement_at_pixels.clone(), + num_instances: aggregation_instance_count, + symbol_id_table, + data: &[], // Data comes from parent decoder context, not the header. + }; + + let decode_ctx = if use_huffman { + DecodeContext::new_huffman(&mut ctx.h_ctx.reader, &header, &[], ctx.standard_tables)? + } else { + let contexts = ctx + .a_ctx + .text_region_contexts + .get_or_insert_with(|| TextRegionContexts::new(symbol_code_length)); + + DecodeContext::new_arithmetic( + &mut ctx.a_ctx.decoder, + contexts, + &mut ctx.a_ctx.refinement_region_contexts, + ) + }; + + let mut bitmap = Bitmap::new_with( + header.region_info.width, + header.region_info.height, + header.region_info.x_location, + header.region_info.y_location, + header.flags.default_pixel, + )?; + decode_with(decode_ctx, &all_symbols, &header, &mut bitmap)?; + Ok(bitmap) +} + +struct Symbols<'a> { + input: &'a [&'a Bitmap], + new: Vec, +} + +impl<'a> Symbols<'a> { + fn new(input: &'a [&'a Bitmap], capacity: usize) -> Self { + Self { + input, + new: Vec::with_capacity(capacity), + } + } + + fn input_count(&self) -> u32 { + self.input.len() as u32 + } + + fn get(&self, index: usize) -> Option<&Bitmap> { + if index < self.input.len() { + Some(self.input[index]) + } else { + self.new.get(index - self.input.len()) + } + } +} + +struct SymbolDecodeContext<'a> { + header: &'a SymbolDictionaryHeader<'a>, + a_ctx: ArithmeticContext<'a>, + h_ctx: HuffmanContext<'a>, + symbols: Symbols<'a>, + standard_tables: &'a StandardHuffmanTables, + symbol_widths: Vec, + height_class_first_symbol: u32, + symbols_decoded_count: u32, + total_width: u32, + height_class_height: u32, + /// PrintCraft patch: symbol pixels decoded so far (see `MAX_DICTIONARY_PIXELS`). + pixels: u64, +} + +impl SymbolDecodeContext<'_> { + fn total_symbols(&self) -> u32 { + self.symbols.input_count() + self.header.num_new_symbols + } +} + +struct ArithmeticContext<'a> { + decoder: ArithmeticDecoder<'a>, + /// `IADH` + height_class_delta_decoder: IntegerDecoder, + /// `IADW` + symbol_width_delta_decoder: IntegerDecoder, + /// `IAEX` + export_run_length_decoder: IntegerDecoder, + /// `IAAI` + aggregation_instance_count_decoder: IntegerDecoder, + generic_region_contexts: Vec, + refinement_region_contexts: Vec, + text_region_contexts: Option, +} + +impl<'a> ArithmeticContext<'a> { + fn new( + data: &'a [u8], + header: &SymbolDictionaryHeader<'_>, + input_contexts: Option<&RetainedContexts>, + ) -> Result { + let decoder = ArithmeticDecoder::new(data); + + let height_class_delta_decoder = IntegerDecoder::new(); + let symbol_width_delta_decoder = IntegerDecoder::new(); + let export_run_length_decoder = IntegerDecoder::new(); + let aggregation_instance_count_decoder = IntegerDecoder::new(); + + let (generic_region_contexts, refinement_region_contexts) = + if header.flags.bitmap_context_used { + let ctx = input_contexts.ok_or(SymbolError::Invalid)?; + (ctx.generic_region.clone(), ctx.refinement_region.clone()) + } else { + let template = header.flags.template; + let num_contexts = 1 << template.context_bits(); + let refinement_template = header.flags.refinement_template; + let num_refinement_contexts = 1 << refinement_template.context_bits(); + ( + vec![ArithmeticDecoderContext::default(); num_contexts], + vec![ArithmeticDecoderContext::default(); num_refinement_contexts], + ) + }; + + Ok(Self { + decoder, + height_class_delta_decoder, + symbol_width_delta_decoder, + export_run_length_decoder, + aggregation_instance_count_decoder, + generic_region_contexts, + refinement_region_contexts, + text_region_contexts: None, + }) + } +} + +struct HuffmanContext<'a> { + /// `SDHUFFDH` + height_class_delta_table: &'a HuffmanTable, + /// `SDHUFFDW` + symbol_width_delta_table: &'a HuffmanTable, + /// `SDHUFFBMSIZE` + collective_bitmap_size_table: &'a HuffmanTable, + /// `SDHUFFAGGINST` + aggregation_instance_count_table: &'a HuffmanTable, + export_run_length_table: &'a HuffmanTable, + reader: Reader<'a>, +} + +impl<'a> HuffmanContext<'a> { + fn new( + data: &'a [u8], + header: &SymbolDictionaryHeader<'_>, + referred_tables: &'a [HuffmanTable], + standard_tables: &'a StandardHuffmanTables, + ) -> Result { + let reader = Reader::new(data); + + let custom_count = [ + header.flags.delta_height_table == HuffmanTableSelection::UserSupplied, + header.flags.delta_width_table == HuffmanTableSelection::UserSupplied, + header.flags.collective_bitmap_size_table == HuffmanTableSelection::UserSupplied, + header.flags.aggregate_instance_table == HuffmanTableSelection::UserSupplied, + ] + .into_iter() + .filter(|x| *x) + .count(); + + if referred_tables.len() < custom_count { + bail!(HuffmanError::MissingTables); + } + + let mut custom_table_idx = 0; + let mut get_custom = || -> &HuffmanTable { + let table = &referred_tables[custom_table_idx]; + custom_table_idx += 1; + table + }; + + // Select Huffman tables based on flags (7.4.2.1.6). + let mut get_table = |selection: HuffmanTableSelection| -> &HuffmanTable { + match selection { + HuffmanTableSelection::TableB1 => standard_tables.table_a(), + HuffmanTableSelection::TableB2 => standard_tables.table_b(), + HuffmanTableSelection::TableB3 => standard_tables.table_c(), + HuffmanTableSelection::TableB4 => standard_tables.table_d(), + HuffmanTableSelection::TableB5 => standard_tables.table_e(), + HuffmanTableSelection::UserSupplied => get_custom(), + } + }; + + let height_class_delta_table = get_table(header.flags.delta_height_table); + let symbol_width_delta_table = get_table(header.flags.delta_width_table); + let collective_bitmap_size_table = get_table(header.flags.collective_bitmap_size_table); + let aggregation_instance_count_table = get_table(header.flags.aggregate_instance_table); + let export_run_length_table = get_table(HuffmanTableSelection::TableB1); + + Ok(Self { + reader, + height_class_delta_table, + symbol_width_delta_table, + collective_bitmap_size_table, + aggregation_instance_count_table, + export_run_length_table, + }) + } +} + +/// Decode a height class collective bitmap (6.5.9). +fn decode_height_class_collective_bitmap(ctx: &mut SymbolDecodeContext<'_>) -> Result<()> { + let bitmap_size = ctx + .h_ctx + .collective_bitmap_size_table + .decode(&mut ctx.h_ctx.reader)? + .ok_or(HuffmanError::UnexpectedOob)?; + let bitmap_size = u32::try_from(bitmap_size).map_err(|_| OverflowError::BitmapDimension)?; + + ctx.h_ctx.reader.align(); + + let collective_bitmap = if bitmap_size == 0 { + // Bitmap is stored uncompressed, so we basically just copy-paste + // each byte. Rows are padded to the byte boundary. + let row_bytes = ctx.total_width.div_ceil(8); + + let mut bitmap = Bitmap::new(ctx.total_width, ctx.height_class_height)?; + for y in 0..ctx.height_class_height { + for byte_x in 0..row_bytes { + let byte = ctx + .h_ctx + .reader + .read_byte() + .ok_or(ParseError::UnexpectedEof)?; + for bit in 0..8 { + let x = byte_x * 8 + bit; + if x < ctx.total_width { + let pixel = (byte >> (7 - bit)) & 1; + bitmap.set_pixel(x, y, pixel); + } + } + } + } + bitmap + } else { + // Otherwise, we need to use MMR decoding. + let bitmap_data = ctx + .h_ctx + .reader + .read_bytes(bitmap_size as usize) + .ok_or(ParseError::UnexpectedEof)?; + + let mut bitmap = Bitmap::new(ctx.total_width, ctx.height_class_height)?; + decode_bitmap_mmr(&mut bitmap, bitmap_data)?; + bitmap + }; + + // Finally, we simply chop up the collective bitmap into its constituent + // symbols. + let mut x_offset: u32 = 0; + for symbol_idx in ctx.height_class_first_symbol..ctx.symbols_decoded_count { + let symbol_width = ctx.symbol_widths[symbol_idx as usize]; + let mut symbol = Bitmap::new(symbol_width, ctx.height_class_height)?; + + for y in 0..ctx.height_class_height { + let src_bit_offset = x_offset % WORD_BITS; + let src_word_start = x_offset / WORD_BITS; + let dest_row = (y * symbol.stride) as usize; + + for w in 0..symbol.stride { + let sw = src_word_start + w; + let src1 = collective_bitmap.get_word(y, sw); + let word = if src_bit_offset == 0 { + src1 + } else { + let src2 = collective_bitmap.get_word(y, sw + 1); + (src1 << src_bit_offset) | (src2 >> (WORD_BITS - src_bit_offset)) + }; + symbol.data[dest_row + w as usize] = word; + } + + // Clear trailing bits in the last word beyond `symbol_width`. + let trailing = symbol_width % WORD_BITS; + if trailing != 0 && symbol.stride > 0 { + let last = dest_row + (symbol.stride - 1) as usize; + symbol.data[last] &= !0 << (WORD_BITS - trailing); + } + } + + ctx.symbols.new.push(symbol); + x_offset = x_offset + .checked_add(symbol_width) + .ok_or(OverflowError::Index)?; + } + + Ok(()) +} + +/// Exported symbols (6.5.10). +fn export_symbols(ctx: &mut SymbolDecodeContext<'_>) -> Result> { + let total_symbols = ctx.total_symbols(); + + let mut read_run_length = || -> Result { + let value = if ctx.header.flags.use_huffman { + ctx.h_ctx + .export_run_length_table + .decode(&mut ctx.h_ctx.reader)? + } else { + ctx.a_ctx + .export_run_length_decoder + .decode(&mut ctx.a_ctx.decoder) + } + .ok_or(HuffmanError::UnexpectedOob)?; + + u32::try_from(value).map_err(|_| HuffmanError::InvalidCode.into()) + }; + let mut exported = Vec::with_capacity(ctx.header.num_exported_symbols as usize); + let mut index: u32 = 0; + let mut should_export = false; + + while index < total_symbols { + let run_length = read_run_length()?; + + let end_index = index.checked_add(run_length).ok_or(OverflowError::Index)?; + if end_index > total_symbols { + bail!(SymbolError::OutOfRange); + } + + if should_export { + for symbol_idx in index..end_index { + let symbol = ctx + .symbols + .get(symbol_idx as usize) + .ok_or(SymbolError::OutOfRange)? + .clone(); + exported.push(symbol); + } + } + + index = end_index; + should_export = !should_export; + } + + if exported.len() != ctx.header.num_exported_symbols as usize { + bail!(SymbolError::NoSymbols); + } + + Ok(exported) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum HuffmanTableSelection { + TableB1, + TableB2, + TableB3, + TableB4, + TableB5, + UserSupplied, +} + +/// Parsed symbol dictionary segment flags (7.4.2.1.1). +#[derive(Debug, Clone)] +pub(crate) struct SymbolDictionaryFlags { + pub(crate) use_huffman: bool, + pub(crate) use_refagg: bool, + pub(crate) delta_height_table: HuffmanTableSelection, + pub(crate) delta_width_table: HuffmanTableSelection, + pub(crate) collective_bitmap_size_table: HuffmanTableSelection, + pub(crate) aggregate_instance_table: HuffmanTableSelection, + pub(crate) bitmap_context_used: bool, + pub(crate) bitmap_context_retained: bool, + pub(crate) template: Template, + pub(crate) refinement_template: RefinementTemplate, +} + +/// Parsed symbol dictionary segment header (7.4.2.1). +#[derive(Debug, Clone)] +pub(crate) struct SymbolDictionaryHeader<'a> { + pub(crate) flags: SymbolDictionaryFlags, + pub(crate) adaptive_template_pixels: [AdaptiveTemplatePixel; 4], + pub(crate) refinement_at_pixels: Vec, + pub(crate) num_exported_symbols: u32, + pub(crate) num_new_symbols: u32, + pub(crate) data: &'a [u8], +} + +/// Parse a symbol dictionary segment header (7.4.2.1). +pub(crate) fn parse<'a>(reader: &mut Reader<'a>) -> Result> { + let flags_word = reader.read_u16().ok_or(ParseError::UnexpectedEof)?; + let use_huffman = flags_word & 0x0001 != 0; + let use_refagg = flags_word & 0x0002 != 0; + + let delta_height_table = match (flags_word >> 2) & 0x03 { + 0 => HuffmanTableSelection::TableB4, + 1 => HuffmanTableSelection::TableB5, + 3 => HuffmanTableSelection::UserSupplied, + _ => bail!(HuffmanError::InvalidSelection), + }; + + let delta_width_table = match (flags_word >> 4) & 0x03 { + 0 => HuffmanTableSelection::TableB2, + 1 => HuffmanTableSelection::TableB3, + 3 => HuffmanTableSelection::UserSupplied, + _ => bail!(HuffmanError::InvalidSelection), + }; + + let collective_bitmap_size_table = if flags_word & 0x0040 != 0 { + HuffmanTableSelection::UserSupplied + } else { + HuffmanTableSelection::TableB1 + }; + + let aggregate_instance_table = if flags_word & 0x0080 != 0 { + HuffmanTableSelection::UserSupplied + } else { + HuffmanTableSelection::TableB1 + }; + + let bitmap_context_used = flags_word & 0x0100 != 0; + let bitmap_context_retained = flags_word & 0x0200 != 0; + let template = Template::from_byte((flags_word >> 10) as u8); + let refinement_template = RefinementTemplate::from_byte((flags_word >> 12) as u8); + + let flags = SymbolDictionaryFlags { + use_huffman, + use_refagg, + delta_height_table, + delta_width_table, + collective_bitmap_size_table, + aggregate_instance_table, + bitmap_context_used, + bitmap_context_retained, + template, + refinement_template, + }; + + let at_pixels = if !use_huffman { + parse_adaptive_template_pixels(reader, template, false)? + } else { + // Unused. + [AdaptiveTemplatePixel::default(); 4] + }; + + let refinement_at_pixels = if use_refagg && refinement_template == RefinementTemplate::Template0 + { + parse_refinement_at_pixels(reader)? + } else { + Vec::new() + }; + let num_exported_symbols = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + let num_new_symbols = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + + if num_exported_symbols.saturating_add(num_new_symbols) > MAX_SYMBOLS { + bail!(OverflowError::SymbolCount) + } + + let data = reader.tail().ok_or(ParseError::UnexpectedEof)?; + + Ok(SymbolDictionaryHeader { + flags, + adaptive_template_pixels: at_pixels, + refinement_at_pixels, + num_exported_symbols, + num_new_symbols, + data, + }) +} diff --git a/vendor/hayro-jbig2/src/decode/text.rs b/vendor/hayro-jbig2/src/decode/text.rs new file mode 100644 index 000000000..bbc2ac55c --- /dev/null +++ b/vendor/hayro-jbig2/src/decode/text.rs @@ -0,0 +1,903 @@ +//! Text region segment parsing and decoding (7.4.3, 6.4). + +use alloc::vec; +use alloc::vec::Vec; +use core::iter; + +use super::{ + AdaptiveTemplatePixel, CombinationOperator, RefinementTemplate, RegionSegmentInfo, + parse_refinement_at_pixels, parse_region_segment_info, +}; +use super::{RegionBitmap, generic_refinement}; +use crate::ScratchBuffers; +use crate::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext}; +use crate::bitmap::Bitmap; +use crate::error::{HuffmanError, OverflowError, ParseError, Result, SymbolError, bail}; +use crate::huffman_table::{HuffmanTable, StandardHuffmanTables, TableLine}; +use crate::integer_decoder::IntegerDecoder; +use crate::reader::Reader; +use crate::symbol_id_decoder::SymbolIdDecoder; + +/// Decode a text region segment (6.4). +pub(crate) fn decode( + header: &TextRegionHeader<'_>, + symbols: &[&Bitmap], + referred_tables: &[HuffmanTable], + standard_tables: &StandardHuffmanTables, + scratch: &mut ScratchBuffers, +) -> Result { + let mut bitmap = Bitmap::new_with( + header.region_info.width, + header.region_info.height, + header.region_info.x_location, + header.region_info.y_location, + header.flags.default_pixel, + )?; + + decode_into( + header, + symbols, + referred_tables, + standard_tables, + &mut bitmap, + scratch, + )?; + + Ok(RegionBitmap { + bitmap, + combination_operator: header.region_info.combination_operator, + }) +} + +pub(crate) fn decode_into( + header: &TextRegionHeader<'_>, + symbols: &[&Bitmap], + referred_tables: &[HuffmanTable], + standard_tables: &StandardHuffmanTables, + bitmap: &mut Bitmap, + scratch: &mut ScratchBuffers, +) -> Result<()> { + if header.flags.use_huffman { + let mut reader = Reader::new(header.data); + let ctx = + DecodeContext::new_huffman(&mut reader, header, referred_tables, standard_tables)?; + decode_with(ctx, symbols, header, bitmap)?; + } else { + let mut decoder = ArithmeticDecoder::new(header.data); + + let num_symbols = symbols.len() as u32; + let symbol_code_length = 32 - num_symbols.saturating_sub(1).leading_zeros(); + let mut contexts = TextRegionContexts::new(symbol_code_length); + + let num_gr_contexts = 1 << header.flags.refinement_template.context_bits(); + scratch.contexts.clear(); + scratch + .contexts + .resize(num_gr_contexts, ArithmeticDecoderContext::default()); + + let ctx = DecodeContext::new_arithmetic(&mut decoder, &mut contexts, &mut scratch.contexts); + decode_with(ctx, symbols, header, bitmap)?; + } + + Ok(()) +} + +/// Decode a text region segment with a decode context (6.4). +pub(crate) fn decode_with( + mut ctx: DecodeContext<'_, '_>, + symbols: &[&Bitmap], + header: &TextRegionHeader<'_>, + region: &mut Bitmap, +) -> Result<()> { + let strip_size = header.strip_size(); + + // Arbitrarily chosen, but we need some limit to prevent timeouts. + const MAX_INSTANCES: u32 = 10_000; + + if header.num_instances > MAX_INSTANCES { + bail!(SymbolError::TooManyInstances); + } + + let mut strip_t = ctx + .read_strip_delta_t(strip_size)? + .checked_neg() + .ok_or(OverflowError::PlacementCoordinate)?; + let mut first_s: i32 = 0; + let mut instance_count = 0; + + while instance_count < header.num_instances { + let delta_t = ctx.read_strip_delta_t(strip_size)?; + strip_t = strip_t + .checked_add(delta_t) + .ok_or(OverflowError::PlacementCoordinate)?; + + let mut first_symbol_in_strip = true; + let mut current_s = 0; + + loop { + // Prevent infinite loop for invalid files. + if instance_count > header.num_instances { + bail!(SymbolError::TooManySymbols); + } + + if first_symbol_in_strip { + let delta_first_s = ctx.read_first_s()?; + first_s = first_s + .checked_add(delta_first_s) + .ok_or(OverflowError::PlacementCoordinate)?; + current_s = first_s; + first_symbol_in_strip = false; + } else { + let Some(delta_s) = ctx.read_delta_s()? else { + // OOB - end of strip. + break; + }; + + current_s = current_s + .checked_add(delta_s) + .and_then(|v| v.checked_add(header.flags.delta_s_offset as i32)) + .ok_or(OverflowError::PlacementCoordinate)?; + } + + let current_t = ctx.read_symbol_t(strip_size, header.flags.log_strip_size)?; + let symbol_t = strip_t + .checked_add(current_t) + .ok_or(OverflowError::PlacementCoordinate)?; + + let symbol_id = ctx.read_symbol_id()?; + + let symbol_bitmap = + decode_symbol_instance_bitmap(&mut ctx, symbols, header, symbol_id)?; + + let symbol_bitmap_ref: &Bitmap = match &symbol_bitmap { + SymbolBitmap::Reference(idx) => symbols.get(*idx).ok_or(SymbolError::OutOfRange)?, + SymbolBitmap::Owned(bitmap) => bitmap, + }; + let symbol_width = symbol_bitmap_ref.width as i32; + let symbol_height = symbol_bitmap_ref.height as i32; + + if !header.flags.transposed + && (header.flags.reference_corner == ReferenceCorner::TopRight + || header.flags.reference_corner == ReferenceCorner::BottomRight) + { + current_s = current_s + .checked_add(symbol_width - 1) + .ok_or(OverflowError::PlacementCoordinate)?; + } else if header.flags.transposed + && (header.flags.reference_corner == ReferenceCorner::BottomLeft + || header.flags.reference_corner == ReferenceCorner::BottomRight) + { + current_s = current_s + .checked_add(symbol_height - 1) + .ok_or(OverflowError::PlacementCoordinate)?; + } + + let symbol_s = current_s; + + let (x, y) = if !header.flags.transposed { + match header.flags.reference_corner { + ReferenceCorner::TopLeft => (symbol_s, symbol_t), + ReferenceCorner::TopRight => (symbol_s - symbol_width + 1, symbol_t), + ReferenceCorner::BottomLeft => (symbol_s, symbol_t - symbol_height + 1), + ReferenceCorner::BottomRight => { + (symbol_s - symbol_width + 1, symbol_t - symbol_height + 1) + } + } + } else { + match header.flags.reference_corner { + ReferenceCorner::TopLeft => (symbol_t, symbol_s), + ReferenceCorner::TopRight => (symbol_t - symbol_width + 1, symbol_s), + ReferenceCorner::BottomLeft => (symbol_t, symbol_s - symbol_height + 1), + ReferenceCorner::BottomRight => { + (symbol_t - symbol_width + 1, symbol_s - symbol_height + 1) + } + } + }; + + region.combine(symbol_bitmap_ref, x, y, header.flags.combination_operator); + + if !header.flags.transposed + && (header.flags.reference_corner == ReferenceCorner::TopLeft + || header.flags.reference_corner == ReferenceCorner::BottomLeft) + { + current_s = current_s + .checked_add(symbol_width - 1) + .ok_or(OverflowError::PlacementCoordinate)?; + } else if header.flags.transposed + && (header.flags.reference_corner == ReferenceCorner::TopLeft + || header.flags.reference_corner == ReferenceCorner::TopRight) + { + current_s = current_s + .checked_add(symbol_height - 1) + .ok_or(OverflowError::PlacementCoordinate)?; + } + + instance_count += 1; + } + } + + Ok(()) +} + +/// Shared integer decoder contexts for text region decoding. +pub(crate) struct TextRegionContexts { + /// IADT: Strip delta T decoder (6.4.6) + pub(crate) iadt: IntegerDecoder, + /// IAFS: First symbol S coordinate decoder (6.4.7) + pub(crate) iafs: IntegerDecoder, + /// IADS: Subsequent symbol S coordinate decoder (6.4.8) + pub(crate) iads: IntegerDecoder, + /// IAIT: Symbol instance T coordinate decoder (6.4.9) + pub(crate) iait: IntegerDecoder, + /// IAID: Symbol ID decoder (6.4.10) + pub(crate) iaid: SymbolIdDecoder, + /// IARI: Refinement image indicator decoder (6.4.11) + pub(crate) iari: IntegerDecoder, + /// IARDW: Refinement delta width decoder (6.4.11.1) + pub(crate) iardw: IntegerDecoder, + /// IARDH: Refinement delta height decoder (6.4.11.2) + pub(crate) iardh: IntegerDecoder, + /// IARDX: Refinement X offset decoder (6.4.11.3) + pub(crate) iardx: IntegerDecoder, + /// IARDY: Refinement Y offset decoder (6.4.11.4) + pub(crate) iardy: IntegerDecoder, +} + +impl TextRegionContexts { + /// Create new text region contexts with the given symbol code length. + pub(crate) fn new(symbol_code_length: u32) -> Self { + Self { + iadt: IntegerDecoder::new(), + iafs: IntegerDecoder::new(), + iads: IntegerDecoder::new(), + iait: IntegerDecoder::new(), + iaid: SymbolIdDecoder::new(symbol_code_length), + iari: IntegerDecoder::new(), + iardw: IntegerDecoder::new(), + iardh: IntegerDecoder::new(), + iardx: IntegerDecoder::new(), + iardy: IntegerDecoder::new(), + } + } +} + +pub(crate) enum DecodeContext<'a, 'b> { + Huffman { + reader: &'a mut Reader<'b>, + tables: TextRegionHuffmanTables<'a>, + symbol_codes: &'a HuffmanTable, + }, + Arithmetic { + decoder: &'a mut ArithmeticDecoder<'b>, + contexts: &'a mut TextRegionContexts, + gr_contexts: &'a mut [ArithmeticDecoderContext], + }, +} + +impl<'a, 'b> DecodeContext<'a, 'b> { + pub(crate) fn new_huffman( + reader: &'a mut Reader<'b>, + header: &'a TextRegionHeader<'_>, + referred_tables: &'a [HuffmanTable], + standard_tables: &'a StandardHuffmanTables, + ) -> Result { + let huffman_flags = header + .huffman_flags + .as_ref() + .ok_or(HuffmanError::InvalidSelection)?; + let tables = select_huffman_tables(huffman_flags, referred_tables, standard_tables)?; + let symbol_codes = header + .symbol_id_table + .as_ref() + .ok_or(HuffmanError::MissingTables)?; + + Ok(DecodeContext::Huffman { + reader, + tables, + symbol_codes, + }) + } + + pub(crate) fn new_arithmetic( + decoder: &'a mut ArithmeticDecoder<'b>, + contexts: &'a mut TextRegionContexts, + gr_contexts: &'a mut [ArithmeticDecoderContext], + ) -> Self { + DecodeContext::Arithmetic { + decoder, + contexts, + gr_contexts, + } + } + + fn read_strip_delta_t(&mut self, strip_size: u32) -> Result { + match self { + DecodeContext::Huffman { reader, tables, .. } => { + let value = tables.delta_t.decode_no_oob(reader)?; + value + .checked_mul(strip_size as i32) + .ok_or(SymbolError::InvalidStripDelta.into()) + } + DecodeContext::Arithmetic { + decoder, contexts, .. + } => { + let value = contexts + .iadt + .decode(decoder) + .ok_or(SymbolError::UnexpectedOob)?; + value + .checked_mul(strip_size as i32) + .ok_or(SymbolError::InvalidStripDelta.into()) + } + } + } + + /// Decode first symbol instance S coordinate (6.4.7). + fn read_first_s(&mut self) -> Result { + match self { + DecodeContext::Huffman { reader, tables, .. } => tables.first_s.decode_no_oob(reader), + DecodeContext::Arithmetic { + decoder, contexts, .. + } => contexts + .iafs + .decode(decoder) + .ok_or(SymbolError::UnexpectedOob.into()), + } + } + + /// Decode subsequent symbol instance S coordinate (6.4.8). + fn read_delta_s(&mut self) -> Result> { + match self { + DecodeContext::Huffman { reader, tables, .. } => tables.delta_s.decode(reader), + DecodeContext::Arithmetic { + decoder, contexts, .. + } => Ok(contexts.iads.decode(decoder)), + } + } + + /// Decode symbol instance T coordinate (6.4.9). + fn read_symbol_t(&mut self, strip_size: u32, log_strip_size: u8) -> Result { + if strip_size == 1 { + return Ok(0); + } + + match self { + DecodeContext::Huffman { reader, .. } => reader + .read_bits(log_strip_size) + .ok_or(HuffmanError::InvalidCode.into()) + .map(|v| v as i32), + DecodeContext::Arithmetic { + decoder, contexts, .. + } => contexts + .iait + .decode(decoder) + .ok_or(SymbolError::UnexpectedOob.into()), + } + } + + /// Decode symbol instance symbol ID (6.4.10). + fn read_symbol_id(&mut self) -> Result { + match self { + DecodeContext::Huffman { + reader, + symbol_codes, + .. + } => symbol_codes.decode_no_oob(reader).map(|v| v as usize), + DecodeContext::Arithmetic { + decoder, contexts, .. + } => Ok(contexts.iaid.decode(decoder) as usize), + } + } + + fn read_refinement_flag(&mut self) -> Result { + match self { + DecodeContext::Huffman { reader, .. } => { + reader.read_bit().ok_or(ParseError::UnexpectedEof.into()) + } + DecodeContext::Arithmetic { + decoder, contexts, .. + } => Ok(contexts + .iari + .decode(decoder) + .ok_or(SymbolError::UnexpectedOob)? as u8), + } + } + + /// Decode symbol instance refinement delta width (6.4.11.1). + fn read_refinement_delta_width(&mut self) -> Result { + match self { + DecodeContext::Huffman { reader, tables, .. } => { + tables.refinement_width.decode_no_oob(reader) + } + DecodeContext::Arithmetic { + decoder, contexts, .. + } => contexts + .iardw + .decode(decoder) + .ok_or(SymbolError::UnexpectedOob.into()), + } + } + + /// Decode symbol instance refinement delta height (6.4.11.2). + fn read_refinement_delta_height(&mut self) -> Result { + match self { + DecodeContext::Huffman { reader, tables, .. } => { + tables.refinement_height.decode_no_oob(reader) + } + DecodeContext::Arithmetic { + decoder, contexts, .. + } => contexts + .iardh + .decode(decoder) + .ok_or(SymbolError::UnexpectedOob.into()), + } + } + + /// Decode symbol instance refinement x offset (6.4.11.3). + fn read_refinement_x_offset(&mut self) -> Result { + match self { + DecodeContext::Huffman { reader, tables, .. } => { + tables.refinement_x.decode_no_oob(reader) + } + DecodeContext::Arithmetic { + decoder, contexts, .. + } => contexts + .iardx + .decode(decoder) + .ok_or(SymbolError::UnexpectedOob.into()), + } + } + + /// Decode symbol instance refinement y offset (6.4.11.4). + fn read_refinement_y_offset(&mut self) -> Result { + match self { + DecodeContext::Huffman { reader, tables, .. } => { + tables.refinement_y.decode_no_oob(reader) + } + DecodeContext::Arithmetic { + decoder, contexts, .. + } => contexts + .iardy + .decode(decoder) + .ok_or(SymbolError::UnexpectedOob.into()), + } + } + + /// Decode the refinement bitmap, steps 5) to 7) of 6.4.11. + fn decode_refinement_bitmap( + &mut self, + refined: &mut Bitmap, + reference_bitmap: &Bitmap, + reference_x_offset: i32, + reference_y_offset: i32, + refinement_template: RefinementTemplate, + refinement_at_pixels: &[AdaptiveTemplatePixel], + ) -> Result<()> { + match self { + DecodeContext::Huffman { reader, tables, .. } => { + let refinement_data_size = tables.refinement_size.decode_no_oob(reader)? as u32; + reader.align(); + + let refinement_data = reader + .read_bytes(refinement_data_size as usize) + .ok_or(ParseError::UnexpectedEof)?; + + let mut decoder = ArithmeticDecoder::new(refinement_data); + let num_context_bits = refinement_template.context_bits(); + let mut contexts = vec![ArithmeticDecoderContext::default(); 1 << num_context_bits]; + + generic_refinement::decode_bitmap( + &mut decoder, + &mut contexts, + refined, + reference_bitmap, + reference_x_offset, + reference_y_offset, + refinement_template, + refinement_at_pixels, + false, + ) + } + DecodeContext::Arithmetic { + decoder, + gr_contexts, + .. + } => generic_refinement::decode_bitmap( + decoder, + gr_contexts, + refined, + reference_bitmap, + reference_x_offset, + reference_y_offset, + refinement_template, + refinement_at_pixels, + false, + ), + } + } +} + +/// Result of determining a symbol instance bitmap. +enum SymbolBitmap { + /// Use the symbol at this index directly (`R_I` = 0). + Reference(usize), + /// Use this refined bitmap (`R_I` = 1). + Owned(Bitmap), +} + +/// Decode the symbol instance bitmap (6.4.11). +fn decode_symbol_instance_bitmap( + ctx: &mut DecodeContext<'_, '_>, + symbols: &[&Bitmap], + header: &TextRegionHeader<'_>, + symbol_id: usize, +) -> Result { + if !header.flags.use_refinement || ctx.read_refinement_flag()? == 0 { + return Ok(SymbolBitmap::Reference(symbol_id)); + } + + // Otherwise, the refinement flag was 1. + + let reference_bitmap = symbols.get(symbol_id).ok_or(SymbolError::OutOfRange)?; + + let rdw = ctx.read_refinement_delta_width()?; + let rdh = ctx.read_refinement_delta_height()?; + let rdx = ctx.read_refinement_x_offset()?; + let rdy = ctx.read_refinement_y_offset()?; + + let refined_width = (reference_bitmap.width as i32) + .checked_add(rdw) + .and_then(|v| u32::try_from(v).ok()) + .ok_or(OverflowError::BitmapDimension)?; + let refined_height = (reference_bitmap.height as i32) + .checked_add(rdh) + .and_then(|v| u32::try_from(v).ok()) + .ok_or(OverflowError::BitmapDimension)?; + let reference_x_offset = rdw + .div_euclid(2) + .checked_add(rdx) + .ok_or(OverflowError::ReferenceOffset)?; + let reference_y_offset = rdh + .div_euclid(2) + .checked_add(rdy) + .ok_or(OverflowError::ReferenceOffset)?; + + let mut refined_bitmap = Bitmap::new(refined_width, refined_height)?; + + ctx.decode_refinement_bitmap( + &mut refined_bitmap, + reference_bitmap, + reference_x_offset, + reference_y_offset, + header.flags.refinement_template, + &header.refinement_at_pixels, + )?; + + Ok(SymbolBitmap::Owned(refined_bitmap)) +} + +/// Select Huffman tables based on flags (7.4.3.1.6). +fn select_huffman_tables<'a>( + flags: &TextRegionHuffmanFlags, + custom_tables: &'a [HuffmanTable], + standard_tables: &'a StandardHuffmanTables, +) -> Result> { + let mut custom_table_idx = 0; + + let mut get_custom = || -> Result<&'a HuffmanTable> { + let table = custom_tables + .get(custom_table_idx) + .ok_or(HuffmanError::MissingTables)?; + custom_table_idx += 1; + Ok(table) + }; + + // "1) SBHUFFFS" + let first_s = match flags.first_s_table { + 0 => standard_tables.table_f(), + 1 => standard_tables.table_g(), + 3 => get_custom()?, + _ => bail!(HuffmanError::InvalidSelection), + }; + + // "2) SBHUFFDS" + let delta_s = match flags.delta_s_table { + 0 => standard_tables.table_h(), + 1 => standard_tables.table_i(), + 2 => standard_tables.table_j(), + 3 => get_custom()?, + _ => bail!(HuffmanError::InvalidSelection), + }; + + // "3) SBHUFFDT" + let delta_t = match flags.delta_t_table { + 0 => standard_tables.table_k(), + 1 => standard_tables.table_l(), + 2 => standard_tables.table_m(), + 3 => get_custom()?, + _ => bail!(HuffmanError::InvalidSelection), + }; + + // "4) SBHUFFRDW" + let refinement_width = match flags.refinement_width_table { + 0 => standard_tables.table_n(), + 1 => standard_tables.table_o(), + 3 => get_custom()?, + _ => bail!(HuffmanError::InvalidSelection), + }; + + // "5) SBHUFFRDH" + let refinement_height = match flags.refinement_height_table { + 0 => standard_tables.table_n(), + 1 => standard_tables.table_o(), + 3 => get_custom()?, + _ => bail!(HuffmanError::InvalidSelection), + }; + + // "6) SBHUFFRDY" + let refinement_y = match flags.refinement_y_table { + 0 => standard_tables.table_n(), + 1 => standard_tables.table_o(), + 3 => get_custom()?, + _ => bail!(HuffmanError::InvalidSelection), + }; + + // "7) SBHUFFRDX" + let refinement_x = match flags.refinement_x_table { + 0 => standard_tables.table_n(), + 1 => standard_tables.table_o(), + 3 => get_custom()?, + _ => bail!(HuffmanError::InvalidSelection), + }; + + // "8) SBHUFFRSIZE" + let refinement_size = match flags.refinement_size_table { + 0 => standard_tables.table_a(), + 1 => get_custom()?, + _ => bail!(HuffmanError::InvalidSelection), + }; + + Ok(TextRegionHuffmanTables { + first_s, + delta_s, + delta_t, + refinement_width, + refinement_height, + refinement_y, + refinement_x, + refinement_size, + }) +} + +/// Decode the symbol ID Huffman table (7.4.3.1.7). +fn decode_symbol_id_huffman_table( + reader: &mut Reader<'_>, + num_symbols: u32, +) -> Result { + let mut runcode_lines: Vec = Vec::with_capacity(35); + for runcode_idx in 0..35 { + let prefix_length = reader.read_bits(4).ok_or(HuffmanError::InvalidCode)? as u8; + runcode_lines.push(TableLine::new(runcode_idx, prefix_length, 0)); + } + + let runcode_table = HuffmanTable::build(&runcode_lines)?; + let mut symbol_code_lengths = Vec::with_capacity(num_symbols as usize); + + while symbol_code_lengths.len() < num_symbols as usize { + let runcode = runcode_table.decode_no_oob(reader)? as u32; + + match runcode { + 0..=31 => { + symbol_code_lengths.push(runcode as u8); + } + 32 => { + // Copy previous 3-6 times. + let extra_bits = reader.read_bits(2).ok_or(HuffmanError::InvalidCode)? as usize; + let repeat = extra_bits + 3; + let previous_length = *symbol_code_lengths + .last() + .ok_or(HuffmanError::InvalidCode)?; + symbol_code_lengths.extend(iter::repeat_n(previous_length, repeat)); + } + 33 => { + // Repeat 0 length 3-10 times. + let extra_bits = reader.read_bits(3).ok_or(HuffmanError::InvalidCode)? as usize; + let repeat = extra_bits + 3; + symbol_code_lengths.extend(iter::repeat_n(0, repeat)); + } + 34 => { + // Repeat 0 length 11-138 times. + let extra_bits = reader.read_bits(7).ok_or(HuffmanError::InvalidCode)? as usize; + let repeat = extra_bits + 11; + symbol_code_lengths.extend(iter::repeat_n(0, repeat)); + } + _ => bail!(HuffmanError::InvalidCode), + } + } + + if symbol_code_lengths.len() != num_symbols as usize { + bail!(HuffmanError::InvalidCode); + } + + reader.align(); + + let symbol_lines: Vec = symbol_code_lengths + .iter() + .enumerate() + .map(|(symbol_idx, &prefix_length)| TableLine::new(symbol_idx as i32, prefix_length, 0)) + .collect(); + HuffmanTable::build(&symbol_lines) +} + +pub(crate) struct TextRegionHuffmanTables<'a> { + first_s: &'a HuffmanTable, + delta_s: &'a HuffmanTable, + delta_t: &'a HuffmanTable, + refinement_width: &'a HuffmanTable, + refinement_height: &'a HuffmanTable, + refinement_y: &'a HuffmanTable, + refinement_x: &'a HuffmanTable, + refinement_size: &'a HuffmanTable, +} + +/// Reference corner for symbol placement. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ReferenceCorner { + BottomLeft, + TopLeft, + BottomRight, + TopRight, +} + +impl ReferenceCorner { + fn from_byte(value: u8) -> Self { + match value { + 0 => Self::BottomLeft, + 1 => Self::TopLeft, + 2 => Self::BottomRight, + 3 => Self::TopRight, + _ => unreachable!(), + } + } +} + +/// Parsed text region segment flags (7.4.3.1.1). +#[derive(Debug, Clone)] +pub(crate) struct TextRegionFlags { + pub(crate) use_huffman: bool, + pub(crate) use_refinement: bool, + pub(crate) log_strip_size: u8, + pub(crate) reference_corner: ReferenceCorner, + pub(crate) transposed: bool, + pub(crate) combination_operator: CombinationOperator, + pub(crate) default_pixel: bool, + pub(crate) delta_s_offset: i8, + pub(crate) refinement_template: RefinementTemplate, +} + +/// Text region segment Huffman flags (7.4.3.1.2). +#[derive(Debug, Clone)] +pub(crate) struct TextRegionHuffmanFlags { + pub(crate) first_s_table: u8, + pub(crate) delta_s_table: u8, + pub(crate) delta_t_table: u8, + pub(crate) refinement_width_table: u8, + pub(crate) refinement_height_table: u8, + pub(crate) refinement_y_table: u8, + pub(crate) refinement_x_table: u8, + pub(crate) refinement_size_table: u8, +} + +/// Parsed text region segment header (7.4.3.1). +#[derive(Debug, Clone)] +pub(crate) struct TextRegionHeader<'a> { + pub(crate) region_info: RegionSegmentInfo, + pub(crate) flags: TextRegionFlags, + pub(crate) huffman_flags: Option, + pub(crate) refinement_at_pixels: Vec, + pub(crate) num_instances: u32, + pub(crate) symbol_id_table: Option, + pub(crate) data: &'a [u8], +} + +impl TextRegionHeader<'_> { + pub(crate) fn strip_size(&self) -> u32 { + 1_u32 << self.flags.log_strip_size + } +} + +/// Parse text region segment flags (7.4.3.1.1). +fn parse_text_region_flags(reader: &mut Reader<'_>) -> Result { + let flags_word = reader.read_u16().ok_or(ParseError::UnexpectedEof)?; + let use_huffman = flags_word & 0x0001 != 0; + let use_refinement = flags_word & 0x0002 != 0; + let log_strip_size = ((flags_word >> 2) & 0x03) as u8; + let reference_corner = ReferenceCorner::from_byte(((flags_word >> 4) & 0x03) as u8); + let transposed = flags_word & 0x0040 != 0; + let combination_operator = CombinationOperator::from_value(((flags_word >> 7) & 0x03) as u8)?; + + let default_pixel = flags_word & 0x0200 != 0; + + let delta_s_offset_raw = ((flags_word >> 10) & 0x1F) as u8; + let delta_s_offset = if delta_s_offset_raw & 0x10 != 0 { + (delta_s_offset_raw | 0xE0) as i8 + } else { + delta_s_offset_raw as i8 + }; + + let refinement_template = RefinementTemplate::from_byte((flags_word >> 15) as u8); + + Ok(TextRegionFlags { + use_huffman, + use_refinement, + log_strip_size, + reference_corner, + transposed, + combination_operator, + default_pixel, + delta_s_offset, + refinement_template, + }) +} + +/// Parse text region Huffman flags (7.4.3.1.2). +fn parse_text_region_huffman_flags(reader: &mut Reader<'_>) -> Result { + let flags_word = reader.read_u16().ok_or(ParseError::UnexpectedEof)?; + let first_s_table = (flags_word & 0x03) as u8; + let delta_s_table = ((flags_word >> 2) & 0x03) as u8; + let delta_t_table = ((flags_word >> 4) & 0x03) as u8; + let refinement_width_table = ((flags_word >> 6) & 0x03) as u8; + let refinement_height_table = ((flags_word >> 8) & 0x03) as u8; + let refinement_y_table = ((flags_word >> 10) & 0x03) as u8; + let refinement_x_table = ((flags_word >> 12) & 0x03) as u8; + let refinement_size_table = ((flags_word >> 14) & 0x01) as u8; + + Ok(TextRegionHuffmanFlags { + first_s_table, + delta_s_table, + delta_t_table, + refinement_width_table, + refinement_height_table, + refinement_y_table, + refinement_x_table, + refinement_size_table, + }) +} + +/// Parse a text region segment header (7.4.3.1). +pub(crate) fn parse<'a>(reader: &mut Reader<'a>, num_symbols: u32) -> Result> { + let region_info = parse_region_segment_info(reader)?; + let flags = parse_text_region_flags(reader)?; + let huffman_flags = if flags.use_huffman { + Some(parse_text_region_huffman_flags(reader)?) + } else { + None + }; + + let refinement_at_pixels = + if flags.use_refinement && flags.refinement_template == RefinementTemplate::Template0 { + parse_refinement_at_pixels(reader)? + } else { + Vec::new() + }; + + let num_instances = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + + let symbol_id_table = if flags.use_huffman { + Some(decode_symbol_id_huffman_table(reader, num_symbols)?) + } else { + None + }; + + let data = reader.tail().ok_or(ParseError::UnexpectedEof)?; + + Ok(TextRegionHeader { + region_info, + flags, + huffman_flags, + refinement_at_pixels, + num_instances, + symbol_id_table, + data, + }) +} diff --git a/vendor/hayro-jbig2/src/error.rs b/vendor/hayro-jbig2/src/error.rs new file mode 100644 index 000000000..c579368b4 --- /dev/null +++ b/vendor/hayro-jbig2/src/error.rs @@ -0,0 +1,322 @@ +//! Error types for JBIG2 decoding. + +use core::fmt; + +/// The main error type for JBIG2 decoding operations. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DecodeError { + /// Errors related to reading/parsing data. + Parse(ParseError), + /// Errors related to file structure. + Format(FormatError), + /// Errors related to segment processing. + Segment(SegmentError), + /// Errors related to Huffman decoding. + Huffman(HuffmanError), + /// Errors related to region parameters. + Region(RegionError), + /// Errors related to template configuration. + Template(TemplateError), + /// Errors related to symbol handling. + Symbol(SymbolError), + /// Arithmetic overflow in calculations. + Overflow(OverflowError), + /// Feature not yet implemented. + Unsupported, +} + +/// Errors related to reading/parsing data. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParseError { + /// Unexpected end of input. + UnexpectedEof, +} + +/// Errors related to file structure. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FormatError { + /// Invalid file header signature. + InvalidHeader, + /// Reserved bits are not zero. + ReservedBits, + /// Missing required page information segment. + MissingPageInfo, + /// Page height unknown with no stripe segments. + UnknownPageHeight, + /// Page has zero width or height. + EmptyPage, +} + +/// Errors related to segment processing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SegmentError { + /// Unknown or reserved segment type. + UnknownType, + /// Invalid referred-to segment count. + InvalidReferredCount, + /// Segment refers to a larger segment number. + InvalidReference, + /// Missing end marker for unknown-length region. + MissingEndMarker, + /// Missing required pattern dictionary. + MissingPatternDictionary, +} + +/// Errors related to Huffman decoding. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HuffmanError { + /// Invalid Huffman code sequence. + InvalidCode, + /// Invalid Huffman table selection. + InvalidSelection, + /// Not enough referred Huffman tables. + MissingTables, + /// Unexpected out-of-band value. + UnexpectedOob, + /// Huffman tree has conflicting codes (one code is a prefix of another). + ConflictingCodes, + /// Prefix length exceeds maximum supported value. + PrefixLengthTooLarge, +} + +/// Errors related to region parameters. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RegionError { + /// Invalid combination operator value. + InvalidCombinationOperator, + /// Region with invalid dimension. + InvalidDimension, + /// MMR (Modified Modified Read) decoding failed. + InvalidMmrData, + /// Gray-scale value exceeds pattern count. + GrayScaleOutOfRange, +} + +/// Errors related to template configuration. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TemplateError { + /// An invalid template value was used. + Invalid, + /// Invalid adaptive template pixel location. + InvalidAtPixel, +} + +/// Errors related to symbol handling. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SymbolError { + /// No symbols available for text region. + NoSymbols, + /// The symbol dictionary contains more symbols than expected. + TooManySymbols, + /// Symbol ID out of valid range. + OutOfRange, + /// Unexpected out-of-band value. + UnexpectedOob, + /// An invalid symbol was encountered. + Invalid, + /// Strip delta multiplication overflowed. + InvalidStripDelta, + /// Too many symbol instances in a text region. + TooManyInstances, +} + +/// Arithmetic overflow errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OverflowError { + /// Bitmap dimension computation overflowed. + BitmapDimension, + /// Halftone grid dimension computation overflowed. + GridDimension, + /// Placement coordinate computation overflowed. + PlacementCoordinate, + /// Reference offset computation overflowed. + ReferenceOffset, + /// Index computation overflowed. + Index, + /// A symbol dictionary has too many symbols. + SymbolCount, +} + +impl fmt::Display for DecodeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Parse(e) => write!(f, "{e}"), + Self::Format(e) => write!(f, "{e}"), + Self::Segment(e) => write!(f, "{e}"), + Self::Huffman(e) => write!(f, "{e}"), + Self::Region(e) => write!(f, "{e}"), + Self::Template(e) => write!(f, "{e}"), + Self::Symbol(e) => write!(f, "{e}"), + Self::Overflow(e) => write!(f, "{e}"), + Self::Unsupported => write!(f, "unsupported feature"), + } + } +} + +impl fmt::Display for OverflowError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::BitmapDimension => write!(f, "overflow in bitmap dimension computation"), + Self::PlacementCoordinate => write!(f, "overflow in placement coordinate computation"), + Self::ReferenceOffset => write!(f, "overflow in reference offset computation"), + Self::Index => write!(f, "overflow in index computation"), + Self::GridDimension => write!(f, "overflow in grid dimension computation"), + Self::SymbolCount => write!(f, "a symbol dictionary has too many symbols"), + } + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UnexpectedEof => write!(f, "unexpected end of input"), + } + } +} + +impl fmt::Display for FormatError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidHeader => write!(f, "invalid JBIG2 file header"), + Self::ReservedBits => write!(f, "reserved bits must be zero"), + Self::MissingPageInfo => write!(f, "missing page information segment"), + Self::UnknownPageHeight => write!(f, "page height unknown with no stripe segments"), + Self::EmptyPage => write!(f, "page has zero width or height"), + } + } +} + +impl fmt::Display for SegmentError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UnknownType => write!(f, "unknown or reserved segment type"), + Self::InvalidReferredCount => write!(f, "invalid referred-to segment count"), + Self::InvalidReference => write!(f, "segment refers to larger segment number"), + Self::MissingEndMarker => write!(f, "missing end marker for unknown-length region"), + Self::MissingPatternDictionary => write!(f, "missing required pattern dictionary"), + } + } +} + +impl fmt::Display for HuffmanError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidCode => write!(f, "invalid Huffman code"), + Self::InvalidSelection => write!(f, "invalid Huffman table selection"), + Self::MissingTables => write!(f, "not enough referred Huffman tables"), + Self::UnexpectedOob => write!(f, "unexpected out-of-band value"), + Self::ConflictingCodes => write!(f, "conflicting Huffman codes"), + Self::PrefixLengthTooLarge => write!(f, "Huffman prefix length too large"), + } + } +} + +impl fmt::Display for RegionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InvalidCombinationOperator => write!(f, "invalid combination operator"), + Self::InvalidDimension => write!(f, "invalid dimension value"), + Self::InvalidMmrData => write!(f, "invalid MMR-encoded data"), + Self::GrayScaleOutOfRange => write!(f, "gray-scale value exceeds pattern count"), + } + } +} + +impl fmt::Display for TemplateError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Invalid => write!(f, "invalid template value"), + Self::InvalidAtPixel => write!(f, "invalid adaptive template pixel location"), + } + } +} + +impl fmt::Display for SymbolError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NoSymbols => write!(f, "no symbols available"), + Self::OutOfRange => write!(f, "symbol ID out of range"), + Self::UnexpectedOob => write!(f, "unexpected out-of-band value"), + Self::TooManySymbols => write!(f, "symbol dictionary contains too many symbols"), + Self::Invalid => write!(f, "invalid symbol encountered"), + Self::InvalidStripDelta => write!(f, "strip delta overflow"), + Self::TooManyInstances => write!(f, "too many symbol instances"), + } + } +} + +impl core::error::Error for DecodeError {} +impl core::error::Error for ParseError {} +impl core::error::Error for FormatError {} +impl core::error::Error for SegmentError {} +impl core::error::Error for HuffmanError {} +impl core::error::Error for RegionError {} +impl core::error::Error for TemplateError {} +impl core::error::Error for SymbolError {} +impl core::error::Error for OverflowError {} + +impl From for DecodeError { + fn from(e: ParseError) -> Self { + Self::Parse(e) + } +} + +impl From for DecodeError { + fn from(e: FormatError) -> Self { + Self::Format(e) + } +} + +impl From for DecodeError { + fn from(e: SegmentError) -> Self { + Self::Segment(e) + } +} + +impl From for DecodeError { + fn from(e: HuffmanError) -> Self { + Self::Huffman(e) + } +} + +impl From for DecodeError { + fn from(e: RegionError) -> Self { + Self::Region(e) + } +} + +impl From for DecodeError { + fn from(e: TemplateError) -> Self { + Self::Template(e) + } +} + +impl From for DecodeError { + fn from(e: SymbolError) -> Self { + Self::Symbol(e) + } +} + +impl From for DecodeError { + fn from(e: OverflowError) -> Self { + Self::Overflow(e) + } +} + +/// Result type for JBIG2 decoding operations. +pub type Result = core::result::Result; + +macro_rules! bail { + ($err:expr) => { + return Err($err.into()) + }; +} + +macro_rules! err { + ($err:expr) => { + Err($err.into()) + }; +} + +pub(crate) use bail; +pub(crate) use err; diff --git a/vendor/hayro-jbig2/src/file.rs b/vendor/hayro-jbig2/src/file.rs new file mode 100644 index 000000000..1440a22ef --- /dev/null +++ b/vendor/hayro-jbig2/src/file.rs @@ -0,0 +1,214 @@ +//! File format parsing for JBIG2 bitstreams (Annex D of ITU-T T.88). +//! +//! This module handles parsing of standalone JBIG2 files in both sequential +//! and random-access organization formats. + +use alloc::vec::Vec; + +use crate::error::{FormatError, ParseError, Result, bail}; +use crate::reader::Reader; +use crate::segment::{ + Segment, SegmentType, parse_segment, parse_segment_data, parse_segment_header, +}; + +/// "There are two standalone file organizations possible for a JBIG2 bitstream." +/// (Annex D) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum FileOrganization { + /// "This is a standalone file organization. This organization is intended for + /// streaming applications, where the decoder is guaranteed to begin at the start + /// of the bitstream and decode everything up to the end of the bitstream." + /// (D.1) + Sequential, + /// "This is a standalone file organization. This organization is intended for + /// random-access applications, where the decoder might want to process parts of + /// the file in an arbitrary order." (D.2) + RandomAccess, +} + +/// Parsed file header. +/// +/// "A file header contains the following fields, in order: +/// ID string – see D.4.1. +/// File header flags – see D.4.2. +/// Number of pages – see D.4.3." (D.4) +#[derive(Debug, Clone)] +pub(crate) struct FileHeader { + /// The file organization type. + pub(crate) organization: FileOrganization, + /// The number of pages in the file, if known. + pub(crate) _number_of_pages: Option, + /// "If this bit is 0, no generic region segments uses the templates with + /// 12 AT pixels. If the file contains one or more generic region segments + /// using such templates, this bit must be 1." (D.4.2, Bit 2) + pub(crate) _uses_extended_templates: bool, + /// "If this bit is 0, no region segment is extended to be coloured. If the + /// file contains one or more coloured region segments, this bit must be 1." + /// (D.4.2, Bit 3) + pub(crate) _contains_coloured_regions: bool, +} + +/// A parsed JBIG2 file. +#[derive(Debug)] +pub(crate) struct File<'a> { + /// The file header. + pub(crate) _header: FileHeader, + /// The segments in the file. + pub(crate) segments: Vec>, +} + +/// "This is an 8-byte sequence containing 0x97 0x4A 0x42 0x32 0x0D 0x0A 0x1A 0x0A." +/// (D.4.1) +const FILE_HEADER_ID: [u8; 8] = [0x97, 0x4A, 0x42, 0x32, 0x0D, 0x0A, 0x1A, 0x0A]; + +/// Parse a standalone JBIG2 file. +pub(crate) fn parse_file(data: &[u8]) -> Result> { + let mut reader = Reader::new(data); + + let header = parse_file_header(&mut reader)?; + let mut segments = parse_segments(&mut reader, header.organization)?; + + // Technically shouldn't be necessary because the spec mandates that segments + // are in sorted order, but just to be safe. + segments.sort_by_key(|seg| seg.header.segment_number); + + Ok(File { + _header: header, + segments, + }) +} + +fn parse_file_header(reader: &mut Reader<'_>) -> Result { + // D.4.1: ID string + let id = reader.read_bytes(8).ok_or(ParseError::UnexpectedEof)?; + if id != FILE_HEADER_ID { + bail!(FormatError::InvalidHeader); + } + + // D.4.2: File header flags + let flags = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + + // "Bit 0: File organization type. If this bit is 0, the file uses the + // random-access organization. If this bit is 1, the file uses the + // sequential organization." (D.4.2) + let organization = if flags & 0x01 != 0 { + FileOrganization::Sequential + } else { + FileOrganization::RandomAccess + }; + + // "Bit 1: Unknown number of pages. If this bit is 0, then the number of + // pages contained in the file is known. If this bit is 1, then the number + // of pages contained in the file was not known at the time that the file + // header was coded." (D.4.2) + let unknown_page_count = flags & 0x02 != 0; + + // "Bit 2: If this bit is 0, no generic region segments uses the templates + // with 12 AT pixels." (D.4.2) + let uses_extended_templates = flags & 0x04 != 0; + + // "Bit 3: If this bit is 0, no region segment is extended to be coloured." + // (D.4.2) + let contains_coloured_regions = flags & 0x08 != 0; + + // "Bits 4-7: Reserved; must be 0." (D.4.2) + if flags & 0xF0 != 0 { + bail!(FormatError::ReservedBits); + } + + // D.4.3: Number of pages + // "This is a 4-byte field, and is not present if the 'unknown number of + // pages' bit was 1." (D.4.3) + let number_of_pages = if unknown_page_count { + None + } else { + Some(reader.read_u32().ok_or(ParseError::UnexpectedEof)?) + }; + + Ok(FileHeader { + organization, + _number_of_pages: number_of_pages, + _uses_extended_templates: uses_extended_templates, + _contains_coloured_regions: contains_coloured_regions, + }) +} + +fn parse_segments<'a>( + reader: &mut Reader<'a>, + organization: FileOrganization, +) -> Result>> { + let mut segments = Vec::new(); + + match organization { + FileOrganization::Sequential => parse_segments_sequential(reader, &mut segments)?, + FileOrganization::RandomAccess => parse_segments_random_access(reader, &mut segments)?, + } + + Ok(segments) +} + +/// Parse segments in sequential organization. +/// +/// "In this organization, the file structure looks like Figure D.1. A file header +/// is followed by a sequence of segments. The two parts of each segment are stored +/// together: first the segment header then the segment data." (D.1) +pub(crate) fn parse_segments_sequential<'a>( + reader: &mut Reader<'a>, + segments: &mut Vec>, +) -> Result<()> { + loop { + if reader.at_end() { + break; + } + + let segment = parse_segment(reader)?; + + // "If a file contains an end of file segment, it must be the last segment." + // (7.4.11) + let is_eof = matches!(segment.header.segment_type, SegmentType::EndOfFile); + segments.push(segment); + + if is_eof { + break; + } + } + + Ok(()) +} + +/// Parse segments in random-access organization. +/// +/// "In this organization, the file structure looks like Figure D.2. A file header +/// is followed by a sequence of segments headers; the last segment header is +/// followed by the data for the first segment, then the data for the second +/// segment, and so on." (D.2) +fn parse_segments_random_access<'a>( + reader: &mut Reader<'a>, + segments: &mut Vec>, +) -> Result<()> { + let mut headers = Vec::new(); + + loop { + if reader.at_end() { + break; + } + + let header = parse_segment_header(reader)?; + + // "If a file contains an end of file segment, it must be the last segment." + // (7.4.11) + let is_eof = matches!(header.segment_type, SegmentType::EndOfFile); + headers.push(header); + + if is_eof { + break; + } + } + + // Then, read all segment data. + for header in headers { + segments.push(parse_segment_data(reader, header)?); + } + + Ok(()) +} diff --git a/vendor/hayro-jbig2/src/gray_scale.rs b/vendor/hayro-jbig2/src/gray_scale.rs new file mode 100644 index 000000000..6fa53482e --- /dev/null +++ b/vendor/hayro-jbig2/src/gray_scale.rs @@ -0,0 +1,291 @@ +//! Gray-scale image decoding procedure (Annex C). + +use alloc::vec; +use alloc::vec::Vec; + +use crate::ScratchBuffers; +use crate::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext}; +use crate::bitmap::{Bitmap, MAX_DIMENSION, WORD_BITS}; +use crate::decode::generic::{ContextGatherer, decode_bitmap_mmr}; +use crate::decode::{AdaptiveTemplatePixel, Template}; +use crate::error::{OverflowError, Result, bail}; +use crate::simd::{self, Level, Simd, u32x8}; + +/// Input parameters to the gray-scale image decoding procedure (Table C.1). +#[derive(Debug, Clone)] +pub(crate) struct GrayScaleParams<'a> { + /// `GSMMR` - Specifies whether MMR is used. + pub(crate) use_mmr: bool, + /// `GSBPP` - The number of bits per gray-scale value. + pub(crate) bits_per_pixel: u32, + /// `GSW` - The width of the gray-scale image. + pub(crate) width: u32, + /// `GSH` - The height of the gray-scale image. + pub(crate) height: u32, + /// `GSTEMPLATE` - The template used to code the gray-scale bitplanes. + pub(crate) template: Template, + /// `GSKIP` - A mask indicating which values should be skipped. + /// None if `GSUSESKIP` = 0. + pub(crate) skip_mask: Option<&'a [u32]>, +} + +/// The gray-scale image decoding procedure (Annex C, C.5). +/// +/// Returns `GSVALS`: the decoded gray-scale image array, GSW × GSH pixels. +#[inline(always)] +pub(crate) fn decode_gray_scale_image( + data: &[u8], + params: &GrayScaleParams<'_>, + ctx: &mut ScratchBuffers, +) -> Result> { + // Table C.1: "GSMMR specifies whether MMR is used." + if params.use_mmr { + decode_mmr(data, params) + } else { + decode_arithmetic(data, params, ctx) + } +} + +/// The gray-scale image decoding procedure using MMR (Annex C, C.5). +/// +/// Table C.4: "MMR = GSMMR" +fn decode_mmr(data: &[u8], params: &GrayScaleParams<'_>) -> Result> { + // `GSW` - The width of the gray-scale image. + let width = params.width; + // `GSH` - The height of the gray-scale image. + let height = params.height; + // `GSBPP` - The number of bits per gray-scale value. + let bits_per_pixel = params.bits_per_pixel; + let stride = width.div_ceil(WORD_BITS); + + let mut offset = 0; + decode_bitplanes(width, height, stride, bits_per_pixel, |_, bitplane| { + // Table C.4: "GBW = GSW, GBH = GSH" + offset += decode_bitmap_mmr(bitplane, &data[offset..])?; + + Ok(()) + }) +} + +/// The gray-scale image decoding procedure using arithmetic coding (Annex C, C.5). +/// +/// Table C.4: "GBTEMPLATE = GSTEMPLATE, TPGDON = 0, USESKIP = GSUSESKIP, SKIP = GSKIP" +fn decode_arithmetic( + data: &[u8], + params: &GrayScaleParams<'_>, + ctx: &mut ScratchBuffers, +) -> Result> { + // `GSW` - The width of the gray-scale image. + let width = params.width; + // `GSH` - The height of the gray-scale image. + let height = params.height; + + if width > MAX_DIMENSION || height > MAX_DIMENSION { + bail!(OverflowError::BitmapDimension); + } + // PrintCraft patch: the product is bounded too (see `bitmap::MAX_PIXELS`). + if u64::from(width) * u64::from(height) > crate::bitmap::MAX_PIXELS { + bail!(OverflowError::BitmapDimension); + } + + // `GSBPP` - The number of bits per gray-scale value. + let bits_per_pixel = params.bits_per_pixel; + let stride = width.div_ceil(WORD_BITS); + // `GSKIP` - The skip mask (if GSUSESKIP = 1). + let skip_mask = params.skip_mask; + let skip_stride = width.div_ceil(32); + // `GSTEMPLATE` - The template used for bitplane decoding. + let template = params.template; + + // Table C.4: Adaptive template pixel positions. + let at_pixels: [AdaptiveTemplatePixel; 4] = match template { + Template::Template0 => [ + AdaptiveTemplatePixel { x: 3, y: -1 }, + AdaptiveTemplatePixel { x: -3, y: -1 }, + AdaptiveTemplatePixel { x: 2, y: -2 }, + AdaptiveTemplatePixel { x: -2, y: -2 }, + ], + Template::Template1 => [ + AdaptiveTemplatePixel { x: 3, y: -1 }, + // Unused. + AdaptiveTemplatePixel::default(), + AdaptiveTemplatePixel::default(), + AdaptiveTemplatePixel::default(), + ], + Template::Template2 | Template::Template3 => [ + AdaptiveTemplatePixel { x: 2, y: -1 }, + // Unused. + AdaptiveTemplatePixel::default(), + AdaptiveTemplatePixel::default(), + AdaptiveTemplatePixel::default(), + ], + }; + + let mut decoder = ArithmeticDecoder::new(data); + ctx.contexts.clear(); + ctx.contexts.resize( + 1 << template.context_bits(), + ArithmeticDecoderContext::default(), + ); + + macro_rules! gs_decode_loop { + ($gather:expr) => { + decode_bitplanes(width, height, stride, bits_per_pixel, |_, bitplane| { + // Table C.4: "GBW = GSW, GBH = GSH, TPGDON = 0" + let mut gatherer = ContextGatherer::new(template, &at_pixels); + + for y in 0..height { + gatherer.start_row(bitplane, y); + for x in 0..width { + gatherer.maybe_reload_buffers(bitplane, x); + + // Table C.4: "USESKIP = GSUSESKIP, SKIP = GSKIP" + if let Some(mask) = skip_mask { + let word_idx = (y * skip_stride + x / 32) as usize; + let bit_pos = 31 - (x % 32); + if (mask[word_idx] >> bit_pos) & 1 != 0 { + // Still need to update the context. + let _ = ($gather)(&mut gatherer, bitplane, x); + gatherer.update_current_row(x, 0); + continue; + } + } + + let context = ($gather)(&mut gatherer, bitplane, x); + let pixel = decoder.read_bit(&mut ctx.contexts[context as usize]); + let value = pixel as u8; + + bitplane.set_pixel(x, y, value); + gatherer.update_current_row(x, value); + } + } + + Ok(()) + }) + }; + } + + match template { + Template::Template0 => gs_decode_loop!(ContextGatherer::gather_template0_default), + Template::Template1 => gs_decode_loop!(ContextGatherer::gather_template1_default), + Template::Template2 => gs_decode_loop!(ContextGatherer::gather_template2_default), + Template::Template3 => gs_decode_loop!(ContextGatherer::gather_template3_default), + } +} + +/// The bitplane decoding and gray value computation procedure (C.5). +/// +/// The closure `decode_next` is called for each bitplane, receiving the bitplane +/// index (`GSBPP`-1 down to 0) and a zeroed bitmap to decode into. +fn decode_bitplanes( + width: u32, + height: u32, + stride: u32, + bits_per_pixel: u32, + mut decode_next: F, +) -> Result> +where + F: FnMut(u32, &mut Bitmap) -> Result<()>, +{ + let size = width as usize * height as usize; + // `GSVALS` - The decoded gray-scale image array. + let mut values = vec![0_u32; size]; + + let mut bitplane = Bitmap::new(width, height)?; + let mut prev_plane = vec![0; bitplane.data.len()]; + + simd::dispatch!(Level::new(), simd => { + for j in (0..bits_per_pixel).rev() { + bitplane.data.fill(0); + decode_next(j, &mut bitplane)?; + + // Done every time except the first time. + if j < bits_per_pixel - 1 { + // C.5 step 3b: "GSPLANES[J][x, y] = GSPLANES[J + 1][x, y] XOR GSPLANES[J][x, y]" + xor_planes(simd, &mut bitplane.data, &prev_plane); + } + + // C.5 step 4: "GSVALS[x, y] = sum(J=0 to GSBPP-1) GSPLANES[J][x, y] * 2^J" + extract_bits(simd, &bitplane.data, &mut values, width, height, stride, j); + + // C.5 step 3c: "Set J = J - 1." + prev_plane.copy_from_slice(&bitplane.data); + } + + Ok(values) + }) +} + +#[inline(always)] +fn xor_planes(simd: S, plane: &mut [u32], prev: &[u32]) { + let simd_end = plane.len() & !7; + + for i in (0..simd_end).step_by(simd::SIMD_WIDTH) { + let chunk = &mut plane[i..i + simd::SIMD_WIDTH]; + let mut a = u32x8::from_slice(simd, chunk); + let b = u32x8::from_slice(simd, &prev[i..i + simd::SIMD_WIDTH]); + a ^= b; + a.store(chunk); + } + + for i in simd_end..plane.len() { + plane[i] ^= prev[i]; + } +} + +#[inline(always)] +fn extract_bits( + simd: S, + data: &[u32], + values: &mut [u32], + width: u32, + height: u32, + stride: u32, + j: u32, +) { + /// Bit masks for extracting individual bits from a packed u32 word (MSB-first). + /// `BIT_MASKS[i] = 1 << (31 - i)`. + const BIT_MASKS: [u32; 32] = { + let mut masks = [0_u32; 32]; + let mut i = 0; + while i < 32 { + masks[i] = 1 << (31 - i); + i += 1; + } + masks + }; + + let bit_value = u32x8::splat(simd, 1_u32 << j); + let zero = u32x8::splat(simd, 0); + let simd_end = (width as usize) & !7; + + for y in 0..height as usize { + let row_data = y * stride as usize; + let row_vals = y * width as usize; + + // Handle in chunks of 8. + for x in (0..simd_end).step_by(simd::SIMD_WIDTH) { + let word = data[row_data + x / 32]; + let bit_offset = x % 32; + + let splatted = u32x8::splat(simd, word); + let masks = + u32x8::from_slice(simd, &BIT_MASKS[bit_offset..bit_offset + simd::SIMD_WIDTH]); + let extracted = splatted & masks; + let nonzero = extracted.simd_gt(zero); + let contribution = nonzero.select(bit_value, zero); + let vals = &mut values[row_vals + x..row_vals + x + simd::SIMD_WIDTH]; + let current = u32x8::from_slice(simd, vals); + (current | contribution).store(vals); + } + + // Handle the tail. + for x in simd_end..width as usize { + let word_idx = row_data + x / 32; + let bit_pos = 31 - (x % 32); + if (data[word_idx] >> bit_pos) & 1 != 0 { + values[row_vals + x] |= 1 << j; + } + } + } +} diff --git a/vendor/hayro-jbig2/src/huffman_table.rs b/vendor/hayro-jbig2/src/huffman_table.rs new file mode 100644 index 000000000..2183c8eea --- /dev/null +++ b/vendor/hayro-jbig2/src/huffman_table.rs @@ -0,0 +1,621 @@ +//! Huffman table decoding, described in Annex B. + +use alloc::rc::Rc; +use alloc::vec; +use alloc::vec::Vec; +use core::num::NonZeroU32; + +use crate::error::{HuffmanError, ParseError, Result, bail}; +use crate::lazy::Lazy; +use crate::reader::Reader; + +include!("huffman_tables_generated.rs"); + +/// Maximum number of nodes in an inline Huffman table. +const INLINE_TABLE_SIZE: usize = 43; + +/// A queryable Huffman table. +#[derive(Debug, Clone)] +pub(crate) struct HuffmanTable(Rc); + +impl HuffmanTable { + /// Create a new inline Huffman table from a fixed-size node array. + fn from_inline(nodes: [HuffmanNode; INLINE_TABLE_SIZE]) -> Self { + Self(Rc::new(InnerHuffmanTable::Inline { nodes })) + } + + /// Create a new dynamic Huffman table from a vector of nodes. + fn from_dynamic(nodes: Vec) -> Self { + Self(Rc::new(InnerHuffmanTable::Dynamic { nodes })) + } + + /// Decode a value from the bit reader using this Huffman table + /// (B.4 "Using a Huffman table"). + /// + /// Returns `Ok(None)` for out-of-band (OOB) values, `Ok(Some(value))` for decoded values. + pub(crate) fn decode(&self, reader: &mut Reader<'_>) -> Result> { + let nodes: &[HuffmanNode] = match self.0.as_ref() { + InnerHuffmanTable::Inline { nodes } => nodes, + InnerHuffmanTable::Dynamic { nodes } => nodes, + }; + + HuffmanNode::decode_from(nodes, 0, reader) + } + + /// Decode a value using the huffman table, erroring out in case an OOB + /// was encountered. + pub(crate) fn decode_no_oob(&self, reader: &mut Reader<'_>) -> Result { + Ok(self.decode(reader)?.ok_or(HuffmanError::UnexpectedOob)?) + } + + /// Build a Huffman table from table line definitions (B.3 "Assigning + /// the prefix codes"). + pub(crate) fn build(lines: &[TableLine]) -> Result { + // `NTEMP` - Number of table lines. + let line_count = lines.len(); + + // Step 1: "Build a histogram in the array LENCOUNT counting the number of times + // each prefix length value occurs in PREFLEN: LENCOUNT[I] is the number of times + // that the value I occurs in the array PREFLEN." + // `LENMAX` - Maximum prefix length. + let max_prefix_length = lines.iter().map(|l| l.prefix_length).max().unwrap_or(0) as usize; + + if max_prefix_length > 32 { + bail!(HuffmanError::PrefixLengthTooLarge); + } + + // `LENCOUNT` - Histogram of prefix lengths. + let mut length_counts = vec![0_u32; max_prefix_length + 1]; + for line in lines { + length_counts[line.prefix_length as usize] += 1; + } + + // Step 2: "Let LENMAX be the largest value for which LENCOUNT[LENMAX] > 0. Set: + // CURLEN = 1, FIRSTCODE[0] = 0, LENCOUNT[0] = 0" + // `FIRSTCODE` - First code value for each length. + let mut first_code_per_length = vec![0_u32; max_prefix_length + 1]; + // `CODES` - Assigned prefix codes for each line. + let mut assigned_codes = vec![0_u32; line_count]; + length_counts[0] = 0; + + // Step 3: "While CURLEN ≤ LENMAX, perform the following operations:" + // `CURLEN` - Current length being processed. + for current_length in 1..=max_prefix_length { + // a) "Set: FIRSTCODE[CURLEN] = (FIRSTCODE[CURLEN − 1] + LENCOUNT[CURLEN − 1]) × 2 + // CURCODE = FIRSTCODE[CURLEN] + // CURTEMP = 0" + first_code_per_length[current_length] = (first_code_per_length[current_length - 1] + + length_counts[current_length - 1]) + .checked_mul(2) + .ok_or(HuffmanError::InvalidCode)?; + // `CURCODE` - Current code value being assigned. + let mut current_code = first_code_per_length[current_length]; + + // b) "While CURTEMP < NTEMP, perform the following operations:" + // `CURTEMP` - Current line index. + for line_index in 0..line_count { + // i) "If PREFLEN[CURTEMP] = CURLEN, then set: + // CODES[CURTEMP] = CURCODE + // CURCODE = CURCODE + 1" + if lines[line_index].prefix_length as usize == current_length { + assigned_codes[line_index] = current_code; + current_code = current_code + .checked_add(1) + .ok_or(HuffmanError::InvalidCode)?; + } + // ii) "Set CURTEMP = CURTEMP + 1" (implicit in for loop) + } + // c) "Set CURLEN = CURLEN + 1" (implicit in for loop) + } + + // Build tree from assigned codes. + let mut nodes = vec![HuffmanNode::new_intermediate()]; + + for (i, line) in lines.iter().enumerate() { + // "Note that the PREFLEN value 0 indicates that the table line is never used." + if line.prefix_length == 0 { + continue; + } + + Self::insert_code( + &mut nodes, + 0, // root index + assigned_codes[i], + line.prefix_length, + line.range_low, + line.range_length, + line.is_lower, + line.is_out_of_band, + )?; + } + + Ok(Self::from_dynamic(nodes)) + } + + /// Build a uniform Huffman table where all symbols have the same code length. + /// + /// This is used for symbol ID encoding (6.5.8.2.3): "For each value of i from + /// 0 to SDNUMINSYMS + NSYMSDECODED – 1, set SBSYMCODES[i] to the binary + /// representation of i using a SBSYMCODELEN-bit string." + pub(crate) fn build_uniform(num_symbols: u32, code_length: u32) -> Result { + let lines: Vec = (0..num_symbols) + .map(|i| TableLine::new(i as i32, code_length as u8, 0)) + .collect(); + Self::build(&lines) + } + + /// Insert a code into the Huffman tree. + fn insert_code( + nodes: &mut Vec, + node_index: u32, + code: u32, + prefix_length: u8, + range_low: i32, + range_length: u8, + is_lower: bool, + is_out_of_band: bool, + ) -> Result<()> { + if prefix_length == 0 { + // We've consumed all bits, this should be a leaf. + nodes[node_index as usize] = + HuffmanNode::new_leaf(range_low, range_length, is_lower, is_out_of_band); + return Ok(()); + } + + // Get the next bit (MSB first). + let bit = (code >> (prefix_length - 1)) & 1; + let remaining_code = code & ((1 << (prefix_length - 1)) - 1); + + let child_index = match nodes[node_index as usize].get_child(bit == 0) { + Some(idx) => idx, + None => { + let new_idx = NonZeroU32::new(nodes.len() as u32).unwrap(); + nodes.push(HuffmanNode::new_intermediate()); + nodes[node_index as usize].set_child(bit == 0, new_idx)?; + new_idx + } + }; + + Self::insert_code( + nodes, + child_index.get(), + remaining_code, + prefix_length - 1, + range_low, + range_length, + is_lower, + is_out_of_band, + ) + } + + /// Read a custom Huffman table from the bitstream (B.2 "Decoding a code table"). + pub(crate) fn read_custom(reader: &mut Reader<'_>) -> Result { + // 1) "Decode the code table flags field as described in B.2.1. This sets the values + // HTOOB, HTPS and HTRS." + let flags = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + + // `HTOOB` + let has_out_of_band = (flags & 1) != 0; + // `HTPS` + let prefix_length_bits = ((flags >> 1) & 7) + 1; + // `HTRS` + let range_length_bits = ((flags >> 4) & 7) + 1; + + // 2) "Decode the code table lowest value field as described in B.2.2. Let HTLOW be + // the value decoded." + // `HTLOW` + let minimum_value = reader.read_i32().ok_or(ParseError::UnexpectedEof)?; + + // 3) "Decode the code table highest value field as described in B.2.3. Let HTHIGH be + // the value decoded." + // `HTHIGH` + let maximum_value = reader.read_i32().ok_or(ParseError::UnexpectedEof)?; + + // 4) "Set: CURRANGELOW = HTLOW, NTEMP = 0" + let mut lines = Vec::new(); + // `CURRANGELOW` + let mut current_range_low = minimum_value; + + // 5) "Decode each table line as follows:" + // d) "If CURRANGELOW ≥ HTHIGH then proceed to step 6." + while current_range_low < maximum_value { + // a) "Read HTPS bits. Set PREFLEN[NTEMP] to the value decoded." + let prefix_length = reader + .read_bits(prefix_length_bits) + .ok_or(HuffmanError::InvalidCode)? as u8; + // b) "Read HTRS bits. Let RANGELEN[NTEMP] be the value decoded." + let range_length = reader + .read_bits(range_length_bits) + .ok_or(HuffmanError::InvalidCode)? as u8; + + // c) "Set: RANGELOW[NTEMP] = CURRANGELOW + // CURRANGELOW = CURRANGELOW + 2^RANGELEN[NTEMP] + // NTEMP = NTEMP + 1" + lines.push(TableLine::new( + current_range_low, + prefix_length, + range_length, + )); + + let range_size = 1_i64 + .checked_shl(range_length as u32) + .ok_or(HuffmanError::InvalidCode)?; + let next_range_low = (current_range_low as i64) + .checked_add(range_size) + .ok_or(HuffmanError::InvalidCode)?; + current_range_low = + i32::try_from(next_range_low).map_err(|_| HuffmanError::InvalidCode)?; + } + + // 6) "Read HTPS bits. Let LOWPREFLEN be the value read." + // 7) "Set: PREFLEN[NTEMP] = LOWPREFLEN, RANGELEN[NTEMP] = 32, + // RANGELOW[NTEMP] = HTLOW − 1, NTEMP = NTEMP + 1 + // This is the lower range table line for this table." + lines.push(TableLine::lower( + minimum_value + .checked_sub(1) + .ok_or(HuffmanError::InvalidCode)?, + reader + .read_bits(prefix_length_bits) + .ok_or(HuffmanError::InvalidCode)? as u8, + 32, + )); + + // 8) "Read HTPS bits. Let HIGHPREFLEN be the value read." + // 9) "Set: PREFLEN[NTEMP] = HIGHPREFLEN, RANGELEN[NTEMP] = 32, + // RANGELOW[NTEMP] = HTHIGH, NTEMP = NTEMP + 1 + // This is the upper range table line for this table." + lines.push(TableLine::upper( + current_range_low, + reader + .read_bits(prefix_length_bits) + .ok_or(HuffmanError::InvalidCode)? as u8, + 32, + )); + + // 10) "If HTOOB is 1, then: + // a) Read HTPS bits. Let OOBPREFLEN be the value read. + // b) Set: PREFLEN[NTEMP] = OOBPREFLEN, NTEMP = NTEMP + 1 + // This is the out-of-band table line for this table." + if has_out_of_band { + lines.push(TableLine::oob( + reader + .read_bits(prefix_length_bits) + .ok_or(HuffmanError::InvalidCode)? as u8, + )); + } + + // 11) "Create the prefix codes using the algorithm described in B.3." + Self::build(&lines) + } +} + +/// A table line definition used to build the Huffman tree. +pub(crate) struct TableLine { + /// `RANGELOW` - The base value for computing the decoded value. + /// For normal/upper lines: value = `range_low` + offset + /// For lower lines: value = `range_low` - offset + pub(crate) range_low: i32, + /// `PREFLEN` - Prefix code length. + pub(crate) prefix_length: u8, + /// `RANGELEN` - Number of additional bits. + pub(crate) range_length: u8, + /// True if this is a lower range line (uses subtraction). + pub(crate) is_lower: bool, + /// `OOB` - True if this is the out-of-band marker. + pub(crate) is_out_of_band: bool, +} + +impl TableLine { + /// Create a normal table line. + pub(crate) const fn new(range_low: i32, prefix_length: u8, range_length: u8) -> Self { + Self { + range_low, + prefix_length, + range_length, + is_lower: false, + is_out_of_band: false, + } + } + + /// Create a lower range line (-∞...`range_high`). + const fn lower(range_high: i32, prefix_length: u8, range_length: u8) -> Self { + Self { + range_low: range_high, + prefix_length, + range_length, + is_lower: true, + is_out_of_band: false, + } + } + + /// Create an upper range line (`range_low`...+∞). + const fn upper(range_low: i32, prefix_length: u8, range_length: u8) -> Self { + Self { + range_low, + prefix_length, + range_length, + is_lower: false, + is_out_of_band: false, + } + } + + /// Create an out-of-band marker line. + const fn oob(prefix_length: u8) -> Self { + Self { + range_low: 0, + prefix_length, + range_length: 0, + is_lower: false, + is_out_of_band: true, + } + } +} + +/// A node in the Huffman tree. +#[derive(Debug, Clone, Copy)] +enum HuffmanNode { + /// Intermediate node. + Intermediate { + zero: Option, + one: Option, + }, + /// Leaf node. + Leaf(LeafData), + /// Empty node (padding to fill fixed-size arrays in inline tables). + Empty, +} + +impl HuffmanNode { + fn new_intermediate() -> Self { + Self::Intermediate { + zero: None, + one: None, + } + } + + fn new_leaf(range_low: i32, range_length: u8, is_lower: bool, is_out_of_band: bool) -> Self { + Self::Leaf(LeafData { + range_low, + range_length, + is_lower, + is_out_of_band, + }) + } + + /// Get the child index for a given bit (0 or 1). + fn get_child(&self, child_zero: bool) -> Option { + match self { + Self::Intermediate { zero, one } => { + if child_zero { + *zero + } else { + *one + } + } + _ => None, + } + } + + /// Set the child index for a given bit (0 or 1). + fn set_child( + &mut self, + child_zero: bool, + index: NonZeroU32, + ) -> core::result::Result<(), HuffmanError> { + match self { + Self::Intermediate { zero, one } => { + if child_zero { + *zero = Some(index); + } else { + *one = Some(index); + } + Ok(()) + } + _ => Err(HuffmanError::ConflictingCodes), + } + } + + /// Implements B.4 "Using a Huffman table". + fn decode_from( + nodes: &[Self], + mut node_index: u32, + reader: &mut Reader<'_>, + ) -> Result> { + // 1) "Read one bit at a time until the bit string read matches the code assigned to + // one of the table lines." + loop { + match nodes[node_index as usize] { + Self::Intermediate { zero, one } => { + let bit = reader.read_bit().ok_or(ParseError::UnexpectedEof)?; + let child_index = if bit == 0 { zero } else { one }; + node_index = child_index.ok_or(HuffmanError::InvalidCode)?.get(); + } + Self::Leaf(leaf) => { + // 3) "If HTOOB is 1 for this table, and table line I is the out-of-band + // table line for this table, then set: HTVAL = OOB" + if leaf.is_out_of_band { + return Ok(None); + } + + // 2) "Read RANGELEN[I] bits. Let HTOFFSET be the value read." + // `HTOFFSET` + let range_offset = reader + .read_bits(leaf.range_length) + .ok_or(HuffmanError::InvalidCode)? + as i32; + + // 4) "Otherwise, if table line I is the lower range table line for this + // table, then set: HTVAL = RANGELOW[I] − HTOFFSET" + // 5) "Otherwise, set: HTVAL = RANGELOW[I] + HTOFFSET" + // `HTVAL` + let value = if leaf.is_lower { + leaf.range_low - range_offset + } else { + leaf.range_low + range_offset + }; + + return Ok(Some(value)); + } + Self::Empty => { + bail!(HuffmanError::InvalidCode); + } + } + } + } +} + +/// Information stored at a leaf node of the Huffman tree. +#[derive(Debug, Clone, Copy)] +struct LeafData { + /// `RANGELOW` - The base value for computing the decoded value. + range_low: i32, + /// `RANGELEN` - Number of additional bits to read. + range_length: u8, + /// True if this is a lower range line (uses subtraction). + is_lower: bool, + /// `OOB` - True if this is the out-of-band marker. + is_out_of_band: bool, +} + +/// The inner representation of a Huffman table. +/// +/// This can be either an inline table (fixed-size array for standard tables) +/// or a dynamic table (Vec for runtime-built custom tables). +#[derive(Debug, Clone)] +#[allow( + clippy::large_enum_variant, + reason = "Inline variant is expected to be large." +)] +enum InnerHuffmanTable { + Inline { + nodes: [HuffmanNode; INLINE_TABLE_SIZE], + }, + Dynamic { + nodes: Vec, + }, +} + +/// Standard Huffman tables (`TABLE_A` through `TABLE_O`). +#[derive(Debug)] +pub(crate) struct StandardHuffmanTables { + table_a: Lazy, + table_b: Lazy, + table_c: Lazy, + table_d: Lazy, + table_e: Lazy, + table_f: Lazy, + table_g: Lazy, + table_h: Lazy, + table_i: Lazy, + table_j: Lazy, + table_k: Lazy, + table_l: Lazy, + table_m: Lazy, + table_n: Lazy, + table_o: Lazy, +} + +impl Default for StandardHuffmanTables { + fn default() -> Self { + Self::new() + } +} + +impl StandardHuffmanTables { + /// Create a new instance. + pub(crate) fn new() -> Self { + Self { + table_a: Lazy::new(|| HuffmanTable::from_inline(TABLE_A)), + table_b: Lazy::new(|| HuffmanTable::from_inline(TABLE_B)), + table_c: Lazy::new(|| HuffmanTable::from_inline(TABLE_C)), + table_d: Lazy::new(|| HuffmanTable::from_inline(TABLE_D)), + table_e: Lazy::new(|| HuffmanTable::from_inline(TABLE_E)), + table_f: Lazy::new(|| HuffmanTable::from_inline(TABLE_F)), + table_g: Lazy::new(|| HuffmanTable::from_inline(TABLE_G)), + table_h: Lazy::new(|| HuffmanTable::from_inline(TABLE_H)), + table_i: Lazy::new(|| HuffmanTable::from_inline(TABLE_I)), + table_j: Lazy::new(|| HuffmanTable::from_inline(TABLE_J)), + table_k: Lazy::new(|| HuffmanTable::from_inline(TABLE_K)), + table_l: Lazy::new(|| HuffmanTable::from_inline(TABLE_L)), + table_m: Lazy::new(|| HuffmanTable::from_inline(TABLE_M)), + table_n: Lazy::new(|| HuffmanTable::from_inline(TABLE_N)), + table_o: Lazy::new(|| HuffmanTable::from_inline(TABLE_O)), + } + } + + /// Get Table B.1 (`TABLE_A`). + pub(crate) fn table_a(&self) -> &HuffmanTable { + self.table_a.get(|| HuffmanTable::from_inline(TABLE_A)) + } + + /// Get Table B.2 (`TABLE_B`). + pub(crate) fn table_b(&self) -> &HuffmanTable { + self.table_b.get(|| HuffmanTable::from_inline(TABLE_B)) + } + + /// Get Table B.3 (`TABLE_C`). + pub(crate) fn table_c(&self) -> &HuffmanTable { + self.table_c.get(|| HuffmanTable::from_inline(TABLE_C)) + } + + /// Get Table B.4 (`TABLE_D`). + pub(crate) fn table_d(&self) -> &HuffmanTable { + self.table_d.get(|| HuffmanTable::from_inline(TABLE_D)) + } + + /// Get Table B.5 (`TABLE_E`). + pub(crate) fn table_e(&self) -> &HuffmanTable { + self.table_e.get(|| HuffmanTable::from_inline(TABLE_E)) + } + + /// Get Table B.6 (`TABLE_F`). + pub(crate) fn table_f(&self) -> &HuffmanTable { + self.table_f.get(|| HuffmanTable::from_inline(TABLE_F)) + } + + /// Get Table B.7 (`TABLE_G`). + pub(crate) fn table_g(&self) -> &HuffmanTable { + self.table_g.get(|| HuffmanTable::from_inline(TABLE_G)) + } + + /// Get Table B.8 (`TABLE_H`). + pub(crate) fn table_h(&self) -> &HuffmanTable { + self.table_h.get(|| HuffmanTable::from_inline(TABLE_H)) + } + + /// Get Table B.9 (`TABLE_I`). + pub(crate) fn table_i(&self) -> &HuffmanTable { + self.table_i.get(|| HuffmanTable::from_inline(TABLE_I)) + } + + /// Get Table B.10 (`TABLE_J`). + pub(crate) fn table_j(&self) -> &HuffmanTable { + self.table_j.get(|| HuffmanTable::from_inline(TABLE_J)) + } + + /// Get Table B.11 (`TABLE_K`). + pub(crate) fn table_k(&self) -> &HuffmanTable { + self.table_k.get(|| HuffmanTable::from_inline(TABLE_K)) + } + + /// Get Table B.12 (`TABLE_L`). + pub(crate) fn table_l(&self) -> &HuffmanTable { + self.table_l.get(|| HuffmanTable::from_inline(TABLE_L)) + } + + /// Get Table B.13 (`TABLE_M`). + pub(crate) fn table_m(&self) -> &HuffmanTable { + self.table_m.get(|| HuffmanTable::from_inline(TABLE_M)) + } + + /// Get Table B.14 (`TABLE_N`). + pub(crate) fn table_n(&self) -> &HuffmanTable { + self.table_n.get(|| HuffmanTable::from_inline(TABLE_N)) + } + + /// Get Table B.15 (`TABLE_O`). + pub(crate) fn table_o(&self) -> &HuffmanTable { + self.table_o.get(|| HuffmanTable::from_inline(TABLE_O)) + } +} diff --git a/vendor/hayro-jbig2/src/huffman_tables_generated.rs b/vendor/hayro-jbig2/src/huffman_tables_generated.rs new file mode 100644 index 000000000..d3bfe2b62 --- /dev/null +++ b/vendor/hayro-jbig2/src/huffman_tables_generated.rs @@ -0,0 +1,727 @@ +// Auto-generated Huffman tables. Do not edit manually. +// Generated by generate_huffman_tables.py + +type N = HuffmanNode; +type L = LeafData; + +const fn nz(n: u32) -> NonZeroU32 { NonZeroU32::new(n).unwrap() } + +/// Standard Huffman table A (Table B.1). +#[rustfmt::skip] +const TABLE_A: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(1)), one: Some(nz(2)) }, + N::Leaf(L { range_low: 0, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(3)), one: Some(nz(4)) }, + N::Leaf(L { range_low: 16, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(5)), one: Some(nz(6)) }, + N::Leaf(L { range_low: 272, range_length: 16, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 65808, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table B (Table B.2). +#[rustfmt::skip] +const TABLE_B: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(1)), one: Some(nz(2)) }, + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(3)), one: Some(nz(4)) }, + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(5)), one: Some(nz(6)) }, + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(7)), one: Some(nz(8)) }, + N::Leaf(L { range_low: 3, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(9)), one: Some(nz(10)) }, + N::Leaf(L { range_low: 11, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(11)), one: Some(nz(12)) }, + N::Leaf(L { range_low: 75, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: true }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table C (Table B.3). +#[rustfmt::skip] +const TABLE_C: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(9)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(10)), one: Some(nz(2)) }, + N::Intermediate { zero: Some(nz(11)), one: Some(nz(3)) }, + N::Intermediate { zero: Some(nz(12)), one: Some(nz(4)) }, + N::Intermediate { zero: Some(nz(13)), one: Some(nz(5)) }, + N::Intermediate { zero: Some(nz(16)), one: Some(nz(6)) }, + N::Intermediate { zero: Some(nz(15)), one: Some(nz(7)) }, + N::Intermediate { zero: Some(nz(8)), one: Some(nz(14)) }, + N::Leaf(L { range_low: -256, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 3, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 11, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -257, range_length: 32, is_lower: true, is_out_of_band: false }), + N::Leaf(L { range_low: 75, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: true }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table D (Table B.4). +#[rustfmt::skip] +const TABLE_D: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(1)), one: Some(nz(2)) }, + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(3)), one: Some(nz(4)) }, + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(5)), one: Some(nz(6)) }, + N::Leaf(L { range_low: 3, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(7)), one: Some(nz(8)) }, + N::Leaf(L { range_low: 4, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(9)), one: Some(nz(10)) }, + N::Leaf(L { range_low: 12, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 76, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table E (Table B.5). +#[rustfmt::skip] +const TABLE_E: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(8)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(9)), one: Some(nz(2)) }, + N::Intermediate { zero: Some(nz(10)), one: Some(nz(3)) }, + N::Intermediate { zero: Some(nz(11)), one: Some(nz(4)) }, + N::Intermediate { zero: Some(nz(12)), one: Some(nz(5)) }, + N::Intermediate { zero: Some(nz(14)), one: Some(nz(6)) }, + N::Intermediate { zero: Some(nz(7)), one: Some(nz(13)) }, + N::Leaf(L { range_low: -255, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 3, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 4, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 12, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -256, range_length: 32, is_lower: true, is_out_of_band: false }), + N::Leaf(L { range_low: 76, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table F (Table B.6). +#[rustfmt::skip] +const TABLE_F: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(16)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(6)), one: Some(nz(2)) }, + N::Intermediate { zero: Some(nz(21)), one: Some(nz(3)) }, + N::Intermediate { zero: Some(nz(4)), one: Some(nz(13)) }, + N::Intermediate { zero: Some(nz(5)), one: Some(nz(12)) }, + N::Leaf(L { range_low: -2048, range_length: 10, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(7)), one: Some(nz(10)) }, + N::Intermediate { zero: Some(nz(8)), one: Some(nz(9)) }, + N::Leaf(L { range_low: -1024, range_length: 9, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -512, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(11)), one: Some(nz(15)) }, + N::Leaf(L { range_low: -256, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -128, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(14)), one: Some(nz(24)) }, + N::Leaf(L { range_low: -64, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -32, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(17)), one: Some(nz(18)) }, + N::Leaf(L { range_low: 0, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(19)), one: Some(nz(20)) }, + N::Leaf(L { range_low: 128, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 256, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(22)), one: Some(nz(23)) }, + N::Leaf(L { range_low: 512, range_length: 9, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 1024, range_length: 10, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(25)), one: Some(nz(26)) }, + N::Leaf(L { range_low: -2049, range_length: 32, is_lower: true, is_out_of_band: false }), + N::Leaf(L { range_low: 2048, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table G (Table B.7). +#[rustfmt::skip] +const TABLE_G: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(5)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(2)), one: Some(nz(9)) }, + N::Intermediate { zero: Some(nz(3)), one: Some(nz(14)) }, + N::Intermediate { zero: Some(nz(4)), one: Some(nz(8)) }, + N::Leaf(L { range_low: -1024, range_length: 9, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(6)), one: Some(nz(23)) }, + N::Intermediate { zero: Some(nz(7)), one: Some(nz(22)) }, + N::Leaf(L { range_low: -512, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -256, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(10)), one: Some(nz(17)) }, + N::Intermediate { zero: Some(nz(21)), one: Some(nz(11)) }, + N::Intermediate { zero: Some(nz(12)), one: Some(nz(13)) }, + N::Leaf(L { range_low: -128, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -64, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(15)), one: Some(nz(16)) }, + N::Leaf(L { range_low: -32, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(18)), one: Some(nz(26)) }, + N::Intermediate { zero: Some(nz(19)), one: Some(nz(20)) }, + N::Leaf(L { range_low: 32, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 64, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 128, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 256, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(24)), one: Some(nz(25)) }, + N::Leaf(L { range_low: 512, range_length: 9, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 1024, range_length: 10, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(27)), one: Some(nz(28)) }, + N::Leaf(L { range_low: -1025, range_length: 32, is_lower: true, is_out_of_band: false }), + N::Leaf(L { range_low: 2048, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table H (Table B.8). +#[rustfmt::skip] +const TABLE_H: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(19)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(16)), one: Some(nz(2)) }, + N::Intermediate { zero: Some(nz(21)), one: Some(nz(3)) }, + N::Intermediate { zero: Some(nz(24)), one: Some(nz(4)) }, + N::Intermediate { zero: Some(nz(33)), one: Some(nz(5)) }, + N::Intermediate { zero: Some(nz(14)), one: Some(nz(6)) }, + N::Intermediate { zero: Some(nz(7)), one: Some(nz(9)) }, + N::Intermediate { zero: Some(nz(8)), one: Some(nz(12)) }, + N::Leaf(L { range_low: -15, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(10)), one: Some(nz(37)) }, + N::Intermediate { zero: Some(nz(11)), one: Some(nz(13)) }, + N::Leaf(L { range_low: -7, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -5, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -3, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(15)), one: Some(nz(35)) }, + N::Leaf(L { range_low: -2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(27)), one: Some(nz(17)) }, + N::Intermediate { zero: Some(nz(18)), one: Some(nz(29)) }, + N::Leaf(L { range_low: -1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(20)), one: Some(nz(40)) }, + N::Leaf(L { range_low: 0, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(30)), one: Some(nz(22)) }, + N::Intermediate { zero: Some(nz(23)), one: Some(nz(31)) }, + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(32)), one: Some(nz(25)) }, + N::Intermediate { zero: Some(nz(26)), one: Some(nz(28)) }, + N::Leaf(L { range_low: 3, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 4, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 20, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 22, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 38, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 70, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 134, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(34)), one: Some(nz(36)) }, + N::Leaf(L { range_low: 262, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 390, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 646, range_length: 10, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(38)), one: Some(nz(39)) }, + N::Leaf(L { range_low: -16, range_length: 32, is_lower: true, is_out_of_band: false }), + N::Leaf(L { range_low: 1670, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: true }), + N::Empty, + N::Empty, +]; + +/// Standard Huffman table I (Table B.9). +#[rustfmt::skip] +const TABLE_I: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(19)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(16)), one: Some(nz(2)) }, + N::Intermediate { zero: Some(nz(23)), one: Some(nz(3)) }, + N::Intermediate { zero: Some(nz(26)), one: Some(nz(4)) }, + N::Intermediate { zero: Some(nz(35)), one: Some(nz(5)) }, + N::Intermediate { zero: Some(nz(14)), one: Some(nz(6)) }, + N::Intermediate { zero: Some(nz(7)), one: Some(nz(9)) }, + N::Intermediate { zero: Some(nz(8)), one: Some(nz(12)) }, + N::Leaf(L { range_low: -31, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(10)), one: Some(nz(39)) }, + N::Intermediate { zero: Some(nz(11)), one: Some(nz(13)) }, + N::Leaf(L { range_low: -15, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -11, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -7, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(15)), one: Some(nz(37)) }, + N::Leaf(L { range_low: -5, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(29)), one: Some(nz(17)) }, + N::Intermediate { zero: Some(nz(18)), one: Some(nz(31)) }, + N::Leaf(L { range_low: -3, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(42)), one: Some(nz(20)) }, + N::Intermediate { zero: Some(nz(21)), one: Some(nz(22)) }, + N::Leaf(L { range_low: -1, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 1, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(32)), one: Some(nz(24)) }, + N::Intermediate { zero: Some(nz(25)), one: Some(nz(33)) }, + N::Leaf(L { range_low: 3, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(34)), one: Some(nz(27)) }, + N::Intermediate { zero: Some(nz(28)), one: Some(nz(30)) }, + N::Leaf(L { range_low: 5, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 7, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 39, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 43, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 75, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 139, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 267, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(36)), one: Some(nz(38)) }, + N::Leaf(L { range_low: 523, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 779, range_length: 9, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 1291, range_length: 11, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(40)), one: Some(nz(41)) }, + N::Leaf(L { range_low: -32, range_length: 32, is_lower: true, is_out_of_band: false }), + N::Leaf(L { range_low: 3339, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: true }), +]; + +/// Standard Huffman table J (Table B.10). +#[rustfmt::skip] +const TABLE_J: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(16)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(40)), one: Some(nz(2)) }, + N::Intermediate { zero: Some(nz(13)), one: Some(nz(3)) }, + N::Intermediate { zero: Some(nz(28)), one: Some(nz(4)) }, + N::Intermediate { zero: Some(nz(5)), one: Some(nz(8)) }, + N::Intermediate { zero: Some(nz(35)), one: Some(nz(6)) }, + N::Intermediate { zero: Some(nz(7)), one: Some(nz(12)) }, + N::Leaf(L { range_low: -21, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(22)), one: Some(nz(9)) }, + N::Intermediate { zero: Some(nz(10)), one: Some(nz(37)) }, + N::Intermediate { zero: Some(nz(11)), one: Some(nz(24)) }, + N::Leaf(L { range_low: -5, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -4, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(14)), one: Some(nz(19)) }, + N::Intermediate { zero: Some(nz(15)), one: Some(nz(18)) }, + N::Leaf(L { range_low: -3, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(17)), one: Some(nz(25)) }, + N::Leaf(L { range_low: -2, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(26)), one: Some(nz(20)) }, + N::Intermediate { zero: Some(nz(21)), one: Some(nz(27)) }, + N::Leaf(L { range_low: 3, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(23)), one: Some(nz(36)) }, + N::Leaf(L { range_low: 4, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 5, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 6, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 70, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 102, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(29)), one: Some(nz(32)) }, + N::Intermediate { zero: Some(nz(30)), one: Some(nz(31)) }, + N::Leaf(L { range_low: 134, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 198, range_length: 7, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(33)), one: Some(nz(34)) }, + N::Leaf(L { range_low: 326, range_length: 8, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 582, range_length: 9, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 1094, range_length: 10, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 2118, range_length: 11, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(38)), one: Some(nz(39)) }, + N::Leaf(L { range_low: -22, range_length: 32, is_lower: true, is_out_of_band: false }), + N::Leaf(L { range_low: 4166, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: true }), + N::Empty, + N::Empty, +]; + +/// Standard Huffman table K (Table B.11). +#[rustfmt::skip] +const TABLE_K: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(1)), one: Some(nz(2)) }, + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(3)), one: Some(nz(4)) }, + N::Leaf(L { range_low: 2, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(5)), one: Some(nz(8)) }, + N::Intermediate { zero: Some(nz(6)), one: Some(nz(7)) }, + N::Leaf(L { range_low: 4, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 5, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(9)), one: Some(nz(12)) }, + N::Intermediate { zero: Some(nz(10)), one: Some(nz(11)) }, + N::Leaf(L { range_low: 7, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 9, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(13)), one: Some(nz(18)) }, + N::Intermediate { zero: Some(nz(14)), one: Some(nz(15)) }, + N::Leaf(L { range_low: 13, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(16)), one: Some(nz(17)) }, + N::Leaf(L { range_low: 17, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 21, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(19)), one: Some(nz(22)) }, + N::Intermediate { zero: Some(nz(20)), one: Some(nz(21)) }, + N::Leaf(L { range_low: 29, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 45, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(23)), one: Some(nz(24)) }, + N::Leaf(L { range_low: 77, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 141, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table L (Table B.12). +#[rustfmt::skip] +const TABLE_L: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(1)), one: Some(nz(2)) }, + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(3)), one: Some(nz(4)) }, + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(5)), one: Some(nz(6)) }, + N::Leaf(L { range_low: 3, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(7)), one: Some(nz(10)) }, + N::Intermediate { zero: Some(nz(8)), one: Some(nz(9)) }, + N::Leaf(L { range_low: 5, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 6, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(11)), one: Some(nz(16)) }, + N::Intermediate { zero: Some(nz(12)), one: Some(nz(13)) }, + N::Leaf(L { range_low: 8, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(14)), one: Some(nz(15)) }, + N::Leaf(L { range_low: 10, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 11, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(17)), one: Some(nz(20)) }, + N::Intermediate { zero: Some(nz(18)), one: Some(nz(19)) }, + N::Leaf(L { range_low: 13, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 17, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(21)), one: Some(nz(22)) }, + N::Leaf(L { range_low: 25, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(23)), one: Some(nz(24)) }, + N::Leaf(L { range_low: 41, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 73, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table M (Table B.13). +#[rustfmt::skip] +const TABLE_M: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(1)), one: Some(nz(2)) }, + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(3)), one: Some(nz(5)) }, + N::Intermediate { zero: Some(nz(4)), one: Some(nz(12)) }, + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(6)), one: Some(nz(8)) }, + N::Intermediate { zero: Some(nz(7)), one: Some(nz(11)) }, + N::Leaf(L { range_low: 3, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(9)), one: Some(nz(16)) }, + N::Intermediate { zero: Some(nz(10)), one: Some(nz(13)) }, + N::Leaf(L { range_low: 4, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 5, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 7, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(14)), one: Some(nz(15)) }, + N::Leaf(L { range_low: 15, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 17, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(17)), one: Some(nz(20)) }, + N::Intermediate { zero: Some(nz(18)), one: Some(nz(19)) }, + N::Leaf(L { range_low: 21, range_length: 3, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 29, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(21)), one: Some(nz(22)) }, + N::Leaf(L { range_low: 45, range_length: 5, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(23)), one: Some(nz(24)) }, + N::Leaf(L { range_low: 77, range_length: 6, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 141, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table N (Table B.14). +#[rustfmt::skip] +const TABLE_N: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(5)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(2)), one: Some(nz(6)) }, + N::Intermediate { zero: Some(nz(3)), one: Some(nz(4)) }, + N::Leaf(L { range_low: -2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: -1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(7)), one: Some(nz(8)) }, + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; + +/// Standard Huffman table O (Table B.15). +#[rustfmt::skip] +const TABLE_O: [N; INLINE_TABLE_SIZE] = [ + N::Intermediate { zero: Some(nz(16)), one: Some(nz(1)) }, + N::Intermediate { zero: Some(nz(14)), one: Some(nz(2)) }, + N::Intermediate { zero: Some(nz(12)), one: Some(nz(3)) }, + N::Intermediate { zero: Some(nz(10)), one: Some(nz(4)) }, + N::Intermediate { zero: Some(nz(8)), one: Some(nz(5)) }, + N::Intermediate { zero: Some(nz(6)), one: Some(nz(22)) }, + N::Intermediate { zero: Some(nz(7)), one: Some(nz(21)) }, + N::Leaf(L { range_low: -24, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(9)), one: Some(nz(20)) }, + N::Leaf(L { range_low: -8, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(11)), one: Some(nz(19)) }, + N::Leaf(L { range_low: -4, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(13)), one: Some(nz(18)) }, + N::Leaf(L { range_low: -2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(15)), one: Some(nz(17)) }, + N::Leaf(L { range_low: -1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 0, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 1, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 2, range_length: 0, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 3, range_length: 1, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 5, range_length: 2, is_lower: false, is_out_of_band: false }), + N::Leaf(L { range_low: 9, range_length: 4, is_lower: false, is_out_of_band: false }), + N::Intermediate { zero: Some(nz(23)), one: Some(nz(24)) }, + N::Leaf(L { range_low: -25, range_length: 32, is_lower: true, is_out_of_band: false }), + N::Leaf(L { range_low: 25, range_length: 32, is_lower: false, is_out_of_band: false }), + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, + N::Empty, +]; diff --git a/vendor/hayro-jbig2/src/integer_decoder.rs b/vendor/hayro-jbig2/src/integer_decoder.rs new file mode 100644 index 000000000..8b7656d29 --- /dev/null +++ b/vendor/hayro-jbig2/src/integer_decoder.rs @@ -0,0 +1,118 @@ +use alloc::vec; +use alloc::vec::Vec; + +use crate::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext}; + +/// The integer arithmetic decoder (A.2). +pub(crate) struct IntegerDecoder { + /// `CX` - Context memory for the integer decoder. + contexts: Vec, +} + +impl IntegerDecoder { + /// Create a new integer decoder with fresh contexts. + #[inline(always)] + pub(crate) fn new() -> Self { + Self { + // A.2: "Each arithmetic integer decoding procedure requires 512 bytes of + // storage for its context memory." + contexts: vec![ArithmeticDecoderContext::default(); 512], + } + } + + /// The integer arithmetic decoding procedure (A.2, Figure A.1). + /// + /// Returns `Some(value)` on success, or `None` if OOB (out-of-band) is decoded. + #[inline(always)] + pub(crate) fn decode(&mut self, decoder: &mut ArithmeticDecoder<'_>) -> Option { + // A.2 step 1: "Set: PREV = 1" + // `PREV` - Context prefix, contains bits decoded so far plus a leading 1. + let mut prev: u32 = 1; + + // A.2 step 2: "Follow the flowchart in Figure A.1. Decode each bit with + // CX equal to 'IAx + PREV' where 'IAx' represents the identifier of the + // current arithmetic integer decoding procedure, '+' represents + // concatenation, and the rightmost 9 bits of PREV are used." + + // `S` - Sign bit. + let s = self.decode_bit(decoder, &mut prev); + + // `V` - Magnitude value, decoded according to Figure A.1 flowchart. + #[expect( + clippy::same_functions_in_if_condition, + reason = "each call mutates `prev`" + )] + let v = if self.decode_bit(decoder, &mut prev) == 0 { + // Figure A.1: "V = next 2 bits" + self.decode_n_bits(decoder, &mut prev, 2) + } else if self.decode_bit(decoder, &mut prev) == 0 { + // Figure A.1: "V = (next 4 bits) + 4" + self.decode_n_bits(decoder, &mut prev, 4) + 4 + } else if self.decode_bit(decoder, &mut prev) == 0 { + // Figure A.1: "V = (next 6 bits) + 20" + self.decode_n_bits(decoder, &mut prev, 6) + 20 + } else if self.decode_bit(decoder, &mut prev) == 0 { + // Figure A.1: "V = (next 8 bits) + 84" + self.decode_n_bits(decoder, &mut prev, 8) + 84 + } else if self.decode_bit(decoder, &mut prev) == 0 { + // Figure A.1: "V = (next 12 bits) + 340" + self.decode_n_bits(decoder, &mut prev, 12) + 340 + } else { + // Figure A.1: "V = (next 32 bits) + 4436" + self.decode_n_bits(decoder, &mut prev, 32) + .wrapping_add(4436) + }; + + // A.2: "The result of the integer arithmetic decoding procedure is equal to: + // - V if S = 0 + // - -V if S = 1 and V > 0 + // - OOB if S = 1 and V = 0" + if s == 0 { + Some(v as i32) + } else if v > 0 { + Some(-(v as i32)) + } else { + None + } + } + + /// Decode a single bit and update `PREV` (A.2 step 3). + /// + /// A.2 step 3: "After each bit is decoded: If PREV < 256 set: + /// PREV = (PREV << 1) OR D. Otherwise set: + /// PREV = (((PREV << 1) OR D) AND 511) OR 256" + #[inline(always)] + fn decode_bit(&mut self, decoder: &mut ArithmeticDecoder<'_>, prev: &mut u32) -> u32 { + let ctx_idx = (*prev & 0x1FF) as usize; + // `D` - The just-decoded bit. + let d = decoder.read_bit(&mut self.contexts[ctx_idx]); + + // A.2 step 3: Update PREV. + if *prev < 256 { + *prev = (*prev << 1) | d; + } else { + // A.2: "PREV always contains the values of the eight most-recently- + // decoded bits, plus a leading 1 bit, which is used to indicate the + // number of bits decoded so far." + *prev = (((*prev << 1) | d) & 511) | 256; + } + + d + } + + /// Decode `n` bits and update `PREV` for each. + #[inline(always)] + fn decode_n_bits( + &mut self, + decoder: &mut ArithmeticDecoder<'_>, + prev: &mut u32, + n: usize, + ) -> u32 { + let mut value = 0_u32; + for _ in 0..n { + let bit = self.decode_bit(decoder, prev); + value = (value << 1) | bit; + } + value + } +} diff --git a/vendor/hayro-jbig2/src/integration.rs b/vendor/hayro-jbig2/src/integration.rs new file mode 100644 index 000000000..70df0f42d --- /dev/null +++ b/vendor/hayro-jbig2/src/integration.rs @@ -0,0 +1,151 @@ +//! Integration with the [image] crate. + +use std::{ + ffi::OsStr, + io::{BufRead, Seek}, +}; + +use crate::Image; +use ::image::error::{DecodingError, ImageFormatHint}; +use ::image::{ColorType, ExtendedColorType, ImageDecoder, ImageError, ImageResult}; +use image::hooks::{decoding_hook_registered, register_format_detection_hook}; + +impl ImageDecoder for Image<'_> { + fn dimensions(&self) -> (u32, u32) { + (self.width(), self.height()) + } + + fn color_type(&self) -> ColorType { + ColorType::L8 + } + + fn original_color_type(&self) -> ExtendedColorType { + ExtendedColorType::L1 + } + + fn read_image(self, buf: &mut [u8]) -> ImageResult<()> + where + Self: Sized, + { + decode_into_buf(&self, buf) + } + + fn read_image_boxed(self: Box, buf: &mut [u8]) -> ImageResult<()> { + decode_into_buf(&self, buf) + } +} + +fn decode_into_buf(image: &Image<'_>, buf: &mut [u8]) -> ImageResult<()> { + struct LumaDecoder<'a> { + buf: &'a mut [u8], + pos: usize, + } + + impl crate::Decoder for LumaDecoder<'_> { + fn push_pixel(&mut self, black: bool) { + self.buf[self.pos] = if black { 0 } else { 255 }; + self.pos += 1; + } + + fn push_pixel_chunk(&mut self, black: bool, chunk_count: u32) { + let luma = if black { 0 } else { 255 }; + let count = chunk_count as usize * 8; + self.buf[self.pos..self.pos + count].fill(luma); + self.pos += count; + } + + fn next_line(&mut self) {} + } + + let mut decoder = LumaDecoder { buf, pos: 0 }; + image.decode(&mut decoder)?; + + Ok(()) +} + +/// JBIG2 decoder compatible with `image` decoding hook APIs that pass an `impl BufRead + Seek`. +#[doc(hidden)] +pub struct Jbig2Decoder { + input: Vec, + width: u32, + height: u32, +} + +impl Jbig2Decoder { + /// Create a new decoder that decodes from the stream `r`. + pub fn new(r: R) -> ImageResult { + let mut input = Vec::new(); + let mut r = r; + r.read_to_end(&mut input)?; + + let image = Image::new(&input)?; + + Ok(Self { + width: image.width(), + height: image.height(), + input, + }) + } +} + +impl ImageDecoder for Jbig2Decoder { + fn dimensions(&self) -> (u32, u32) { + (self.width, self.height) + } + + fn color_type(&self) -> ColorType { + ColorType::L8 + } + + fn original_color_type(&self) -> ExtendedColorType { + ExtendedColorType::L1 + } + + fn read_image(self, buf: &mut [u8]) -> ImageResult<()> + where + Self: Sized, + { + let image = Image::new(&self.input)?; + decode_into_buf(&image, buf) + } + + fn read_image_boxed(self: Box, buf: &mut [u8]) -> ImageResult<()> { + let image = Image::new(&self.input)?; + decode_into_buf(&image, buf) + } +} + +impl From for DecodingError { + fn from(value: crate::DecodeError) -> Self { + let format = ImageFormatHint::Name("JBIG2".to_owned()); + Self::new(format, value) + } +} + +impl From for ImageError { + fn from(value: crate::DecodeError) -> Self { + Self::Decoding(value.into()) + } +} + +const JBIG2_MAGIC: &[u8] = &[0x97, 0x4A, 0x42, 0x32, 0x0D, 0x0A, 0x1A, 0x0A]; + +/// Registers the decoder with the `image` crate so that non-format-specific calls such as +/// `ImageReader::open("image.jbig2")?.decode()?;` work with JBIG2 files. +/// +/// Returns `true` on success, or `false` if the hook for JBIG2 is already registered. +pub fn register_decoding_hook() -> bool { + if decoding_hook_registered(OsStr::new("jbig2")) { + return false; + } + + for extension in ["jbig2", "jb2"] { + image::hooks::register_decoding_hook( + extension.into(), + Box::new(|r| Ok(Box::new(Jbig2Decoder::new(r)?))), + ); + register_format_detection_hook(extension.into(), JBIG2_MAGIC, None); + } + + true +} diff --git a/vendor/hayro-jbig2/src/lazy.rs b/vendor/hayro-jbig2/src/lazy.rs new file mode 100644 index 000000000..a85501ef6 --- /dev/null +++ b/vendor/hayro-jbig2/src/lazy.rs @@ -0,0 +1,29 @@ +#[cfg(feature = "std")] +#[derive(Debug)] +pub(crate) struct Lazy(std::cell::OnceCell); + +#[cfg(feature = "std")] +impl Lazy { + pub(crate) fn new(_value: impl FnOnce() -> T) -> Self { + Self(std::cell::OnceCell::new()) + } + + pub(crate) fn get(&self, init: impl FnOnce() -> T) -> &T { + self.0.get_or_init(init) + } +} + +#[cfg(not(feature = "std"))] +#[derive(Debug)] +pub(crate) struct Lazy(T); + +#[cfg(not(feature = "std"))] +impl Lazy { + pub(crate) fn new(value: impl FnOnce() -> T) -> Self { + Self(value()) + } + + pub(crate) fn get(&self, _init: impl FnOnce() -> T) -> &T { + &self.0 + } +} diff --git a/vendor/hayro-jbig2/src/lib.rs b/vendor/hayro-jbig2/src/lib.rs new file mode 100644 index 000000000..ad77a6dd1 --- /dev/null +++ b/vendor/hayro-jbig2/src/lib.rs @@ -0,0 +1,692 @@ +/*! +A memory-safe, pure-Rust JBIG2 decoder. + +`hayro-jbig2` decodes JBIG2 images as specified in ITU-T T.88 (also known as +ISO/IEC 14492). JBIG2 is a bi-level image compression standard commonly used +in PDF documents for compressing scanned text documents. + +The crate is `no_std` compatible but requires an allocator to be available. + +# Safety +This crate forbids unsafe code via a crate-level attribute. +*/ + +#![cfg_attr(not(feature = "std"), no_std)] +#![forbid(unsafe_code)] +#![allow(missing_docs)] + +extern crate alloc; + +use alloc::vec::Vec; + +use crate::arithmetic_decoder::ArithmeticDecoderContext; + +/// A reusable context for decoding JBIG2 images. +#[derive(Default)] +pub struct DecoderContext { + pub(crate) page_state: PageState, + pub(crate) scratch_buffers: ScratchBuffers, + pub(crate) page_bitmap: Bitmap, +} + +#[derive(Default)] +pub(crate) struct ScratchBuffers { + pub(crate) contexts: Vec, +} + +/// A decoder for JBIG2 images. +pub trait Decoder { + /// Push a single pixel to the output. + fn push_pixel(&mut self, black: bool); + /// Push multiple chunks of 8 pixels of the same color. + /// + /// The `chunk_count` parameter indicates how many 8-pixel chunks to push. + /// For example, if this method is called with `white = true` and + /// `chunk_count = 10`, 80 white pixels are pushed (10 × 8 = 80). + /// + /// You can assume that this method is only called if the number of already + /// pushed pixels is a multiple of 8 (i.e. byte-aligned). + fn push_pixel_chunk(&mut self, black: bool, chunk_count: u32); + /// Called when a row has been completed. + fn next_line(&mut self); +} + +#[cfg(feature = "image")] +pub mod integration; + +mod arithmetic_decoder; +mod bitmap; +mod decode; +mod error; +mod file; +mod gray_scale; +mod huffman_table; +mod integer_decoder; +mod lazy; +mod page_info; +mod reader; +mod segment; +mod simd; +mod symbol_id_decoder; + +use error::bail; +pub use error::{ + DecodeError, FormatError, HuffmanError, OverflowError, ParseError, RegionError, Result, + SegmentError, SymbolError, TemplateError, +}; + +use crate::file::parse_segments_sequential; +use bitmap::Bitmap; +use decode::CombinationOperator; +use decode::generic; +use decode::generic_refinement; +use decode::halftone; +use decode::pattern; +use decode::pattern::PatternDictionary; +use decode::symbol; +use decode::symbol::SymbolDictionary; +use decode::text; +use file::parse_file; +use huffman_table::{HuffmanTable, StandardHuffmanTables}; +use page_info::{PageInformation, parse_page_information}; +use reader::Reader; +use segment::SegmentType; + +/// A JBIG2 image. +pub struct Image<'a> { + /// The parsed segments. + segments: Vec>, + /// The width of the image in pixels. + width: u32, + /// The height of the image in pixels. + height: u32, + /// The height determined from `EndOfStripe` segments, if applicable. + height_from_stripes: Option, +} + +impl<'a> Image<'a> { + /// Parse a JBIG2 file from the given data. + /// + /// The file is expected to use the sequential or random-access organization, + /// as defined in Annex D.1 and D.2. + pub fn new(data: &'a [u8]) -> Result { + let file = parse_file(data)?; + Self::from_segments(file.segments) + } + + /// Parse an embedded JBIG2 image with optional global segments. + /// + /// The file is expected to use the embedded organization defined in + /// Annex D.3. + pub fn new_embedded(data: &'a [u8], globals: Option<&'a [u8]>) -> Result { + let mut segments = Vec::new(); + if let Some(globals_data) = globals { + let mut reader = Reader::new(globals_data); + parse_segments_sequential(&mut reader, &mut segments)?; + }; + + let mut reader = Reader::new(data); + parse_segments_sequential(&mut reader, &mut segments)?; + + segments.sort_by_key(|seg| seg.header.segment_number); + + Self::from_segments(segments) + } + + fn from_segments(segments: Vec>) -> Result { + // Pre-scan for stripe height from EndOfStripe segments. + let height_from_stripes = segments + .iter() + .filter(|seg| seg.header.segment_type == SegmentType::EndOfStripe) + .filter_map(|seg| u32::from_be_bytes(seg.data.try_into().ok()?).checked_add(1)) + .max(); + + // Find and parse page information to extract dimensions. + let page_info_seg = segments + .iter() + .find(|s| s.header.segment_type == SegmentType::PageInformation) + .ok_or(FormatError::MissingPageInfo)?; + + let mut reader = Reader::new(page_info_seg.data); + let page_info = parse_page_information(&mut reader)?; + + // "A page's bitmap height may be declared in its page information segment + // to be unknown (by specifying a height of 0xFFFFFFFF). In this case, the + // page must be striped." (7.4.8.2) + let height = if page_info.height == 0xFFFF_FFFF { + height_from_stripes.ok_or(FormatError::UnknownPageHeight)? + } else { + page_info.height + }; + + if page_info.width == 0 || height == 0 { + bail!(FormatError::EmptyPage); + } + + Ok(Self { + segments, + width: page_info.width, + height, + height_from_stripes, + }) + } + + /// The width of the image in pixels. + pub fn width(&self) -> u32 { + self.width + } + + /// The height of the image in pixels. + pub fn height(&self) -> u32 { + self.height + } + + /// Decode the image data through the given [`Decoder`]. + pub fn decode(&self, decoder: &mut D) -> Result<()> { + let mut ctx = DecoderContext::default(); + + self.decode_with(decoder, &mut ctx) + } + + /// Decode the image data through the given [`Decoder`] and [`DecoderContext`]. + /// + /// This is useful in case you want to convert multiple JBIG2 images, + /// as it allows `hayro-jbig2` to reuse allocations during decoding. + pub fn decode_with(&self, decoder: &mut D, ctx: &mut DecoderContext) -> Result<()> { + decode_segments(&self.segments, self.height_from_stripes, ctx)?; + emit_bitmap(&ctx.page_bitmap, decoder); + + Ok(()) + } +} + +fn emit_bitmap(bitmap: &Bitmap, decoder: &mut D) { + let width = bitmap.width; + let bytes_per_row = width.div_ceil(8) as usize; + + for row in bitmap.data.chunks_exact(bitmap.stride as usize) { + let mut x = 0_u32; + let mut chunk_byte: Option = None; + let mut chunk_count = 0_u32; + + let bytes = row.iter().flat_map(|w| w.to_be_bytes()).take(bytes_per_row); + + for byte in bytes { + let remaining = width - x; + + if remaining >= 8 && (byte == 0x00 || byte == 0xFF) { + // Continue the previous chunk. + if chunk_byte == Some(byte) { + chunk_count += 1; + x += 8; + continue; + } + + // Flush previous chunk if any, then start new one. + if let Some(b) = chunk_byte { + decoder.push_pixel_chunk(b == 0xFF, chunk_count); + } + + chunk_byte = Some(byte); + chunk_count = 1; + x += 8; + + continue; + } + + // Can't continue/start chunk, flush any existing chunk first. + if let Some(b) = chunk_byte.take() { + decoder.push_pixel_chunk(b == 0xFF, chunk_count); + chunk_count = 0; + } + + // Emit individual pixels. + let count = remaining.min(8); + for i in 0..count { + decoder.push_pixel((byte >> (7 - i)) & 1 != 0); + } + x += count; + } + + // Flush any remaining chunk at end of row. + if let Some(b) = chunk_byte { + decoder.push_pixel_chunk(b == 0xFF, chunk_count); + } + + decoder.next_line(); + } +} + +fn decode_segments( + segments: &[segment::Segment<'_>], + height_from_stripes: Option, + decoder_ctx: &mut DecoderContext, +) -> Result<()> { + // Find and parse page information segment first. + if let Some(page_info) = segments + .iter() + .find(|s| s.header.segment_type == SegmentType::PageInformation) + { + let mut reader = Reader::new(page_info.data); + init_page( + &mut reader, + height_from_stripes, + &mut decoder_ctx.page_state, + &mut decoder_ctx.page_bitmap, + )?; + } else { + bail!(FormatError::MissingPageInfo); + } + + let page_bitmap = &mut decoder_ctx.page_bitmap; + let page_state = &mut decoder_ctx.page_state; + let scratch_buffers = &mut decoder_ctx.scratch_buffers; + + // Process all segments. + for seg in segments { + let mut reader = Reader::new(seg.data); + + match seg.header.segment_type { + SegmentType::PageInformation => { + // Already processed above, skip. + } + SegmentType::ImmediateGenericRegion | SegmentType::ImmediateLosslessGenericRegion => { + let had_unknown_length = seg.header.data_length.is_none(); + let header = generic::parse(&mut reader, had_unknown_length)?; + + if page_state.can_decode_directly(page_bitmap, &header.region_info, false) { + generic::decode_into(&header, page_bitmap, scratch_buffers)?; + } else { + let region = generic::decode(&header, scratch_buffers)?; + page_bitmap.combine( + ®ion.bitmap, + region.bitmap.x_location as i32, + region.bitmap.y_location as i32, + region.combination_operator, + ); + } + page_state.page_pristine = false; + } + SegmentType::IntermediateGenericRegion => { + // Intermediate segments cannot have unknown length. + let header = generic::parse(&mut reader, false)?; + let region = generic::decode(&header, scratch_buffers)?; + page_state.store_region(seg.header.segment_number, region.bitmap); + } + SegmentType::PatternDictionary => { + let header = pattern::parse(&mut reader)?; + let dictionary = pattern::decode(&header, scratch_buffers)?; + page_state.store_pattern_dictionary(seg.header.segment_number, dictionary); + } + SegmentType::SymbolDictionary => { + // "1) Concatenate all the input symbol dictionaries to form SDINSYMS." + // (6.5.5, step 1) + // Collect references to avoid cloning; symbols are only cloned if re-exported. + let input_symbols: Vec<&Bitmap> = seg + .header + .referred_to_segments + .iter() + .filter_map(|&num| page_state.get_symbol_dictionary(num)) + .flat_map(|dict| dict.exported_symbols.iter()) + .collect(); + + // Collect Huffman tables from referred table segments. + let referred_tables: Vec = seg + .header + .referred_to_segments + .iter() + .filter_map(|&num| page_state.get_huffman_table(num)) + .cloned() + .collect(); + + // Get retained contexts from the last referred symbol dictionary (7.4.2.2 step 3). + let retained_contexts = seg + .header + .referred_to_segments + .last() + .and_then(|&num| page_state.get_symbol_dictionary(num)) + .and_then(|dict| dict.retained_contexts.as_ref()); + + let header = symbol::parse(&mut reader)?; + let dictionary = symbol::decode( + &header, + &input_symbols, + &referred_tables, + &page_state.standard_tables, + retained_contexts, + )?; + page_state.store_symbol_dictionary(seg.header.segment_number, dictionary); + } + SegmentType::ImmediateTextRegion | SegmentType::ImmediateLosslessTextRegion => { + // Collect symbols from referred symbol dictionaries (SBSYMS). + let symbols: Vec<&Bitmap> = seg + .header + .referred_to_segments + .iter() + .filter_map(|&num| page_state.get_symbol_dictionary(num)) + .flat_map(|dict| dict.exported_symbols.iter()) + .collect(); + + // Collect Huffman tables from referred table segments. + // "These user-supplied Huffman decoding tables may be supplied either + // as a Tables segment..." (7.4.3.1.6) + let referred_tables: Vec = seg + .header + .referred_to_segments + .iter() + .filter_map(|&num| page_state.get_huffman_table(num)) + .cloned() + .collect(); + + let header = text::parse(&mut reader, symbols.len() as u32)?; + + if page_state.can_decode_directly( + page_bitmap, + &header.region_info, + header.flags.default_pixel, + ) { + text::decode_into( + &header, + &symbols, + &referred_tables, + &page_state.standard_tables, + page_bitmap, + scratch_buffers, + )?; + } else { + let region = text::decode( + &header, + &symbols, + &referred_tables, + &page_state.standard_tables, + scratch_buffers, + )?; + page_bitmap.combine( + ®ion.bitmap, + region.bitmap.x_location as i32, + region.bitmap.y_location as i32, + region.combination_operator, + ); + } + page_state.page_pristine = false; + } + SegmentType::IntermediateTextRegion => { + // Collect symbols from referred symbol dictionaries (SBSYMS). + let symbols: Vec<&Bitmap> = seg + .header + .referred_to_segments + .iter() + .filter_map(|&num| page_state.get_symbol_dictionary(num)) + .flat_map(|dict| dict.exported_symbols.iter()) + .collect(); + + // Collect Huffman tables from referred table segments. + let referred_tables: Vec = seg + .header + .referred_to_segments + .iter() + .filter_map(|&num| page_state.get_huffman_table(num)) + .cloned() + .collect(); + + let header = text::parse(&mut reader, symbols.len() as u32)?; + let region = text::decode( + &header, + &symbols, + &referred_tables, + &page_state.standard_tables, + scratch_buffers, + )?; + page_state.store_region(seg.header.segment_number, region.bitmap); + } + SegmentType::ImmediateHalftoneRegion | SegmentType::ImmediateLosslessHalftoneRegion => { + let pattern_dict = seg + .header + .referred_to_segments + .first() + .and_then(|&num| page_state.get_pattern_dictionary(num)) + .ok_or(SegmentError::MissingPatternDictionary)?; + + let header = halftone::parse(&mut reader)?; + + if page_state.can_decode_directly( + page_bitmap, + &header.region_info, + header.flags.initial_pixel_color, + ) { + halftone::decode_into(&header, pattern_dict, page_bitmap, scratch_buffers)?; + } else { + let region = halftone::decode(&header, pattern_dict, scratch_buffers)?; + page_bitmap.combine( + ®ion.bitmap, + region.bitmap.x_location as i32, + region.bitmap.y_location as i32, + region.combination_operator, + ); + } + page_state.page_pristine = false; + } + SegmentType::IntermediateHalftoneRegion => { + let pattern_dict = seg + .header + .referred_to_segments + .first() + .and_then(|&num| page_state.get_pattern_dictionary(num)) + .ok_or(SegmentError::MissingPatternDictionary)?; + + let header = halftone::parse(&mut reader)?; + let region = halftone::decode(&header, pattern_dict, scratch_buffers)?; + page_state.store_region(seg.header.segment_number, region.bitmap); + } + SegmentType::IntermediateGenericRefinementRegion => { + // Same logic as immediate refinement, but store result instead of combining. + let reference = seg + .header + .referred_to_segments + .first() + .and_then(|&num| page_state.get_referred_segment(num)) + .unwrap_or(page_bitmap); + + let header = generic_refinement::parse(&mut reader)?; + let region = generic_refinement::decode(&header, reference, scratch_buffers)?; + page_state.store_region(seg.header.segment_number, region.bitmap); + } + SegmentType::ImmediateGenericRefinementRegion + | SegmentType::ImmediateLosslessGenericRefinementRegion => { + // "3) Determine the buffer associated with the region segment that + // this segment refers to." (7.4.7.5) + // + // "2) If there are no referred-to segments, then use the page + // bitmap as the reference buffer." (7.4.7.5) + let referred_segment = seg + .header + .referred_to_segments + .first() + .and_then(|&num| page_state.get_referred_segment(num)); + + let header = generic_refinement::parse(&mut reader)?; + + if let Some(referred_segment) = referred_segment + && page_state.can_decode_directly(page_bitmap, &header.region_info, false) + { + generic_refinement::decode_into( + &header, + referred_segment, + page_bitmap, + scratch_buffers, + )?; + } else { + let reference = referred_segment.unwrap_or(page_bitmap); + let region = generic_refinement::decode(&header, reference, scratch_buffers)?; + page_bitmap.combine( + ®ion.bitmap, + region.bitmap.x_location as i32, + region.bitmap.y_location as i32, + region.combination_operator, + ); + } + page_state.page_pristine = false; + } + SegmentType::Tables => { + // "Tables – see 7.4.13." (type 53) + // "This segment contains data which defines one or more user-supplied + // Huffman coding tables." (7.4.13) + let table = HuffmanTable::read_custom(&mut reader)?; + page_state.store_huffman_table(seg.header.segment_number, table); + } + SegmentType::EndOfPage | SegmentType::EndOfFile => { + break; + } + // Other segment types not yet implemented. + _ => {} + } + } + + Ok(()) +} + +/// Page-level decoding state for a JBIG2 page. +#[derive(Default)] +pub(crate) struct PageState { + /// The parsed page information. + pub(crate) page_info: PageInformation, + /// Whether the page bitmap is still in its initial state (not yet painted to). + pub(crate) page_pristine: bool, + /// Decoded intermediate regions, stored as (`segment_number`, region) pairs. + pub(crate) referred_segments: Vec<(u32, Bitmap)>, + /// Decoded pattern dictionaries, stored as (`segment_number`, dictionary) pairs. + pub(crate) pattern_dictionaries: Vec<(u32, PatternDictionary)>, + /// Decoded symbol dictionaries, stored as (`segment_number`, dictionary) pairs. + pub(crate) symbol_dictionaries: Vec<(u32, SymbolDictionary)>, + /// Decoded Huffman tables from table segments, stored as (`segment_number`, table) pairs. + /// "Tables – see 7.4.13." (type 53) + pub(crate) huffman_tables: Vec<(u32, HuffmanTable)>, + /// Standard Huffman tables (`TABLE_A` through `TABLE_O`). + pub(crate) standard_tables: StandardHuffmanTables, +} + +impl PageState { + fn reset(&mut self, page_info: PageInformation) { + self.page_info = page_info; + self.page_pristine = true; + self.referred_segments.clear(); + self.pattern_dictionaries.clear(); + self.symbol_dictionaries.clear(); + self.huffman_tables.clear(); + // Standard tables are lazily built and reused across images. + } + + /// Check if an immediate region can be decoded directly into the page bitmap. + fn can_decode_directly( + &self, + page_bitmap: &Bitmap, + region_info: &decode::RegionSegmentInfo, + region_default_pixel: bool, + ) -> bool { + if !self.page_pristine { + return false; + } + + let covers_page = region_info.x_location == 0 + && region_info.y_location == 0 + && region_info.width == page_bitmap.width + && region_info.height == page_bitmap.height; + + if !covers_page { + return false; + } + + let page_default_is_zero = self.page_info.flags.default_pixel == 0; + + if region_default_pixel == page_default_is_zero { + return false; + } + + let op = region_info.combination_operator; + match op { + CombinationOperator::Replace => true, + CombinationOperator::Or | CombinationOperator::Xor => page_default_is_zero, + CombinationOperator::And | CombinationOperator::Xnor => !page_default_is_zero, + } + } + + /// Store a decoded region for later reference. + fn store_region(&mut self, segment_number: u32, region: Bitmap) { + self.referred_segments.push((segment_number, region)); + } + + /// Look up a referred segment by number. + fn get_referred_segment(&self, segment_number: u32) -> Option<&Bitmap> { + self.referred_segments + .binary_search_by_key(&segment_number, |(num, _)| *num) + .ok() + .map(|idx| &self.referred_segments[idx].1) + } + + /// Store a decoded pattern dictionary for later reference. + fn store_pattern_dictionary(&mut self, segment_number: u32, dictionary: PatternDictionary) { + self.pattern_dictionaries.push((segment_number, dictionary)); + } + + /// Look up a pattern dictionary by segment number. + fn get_pattern_dictionary(&self, segment_number: u32) -> Option<&PatternDictionary> { + self.pattern_dictionaries + .binary_search_by_key(&segment_number, |(num, _)| *num) + .ok() + .map(|idx| &self.pattern_dictionaries[idx].1) + } + + /// Store a decoded symbol dictionary for later reference. + fn store_symbol_dictionary(&mut self, segment_number: u32, dictionary: SymbolDictionary) { + self.symbol_dictionaries.push((segment_number, dictionary)); + } + + /// Look up a symbol dictionary by segment number. + fn get_symbol_dictionary(&self, segment_number: u32) -> Option<&SymbolDictionary> { + self.symbol_dictionaries + .binary_search_by_key(&segment_number, |(num, _)| *num) + .ok() + .map(|idx| &self.symbol_dictionaries[idx].1) + } + + /// Store a decoded Huffman table for later reference. + fn store_huffman_table(&mut self, segment_number: u32, table: HuffmanTable) { + self.huffman_tables.push((segment_number, table)); + } + + /// Look up a Huffman table by segment number. + fn get_huffman_table(&self, segment_number: u32) -> Option<&HuffmanTable> { + self.huffman_tables + .binary_search_by_key(&segment_number, |(num, _)| *num) + .ok() + .map(|idx| &self.huffman_tables[idx].1) + } +} + +/// Parse page information and initialize the page bitmap. +fn init_page( + reader: &mut Reader<'_>, + height_from_stripes: Option, + page: &mut PageState, + bitmap: &mut Bitmap, +) -> Result<()> { + let page_info = parse_page_information(reader)?; + + // "A page's bitmap height may be declared in its page information segment + // to be unknown (by specifying a height of 0xFFFFFFFF). In this case, the + // page must be striped." (7.4.8.2) + let height = if page_info.height == 0xFFFF_FFFF { + height_from_stripes.ok_or(FormatError::UnknownPageHeight)? + } else { + page_info.height + }; + + // "Bit 2: Page default pixel value. This bit contains the initial value + // for every pixel in the page, before any region segments are decoded + // or drawn." (7.4.8.5) + bitmap.reinitialize(page_info.width, height, page_info.flags.default_pixel != 0)?; + + page.reset(page_info); + + Ok(()) +} diff --git a/vendor/hayro-jbig2/src/page_info.rs b/vendor/hayro-jbig2/src/page_info.rs new file mode 100644 index 000000000..cc0c89c06 --- /dev/null +++ b/vendor/hayro-jbig2/src/page_info.rs @@ -0,0 +1,150 @@ +//! Page information segment parsing (7.4.8). + +use crate::decode::CombinationOperator; +use crate::error::{ParseError, Result}; +use crate::reader::Reader; + +/// Parsed page information segment (7.4.8). +#[derive(Debug, Clone, Default)] +pub(crate) struct PageInformation { + /// "This is a four-byte value containing the width in pixels of the page's + /// bitmap." (7.4.8.1) + pub(crate) width: u32, + /// "This is a four-byte value containing the height in pixels of the page's + /// bitmap." (7.4.8.2) + pub(crate) height: u32, + /// "This is a four-byte value containing the resolution of the original page + /// medium, measured in pixels/metre in the horizontal direction. If this + /// value is unknown, then this field must contain 0x00000000." (7.4.8.3) + /// + /// `None` means unknown resolution. + pub(crate) _x_resolution: Option, + /// "This is a four-byte value containing the resolution of the original page + /// medium, measured in pixels/metre in the vertical direction. If this value + /// is unknown, then this field must contain 0x00000000." (7.4.8.4) + /// + /// `None` means unknown resolution. + pub(crate) _y_resolution: Option, + /// Page segment flags (7.4.8.5). + pub(crate) flags: PageFlags, + /// Page striping information (7.4.8.6). + pub(crate) _striping: PageStriping, +} + +/// Page segment flags (7.4.8.5, Figure 56). +#[derive(Debug, Clone)] +#[allow(unused)] +#[derive(Default)] +pub(crate) struct PageFlags { + /// "Bit 0: Page is eventually lossless. If this bit is 0, then the file does + /// not contain a lossless representation of the original (pre-coding) page. + /// If this bit is 1, then the file contains enough information to reconstruct + /// the original page." (7.4.8.5) + pub(crate) is_lossless: bool, + /// "Bit 1: Page might contain refinements. If this bit is 0, then no + /// refinement region segment may be associated with the page. If this bit + /// is 1, then such segments may be associated with the page." (7.4.8.5) + pub(crate) might_contain_refinements: bool, + /// "Bit 2: Page default pixel value. This bit contains the initial value for + /// every pixel in the page, before any region segments are decoded or drawn." + /// (7.4.8.5) + pub(crate) default_pixel: u8, + /// "Bits 3-4: Page default combination operator." (7.4.8.5) + pub(crate) default_combination_operator: CombinationOperator, + /// "Bit 5: Page requires auxiliary buffers. If this bit is 0, then no region + /// segment requiring an auxiliary buffer may be associated with the page." + /// (7.4.8.5) + pub(crate) requires_auxiliary_buffers: bool, + /// "Bit 6: Page combination operator overridden. If this bit is 0, then every + /// direct region segment associated with this page must use the page's default + /// combination operator. If this bit is 1, then direct region segments + /// associated with this page may use any combination operators." (7.4.8.5) + pub(crate) combination_operator_overridden: bool, + /// "Bit 7: Page might contain coloured segment. If this bit is 0, then no + /// segment with colour extension may be associated with the page." (7.4.8.5) + pub(crate) might_contain_coloured: bool, +} + +/// Page striping information (7.4.8.6, Figure 57). +#[derive(Debug, Clone, Default)] +pub(crate) struct PageStriping { + /// "Bit 15: Page is striped. If the 'page is striped' bit is 1, then the page + /// may have end of stripe segments associated with it." (7.4.8.6) + /// + /// "If the page's bitmap height is unknown (indicated by a page bitmap height + /// of 0xFFFFFFFF) then the 'page is striped' bit must be 1." (7.4.8.6) + pub(crate) _is_striped: bool, + /// "Bits 0-14: Maximum stripe size." (7.4.8.6) + /// + /// "The maximum size of each stripe (the distance between an end of stripe + /// segment's end row and the end row of the previous end of stripe segment, + /// or 0 in the case of the first end of stripe segment) must be no more than + /// the page's maximum stripe size." (7.4.8.6) + pub(crate) _max_stripe_size: u16, +} + +/// Parse a page information segment (7.4.8). +pub(crate) fn parse_page_information(reader: &mut Reader<'_>) -> Result { + // 7.4.8.1: Page bitmap width + let width = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + + // 7.4.8.2: Page bitmap height + let height = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + + // 7.4.8.3: Page X resolution + let x_resolution_raw = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + let x_resolution = if x_resolution_raw == 0 { + None + } else { + Some(x_resolution_raw) + }; + + // 7.4.8.4: Page Y resolution + let y_resolution_raw = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + let y_resolution = if y_resolution_raw == 0 { + None + } else { + Some(y_resolution_raw) + }; + + // 7.4.8.5: Page segment flags + let flags_byte = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + let flags = parse_page_flags(flags_byte); + + // 7.4.8.6: Page striping information + let striping_raw = reader.read_u16().ok_or(ParseError::UnexpectedEof)?; + let striping = PageStriping { + _is_striped: striping_raw & 0x8000 != 0, + _max_stripe_size: striping_raw & 0x7FFF, + }; + + Ok(PageInformation { + width, + height, + _x_resolution: x_resolution, + _y_resolution: y_resolution, + flags, + _striping: striping, + }) +} + +fn parse_page_flags(flags: u8) -> PageFlags { + let combo_bits = (flags >> 3) & 0x03; + let default_combination_operator = match combo_bits { + 0 => CombinationOperator::Or, + 1 => CombinationOperator::And, + 2 => CombinationOperator::Xor, + 3 => CombinationOperator::Xnor, + _ => unreachable!(), + }; + + PageFlags { + is_lossless: flags & 0x01 != 0, + might_contain_refinements: flags & 0x02 != 0, + default_pixel: (flags >> 2) & 0x01, + default_combination_operator, + requires_auxiliary_buffers: flags & 0x20 != 0, + combination_operator_overridden: flags & 0x40 != 0, + might_contain_coloured: flags & 0x80 != 0, + } +} diff --git a/vendor/hayro-jbig2/src/reader.rs b/vendor/hayro-jbig2/src/reader.rs new file mode 100644 index 000000000..418915ca2 --- /dev/null +++ b/vendor/hayro-jbig2/src/reader.rs @@ -0,0 +1,173 @@ +/// A reader for reading bits and bytes from a byte stream. +#[derive(Debug, Clone)] +pub(crate) struct Reader<'a> { + /// The underlying data. + data: &'a [u8], + /// The position in bits. + cur_pos: usize, +} + +impl<'a> Reader<'a> { + #[inline(always)] + pub(crate) fn new(data: &'a [u8]) -> Self { + Self { data, cur_pos: 0 } + } + + #[inline(always)] + pub(crate) fn align(&mut self) { + let bit_pos = self.bit_pos(); + + if !bit_pos.is_multiple_of(8) { + self.cur_pos += 8 - bit_pos; + } + } + + #[inline(always)] + pub(crate) fn at_end(&self) -> bool { + self.byte_pos() >= self.data.len() + } + + #[inline(always)] + pub(crate) fn tail(&self) -> Option<&'a [u8]> { + self.data.get(self.byte_pos()..) + } + + /// Read the given number of bytes. + /// + /// Assumes that the reader is currently byte-aligned. + #[inline(always)] + pub(crate) fn read_bytes(&mut self, len: usize) -> Option<&'a [u8]> { + debug_assert_eq!(self.bit_pos(), 0); + + let bytes = self.peek_bytes(len)?; + self.cur_pos += len * 8; + + Some(bytes) + } + + /// Read a single byte. + /// + /// Assumes that the reader is currently byte-aligned. + #[inline(always)] + pub(crate) fn read_byte(&mut self) -> Option { + debug_assert_eq!(self.bit_pos(), 0); + + let byte = self.peek_byte()?; + self.cur_pos += 8; + + Some(byte) + } + + /// Read a single byte, returning `None` if it's zero. + #[inline(always)] + pub(crate) fn read_nonzero_byte(&mut self) -> Option { + let byte = self.read_byte()?; + + if byte == 0 { None } else { Some(byte) } + } + + /// Skip the given number of bytes. + /// + /// Assumes that the reader is currently byte-aligned. + #[inline(always)] + pub(crate) fn skip_bytes(&mut self, len: usize) -> Option<()> { + debug_assert_eq!(self.bit_pos(), 0); + + self.read_bytes(len).map(|_| ()) + } + + /// Peek the given number of bytes. + /// + /// Assumes that the reader is currently byte-aligned. + #[inline(always)] + pub(crate) fn peek_bytes(&self, len: usize) -> Option<&'a [u8]> { + debug_assert_eq!(self.bit_pos(), 0); + + let start = self.byte_pos(); + let end = start.checked_add(len)?; + self.data.get(start..end) + } + + /// Peek the next byte. + /// + /// Assumes that the reader is currently byte-aligned. + #[inline(always)] + pub(crate) fn peek_byte(&self) -> Option { + debug_assert_eq!(self.bit_pos(), 0); + + self.data.get(self.byte_pos()).copied() + } + + /// Read an u16 number. + /// + /// Assumes that the reader is currently byte-aligned. + #[inline(always)] + pub(crate) fn read_u16(&mut self) -> Option { + Some(u16::from_be_bytes(self.read_bytes(2)?.try_into().ok()?)) + } + + /// Read an u32 number. + /// + /// Assumes that the reader is currently byte-aligned. + #[inline(always)] + pub(crate) fn read_u32(&mut self) -> Option { + Some(u32::from_be_bytes(self.read_bytes(4)?.try_into().ok()?)) + } + + /// Read an i32 number. + /// + /// Assumes that the reader is currently byte-aligned. + #[inline(always)] + pub(crate) fn read_i32(&mut self) -> Option { + Some(i32::from_be_bytes(self.read_bytes(4)?.try_into().ok()?)) + } + + #[inline(always)] + pub(crate) fn read_bit(&mut self) -> Option { + let byte = self.cur_byte()?; + let shift = 7 - (self.bit_pos()); + self.cur_pos += 1; + Some((byte >> shift) & 1) + } + + #[inline(always)] + pub(crate) fn read_bits(&mut self, count: u8) -> Option { + debug_assert!(count <= 32); + + let mut value = 0_u32; + let mut remaining = count; + + while remaining > 0 { + let bit_offset = self.bit_pos(); + let byte = self.cur_byte()? as u32; + + let available = (8 - bit_offset) as u8; + let take = remaining.min(available); + + let shift = available - take; + let mask = (1 << take) - 1; + let bits = (byte >> shift) & mask; + + value = (value << take) | bits; + self.cur_pos += take as usize; + remaining -= take; + } + + Some(value) + } + + #[inline(always)] + pub(crate) fn byte_pos(&self) -> usize { + self.cur_pos >> 3 + } + + #[inline(always)] + pub(crate) fn cur_byte(&self) -> Option { + self.data.get(self.byte_pos()).copied() + } + + #[inline(always)] + pub(crate) fn bit_pos(&self) -> usize { + self.cur_pos & 7 + } +} diff --git a/vendor/hayro-jbig2/src/segment.rs b/vendor/hayro-jbig2/src/segment.rs new file mode 100644 index 000000000..35c17c38a --- /dev/null +++ b/vendor/hayro-jbig2/src/segment.rs @@ -0,0 +1,419 @@ +//! Segment parsing for JBIG2 bitstreams (Section 7.2). +//! +//! This module handles parsing of individual segment headers and defines +//! the segment types used in JBIG2. + +use alloc::vec::Vec; + +use crate::error::{ParseError, Result, SegmentError, bail, err}; +use crate::reader::Reader; + +/// "The segment type is a number between 0 and 63, inclusive. Not all values +/// are allowed." (7.3) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum SegmentType { + /// Symbol dictionary – see 7.4.2. (type 0) + SymbolDictionary, + /// Intermediate text region – see 7.4.3. (type 4) + IntermediateTextRegion, + /// Immediate text region – see 7.4.3. (type 6) + ImmediateTextRegion, + /// Immediate lossless text region – see 7.4.3. (type 7) + ImmediateLosslessTextRegion, + /// Pattern dictionary – see 7.4.4. (type 16) + PatternDictionary, + /// Intermediate halftone region – see 7.4.5. (type 20) + IntermediateHalftoneRegion, + /// Immediate halftone region – see 7.4.5. (type 22) + ImmediateHalftoneRegion, + /// Immediate lossless halftone region – see 7.4.5. (type 23) + ImmediateLosslessHalftoneRegion, + /// Intermediate generic region – see 7.4.6. (type 36) + IntermediateGenericRegion, + /// Immediate generic region – see 7.4.6. (type 38) + ImmediateGenericRegion, + /// Immediate lossless generic region – see 7.4.6. (type 39) + ImmediateLosslessGenericRegion, + /// Intermediate generic refinement region – see 7.4.7. (type 40) + IntermediateGenericRefinementRegion, + /// Immediate generic refinement region – see 7.4.7. (type 42) + ImmediateGenericRefinementRegion, + /// Immediate lossless generic refinement region – see 7.4.7. (type 43) + ImmediateLosslessGenericRefinementRegion, + /// Page information – see 7.4.8. (type 48) + PageInformation, + /// End of page – see 7.4.9. (type 49) + EndOfPage, + /// End of stripe – see 7.4.10. (type 50) + EndOfStripe, + /// End of file – see 7.4.11. (type 51) + EndOfFile, + /// Profiles – see 7.4.12. (type 52) + Profiles, + /// Tables – see 7.4.13. (type 53) + Tables, + /// Colour palette – see 7.4.16. (type 54) + ColourPalette, + /// Extension - see 7.4.14. (type 62) + Extension, +} + +impl SegmentType { + /// "All other segment types are reserved and must not be used." (7.3) + fn from_type_value(value: u8) -> Result { + match value { + 0 => Ok(Self::SymbolDictionary), + 4 => Ok(Self::IntermediateTextRegion), + 6 => Ok(Self::ImmediateTextRegion), + 7 => Ok(Self::ImmediateLosslessTextRegion), + 16 => Ok(Self::PatternDictionary), + 20 => Ok(Self::IntermediateHalftoneRegion), + 22 => Ok(Self::ImmediateHalftoneRegion), + 23 => Ok(Self::ImmediateLosslessHalftoneRegion), + 36 => Ok(Self::IntermediateGenericRegion), + 38 => Ok(Self::ImmediateGenericRegion), + 39 => Ok(Self::ImmediateLosslessGenericRegion), + 40 => Ok(Self::IntermediateGenericRefinementRegion), + 42 => Ok(Self::ImmediateGenericRefinementRegion), + 43 => Ok(Self::ImmediateLosslessGenericRefinementRegion), + 48 => Ok(Self::PageInformation), + 49 => Ok(Self::EndOfPage), + 50 => Ok(Self::EndOfStripe), + 51 => Ok(Self::EndOfFile), + 52 => Ok(Self::Profiles), + 53 => Ok(Self::Tables), + 54 => Ok(Self::ColourPalette), + 62 => Ok(Self::Extension), + _ => err!(SegmentError::UnknownType), + } + } +} + +/// A parsed segment header (7.2.1). +#[derive(Debug, Clone)] +pub(crate) struct SegmentHeader { + /// "This four-byte field contains the segment's segment number. The valid + /// range of segment numbers is 0 through 4294967295 (0xFFFFFFFF) inclusive." + /// (7.2.2) + pub(crate) segment_number: u32, + /// "Bits 0-5: Segment type. See 7.3." (7.2.3) + pub(crate) segment_type: SegmentType, + /// "Bit 7: Deferred non-retain. If this bit is 1, this segment is flagged + /// as retained only by itself and its attached extension segments." (7.2.3) + pub(crate) _retain_flag: bool, + /// "This field encodes the number of the page to which this segment belongs. + /// The first page must be numbered '1'. This field may contain a value of + /// zero; this value indicates that this segment is not associated with any + /// page." (7.2.6) + pub(crate) _page_association: u32, + /// "This field contains the segment numbers of the segments that this segment + /// refers to, if any." (7.2.5) + pub(crate) referred_to_segments: Vec, + /// "This 4-byte field contains the length of the segment's segment data part, + /// in bytes." (7.2.7) + /// + /// `None` means the length was unknown (0xFFFFFFFF), which is only valid for + /// immediate generic region segments in sequential organization. + pub(crate) data_length: Option, +} + +/// A parsed segment with its header and data. +#[derive(Debug)] +pub(crate) struct Segment<'a> { + /// The segment header. + pub(crate) header: SegmentHeader, + /// The segment data (borrowed slice). + pub(crate) data: &'a [u8], +} + +/// Parse a segment header (7.2). +pub(crate) fn parse_segment_header(reader: &mut Reader<'_>) -> Result { + // 7.2.2: Segment number + // "This four-byte field contains the segment's segment number. The valid + // range of segment numbers is 0 through 4294967295 (0xFFFFFFFF) inclusive. + // As mentioned before, it is possible for there to be gaps in the segment + // numbering." + let segment_number = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + + // 7.2.3: Segment header flags + // "This is a 1-byte field." + let flags = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + + // "Bits 0-5: Segment type. See 7.3." + let segment_type = SegmentType::from_type_value(flags & 0x3F)?; + + // "Bit 6: Page association field size. See 7.2.6." + let page_association_long = flags & 0x40 != 0; + + // "Bit 7: Deferred non-retain. If this bit is 1, this segment is flagged as + // retained only by itself and its attached extension segments." + let retain_flag = flags & 0x80 == 0; + + // 7.2.4: Referred-to segment count and retention flags + // "This field contains one or more bytes indicating how many other segments + // are referred to by this segment, and which segments contain data that is + // needed after this segment." + // + // "The three most significant bits of the first byte in this field determine + // the length of the field. If the value of this three-bit subfield is between + // 0 and 4, then the field is one byte long. If the value of this three-bit + // subfield is 7, then the field is at least five bytes long. This three-bit + // subfield must not contain values of 5 and 6." + let count_and_retention = reader.read_byte().ok_or(ParseError::UnexpectedEof)?; + let short_count = (count_and_retention >> 5) & 0x07; + + if short_count == 5 || short_count == 6 { + bail!(SegmentError::InvalidReferredCount); + } + + let referred_to_count = if short_count < 7 { + // Short form: "Bits 5-7: Count of referred-to segments. This field may + // take on values between zero and four." + short_count as u32 + } else { + // Long form: "In the case where the field is in the long format (at least + // five bytes long), it is composed of an initial four-byte field, followed + // by a succession of one-byte fields." + // + // "Bits 0-28: Count of referred-to segments. This specifies the number of + // segments that this segment refers to." + // "Bits 29-31: Indication of long-form format. This field must contain the + // value 7." + let rest = reader.read_bytes(3).ok_or(ParseError::UnexpectedEof)?; + u32::from_be_bytes([count_and_retention & 0x1F, rest[0], rest[1], rest[2]]) + }; + + // Skip retention flag bytes in long form. + // "The first one-byte field following the initial four-byte field is formatted + // as follows: Bit 0: Retain bit for this segment. Bit 1-7: Retain bits for + // referred-to segments." + if short_count == 7 { + // Number of retention bytes: ceil((referred_to_count + 1) / 8) + let retention_bytes = (referred_to_count as usize + 1).div_ceil(8); + reader + .skip_bytes(retention_bytes) + .ok_or(ParseError::UnexpectedEof)?; + } + + // 7.2.5: Referred-to segment numbers + // "When the current segment's number is 256 or less, then each referred-to + // segment number is one byte long. Otherwise, when the current segment's + // number is 65536 or less, each referred-to segment number is two bytes long. + // Otherwise, each referred-to segment number is four bytes long." + let mut referred_to_segments = Vec::with_capacity(referred_to_count as usize); + for _ in 0..referred_to_count { + let referred = if segment_number <= 256 { + reader.read_byte().ok_or(ParseError::UnexpectedEof)? as u32 + } else if segment_number <= 65536 { + reader.read_u16().ok_or(ParseError::UnexpectedEof)? as u32 + } else { + reader.read_u32().ok_or(ParseError::UnexpectedEof)? + }; + + // If a segment refers to other segments, it must refer to only segments + // with lower segment numbers. + if referred >= segment_number { + bail!(SegmentError::InvalidReference); + } + + referred_to_segments.push(referred); + } + + // 7.2.6: Segment page association + // "This field is one byte long if this segment's page association field size + // flag bit is 0, and is four bytes long if this segment's page association + // field size flag bit is 1." + let page_association = if page_association_long { + reader.read_u32().ok_or(ParseError::UnexpectedEof)? + } else { + reader.read_byte().ok_or(ParseError::UnexpectedEof)? as u32 + }; + + // 7.2.7: Segment data length + // "This 4-byte field contains the length of the segment's segment data part, + // in bytes." + // + // "If the segment's type is 'Immediate generic region', then the length field + // may contain the value 0xFFFFFFFF. This value is intended to mean that the + // length of the segment's data part is unknown at the time that the segment + // header is written." + let data_length_raw = reader.read_u32().ok_or(ParseError::UnexpectedEof)?; + let data_length = if data_length_raw == 0xFFFFFFFF { + None + } else { + Some(data_length_raw) + }; + + Ok(SegmentHeader { + segment_number, + segment_type, + _retain_flag: retain_flag, + _page_association: page_association, + referred_to_segments, + data_length, + }) +} + +/// Parse a complete segment (header + data). +pub(crate) fn parse_segment<'a>(reader: &mut Reader<'a>) -> Result> { + let header = parse_segment_header(reader)?; + parse_segment_data(reader, header) +} + +/// Parse segment data for a previously parsed header. +/// +/// "If the segment's type is 'Immediate generic region', then the length field +/// may contain the value 0xFFFFFFFF. This value is intended to mean that the +/// length of the segment's data part is unknown at the time that the segment +/// header is written." (7.2.7) +pub(crate) fn parse_segment_data<'a>( + reader: &mut Reader<'a>, + header: SegmentHeader, +) -> Result> { + let data = if let Some(len) = header.data_length { + reader + .read_bytes(len as usize) + .ok_or(ParseError::UnexpectedEof)? + } else { + // "In order for the decoder to correctly decode the segment, it needs to + // read the four-byte row count field, which is stored in the last four + // bytes of the segment's data part. These four bytes can be detected + // without knowing the length of the data part in advance: if MMR is 1, + // they are preceded by the two-byte sequence 0x00 0x00; if MMR is 0, they + // are preceded by the two-byte sequence 0xFF 0xAC." (7.4.6.4) + let len = scan_for_immediate_generic_region_size(reader)?; + reader.read_bytes(len).ok_or(ParseError::UnexpectedEof)? + }; + + Ok(Segment { header, data }) +} + +/// Scan for the end of an immediate generic region segment with unknown length. +/// +/// "The form of encoding used by the segment may be determined by examining +/// the eighteenth byte of its segment data part, and the end sequences can +/// occur anywhere after that eighteenth byte." (7.2.7) +fn scan_for_immediate_generic_region_size(reader: &Reader<'_>) -> Result { + let mut scan = reader.clone(); + let start_offset = scan.byte_pos(); + + scan.skip_bytes(17).ok_or(ParseError::UnexpectedEof)?; + let flags = scan.read_byte().ok_or(ParseError::UnexpectedEof)?; + let uses_mmr = (flags & 1) != 0; + + // "if MMR is 1, they are preceded by the two-byte sequence 0x00 0x00; + // if MMR is 0, they are preceded by the two-byte sequence 0xFF 0xAC." + let end_marker: [u8; 2] = if uses_mmr { [0x00, 0x00] } else { [0xFF, 0xAC] }; + + // Search for the end marker. The marker is followed by a 4-byte row count. + while let Some(bytes) = scan.peek_bytes(6) { + if bytes[..2] == end_marker { + // Found the marker. Total size is current offset + 2 (marker) + 4 (row count) - start. + return Ok(scan.byte_pos() - start_offset + 2 + 4); + } + scan.skip_bytes(1).ok_or(ParseError::UnexpectedEof)?; + } + + err!(SegmentError::MissingEndMarker) +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use super::*; + + #[test] + fn test_segment_header_example_1() { + // 7.2.8 Segment header example, EXAMPLE 1: + // "A segment header consisting of the sequence of bytes: + // 0x00 0x00 0x00 0x20 0x86 0x6B 0x02 0x1E 0x05 0x04" + // + // Plus 4 bytes for data length (not shown in example). + let data = [ + 0x00, 0x00, 0x00, 0x20, // Segment number = 32 + 0x86, // Flags: type 6, page assoc 1 byte, deferred non-retain + 0x6B, // Refers to 3 segments, retention flags + 0x02, 0x1E, 0x05, // Referred segments: 2, 30, 5 + 0x04, // Page association = 4 + 0x00, 0x00, 0x00, 0x10, // Data length = 16 (added for complete header) + ]; + + let mut reader = Reader::new(&data); + let header = parse_segment_header(&mut reader).unwrap(); + + // "0x00 0x00 0x00 0x20: This segment's number is 0x00000020, or 32 decimal." + assert_eq!(header.segment_number, 32); + + // "0x86: This segment's type is 6. Its page association field is one byte + // long. It is retained by only its attached extension segments." + assert_eq!(header.segment_type, SegmentType::ImmediateTextRegion); + assert!(!header._retain_flag); + + // "0x6B: This segment refers to three other segments. It is referred to by + // some other segment. This is the last reference to the second of the three + // segments that it refers to." + // "0x02 0x1E 0x05: The three segments that it refers to are numbers 2, 30, and 5." + assert_eq!(header.referred_to_segments, vec![2, 30, 5]); + + // "0x04: This segment is associated with page number 4." + assert_eq!(header._page_association, 4); + + assert_eq!(header.data_length, Some(16)); + } + + #[test] + fn test_segment_header_example_2() { + // 7.2.8 Segment header example, EXAMPLE 2: + // "A segment header consisting of the sequence of bytes, in hexadecimal: + // 00 00 02 34 40 E0 00 00 09 02 FD 01 00 00 02 00 + // 1E 00 05 02 00 02 01 02 02 02 03 02 04 00 00 04 + // 01" + // + // Plus 4 bytes for data length (not shown in example). + #[rustfmt::skip] + let data = [ + 0x00, 0x00, 0x02, 0x34, // Segment number = 564 + 0x40, // Flags: type 0, page assoc 4 bytes + 0xE0, 0x00, 0x00, 0x09, // Long form: refers to 9 segments + 0x02, 0xFD, // Retention flags (2 bytes) + 0x01, 0x00, // Referred segment 256 + 0x00, 0x02, // Referred segment 2 + 0x00, 0x1E, // Referred segment 30 + 0x00, 0x05, // Referred segment 5 + 0x02, 0x00, // Referred segment 512 + 0x02, 0x01, // Referred segment 513 + 0x02, 0x02, // Referred segment 514 + 0x02, 0x03, // Referred segment 515 + 0x02, 0x04, // Referred segment 516 + 0x00, 0x00, 0x04, 0x01, // Page association = 1025 + 0x00, 0x00, 0x00, 0x20, // Data length = 32 (added for complete header) + ]; + + let mut reader = Reader::new(&data); + let header = parse_segment_header(&mut reader).unwrap(); + + // "00 00 02 34: This segment's number is 0x00000234, or 564 decimal." + assert_eq!(header.segment_number, 564); + + // "40: This segment's type is 0. Its page association field is four bytes long." + assert_eq!(header.segment_type, SegmentType::SymbolDictionary); + assert!(header._retain_flag); + + // "E0 00 00 09: This segment's referred-to segment count field is in the long + // format. This segment refers to nine other segments." + // "01 00 ... 02 04: The nine segments that it refers to are each identified by + // two bytes, since this segment's number is between 256 and 65535. The segments + // that it refers to are, in decimal, numbers 256, 2, 30, 5, 512, 513, 514, 515, + // and 516." + assert_eq!( + header.referred_to_segments, + vec![256, 2, 30, 5, 512, 513, 514, 515, 516] + ); + + // "00 00 04 01: This segment is associated with page number 1025." + assert_eq!(header._page_association, 1025); + + assert_eq!(header.data_length, Some(32)); + } +} diff --git a/vendor/hayro-jbig2/src/simd.rs b/vendor/hayro-jbig2/src/simd.rs new file mode 100644 index 000000000..2bd1adb7e --- /dev/null +++ b/vendor/hayro-jbig2/src/simd.rs @@ -0,0 +1,256 @@ +pub(crate) const SIMD_WIDTH: usize = 8; + +#[cfg(feature = "simd")] +mod inner { + use super::SIMD_WIDTH; + use core::ops::{BitAnd, BitOr, BitXor, BitXorAssign}; + use fearless_simd::{Select, SimdBase, SimdInt}; + + pub(crate) use fearless_simd::{Level, Simd, dispatch}; + + #[derive(Copy, Clone)] + #[allow(non_camel_case_types)] + #[repr(C, align(32))] + pub(crate) struct mask32x8 { + inner: fearless_simd::mask32x8, + } + + impl mask32x8 { + #[inline(always)] + pub(crate) fn select(self, if_true: u32x8, if_false: u32x8) -> u32x8 { + u32x8 { + inner: self.inner.select(if_true.inner, if_false.inner), + } + } + } + + #[derive(Copy, Clone)] + #[allow(non_camel_case_types)] + #[repr(C, align(32))] + pub(crate) struct u32x8 { + inner: fearless_simd::u32x8, + } + + impl u32x8 { + #[inline(always)] + pub(crate) fn from_slice(simd: S, slice: &[u32]) -> Self { + Self { + inner: fearless_simd::u32x8::from_slice(simd, &slice[..SIMD_WIDTH]), + } + } + + #[inline(always)] + pub(crate) fn splat(simd: S, val: u32) -> Self { + Self { + inner: fearless_simd::u32x8::splat(simd, val), + } + } + + #[inline(always)] + pub(crate) fn store(self, slice: &mut [u32]) { + self.inner.store_slice(&mut slice[..SIMD_WIDTH]); + } + + #[inline(always)] + pub(crate) fn simd_gt(self, other: Self) -> mask32x8 { + mask32x8 { + inner: self.inner.simd_gt(other.inner), + } + } + } + + impl BitAnd for u32x8 { + type Output = Self; + #[inline(always)] + fn bitand(self, rhs: Self) -> Self { + Self { + inner: self.inner & rhs.inner, + } + } + } + + impl BitOr for u32x8 { + type Output = Self; + #[inline(always)] + fn bitor(self, rhs: Self) -> Self { + Self { + inner: self.inner | rhs.inner, + } + } + } + + impl BitXor for u32x8 { + type Output = Self; + #[inline(always)] + fn bitxor(self, rhs: Self) -> Self { + Self { + inner: self.inner ^ rhs.inner, + } + } + } + + impl BitXorAssign for u32x8 { + #[inline(always)] + fn bitxor_assign(&mut self, rhs: Self) { + self.inner = self.inner ^ rhs.inner; + } + } +} + +#[cfg(not(feature = "simd"))] +mod inner { + use super::SIMD_WIDTH; + use core::marker::PhantomData; + use core::ops::{BitAnd, BitOr, BitXor, BitXorAssign}; + + pub(crate) trait Simd: Copy + Clone {} + + #[derive(Copy, Clone)] + pub(crate) struct ScalarSimd; + impl Simd for ScalarSimd {} + + pub(crate) struct Level; + impl Level { + #[inline(always)] + pub(crate) fn new() -> Self { + Level + } + } + + macro_rules! simd_dispatch { + ($level:expr, $simd:ident => $body:expr) => {{ + let _ = $level; + let $simd = $crate::simd::ScalarSimd; + $body + }}; + } + + pub(crate) use simd_dispatch as dispatch; + + #[derive(Copy, Clone)] + #[allow(non_camel_case_types)] + #[repr(C, align(32))] + pub(crate) struct mask32x8 { + val: [bool; SIMD_WIDTH], + _marker: PhantomData, + } + + impl mask32x8 { + #[inline(always)] + pub(crate) fn select(self, if_true: u32x8, if_false: u32x8) -> u32x8 { + let mut result = [0u32; SIMD_WIDTH]; + for i in 0..SIMD_WIDTH { + result[i] = if self.val[i] { + if_true.val[i] + } else { + if_false.val[i] + }; + } + u32x8 { + val: result, + _marker: PhantomData, + } + } + } + + #[derive(Copy, Clone)] + #[allow(non_camel_case_types)] + #[repr(C, align(32))] + pub(crate) struct u32x8 { + pub(super) val: [u32; SIMD_WIDTH], + _marker: PhantomData, + } + + impl u32x8 { + #[inline(always)] + pub(crate) fn from_slice(_simd: S, slice: &[u32]) -> Self { + let mut val = [0u32; SIMD_WIDTH]; + val.copy_from_slice(&slice[..SIMD_WIDTH]); + Self { + val, + _marker: PhantomData, + } + } + + #[inline(always)] + pub(crate) fn splat(_simd: S, val: u32) -> Self { + Self { + val: [val; SIMD_WIDTH], + _marker: PhantomData, + } + } + + #[inline(always)] + pub(crate) fn store(self, slice: &mut [u32]) { + slice[..SIMD_WIDTH].copy_from_slice(&self.val); + } + + #[inline(always)] + pub(crate) fn simd_gt(self, other: Self) -> mask32x8 { + let mut result = [false; SIMD_WIDTH]; + for i in 0..SIMD_WIDTH { + result[i] = self.val[i] > other.val[i]; + } + mask32x8 { + val: result, + _marker: PhantomData, + } + } + } + + impl BitAnd for u32x8 { + type Output = Self; + #[inline(always)] + fn bitand(self, rhs: Self) -> Self { + let mut result = [0u32; SIMD_WIDTH]; + for i in 0..SIMD_WIDTH { + result[i] = self.val[i] & rhs.val[i]; + } + Self { + val: result, + _marker: PhantomData, + } + } + } + + impl BitOr for u32x8 { + type Output = Self; + #[inline(always)] + fn bitor(self, rhs: Self) -> Self { + let mut result = [0u32; SIMD_WIDTH]; + for i in 0..SIMD_WIDTH { + result[i] = self.val[i] | rhs.val[i]; + } + Self { + val: result, + _marker: PhantomData, + } + } + } + + impl BitXor for u32x8 { + type Output = Self; + #[inline(always)] + fn bitxor(self, rhs: Self) -> Self { + let mut result = [0u32; SIMD_WIDTH]; + for i in 0..SIMD_WIDTH { + result[i] = self.val[i] ^ rhs.val[i]; + } + Self { + val: result, + _marker: PhantomData, + } + } + } + + impl BitXorAssign for u32x8 { + #[inline(always)] + fn bitxor_assign(&mut self, rhs: Self) { + for i in 0..SIMD_WIDTH { + self.val[i] ^= rhs.val[i]; + } + } + } +} + +pub(crate) use inner::*; diff --git a/vendor/hayro-jbig2/src/symbol_id_decoder.rs b/vendor/hayro-jbig2/src/symbol_id_decoder.rs new file mode 100644 index 000000000..33ca1c6df --- /dev/null +++ b/vendor/hayro-jbig2/src/symbol_id_decoder.rs @@ -0,0 +1,40 @@ +//! Symbol ID (IAID) decoder (A.3). + +use alloc::vec; +use alloc::vec::Vec; + +use crate::arithmetic_decoder::{ArithmeticDecoder, ArithmeticDecoderContext}; + +pub(crate) struct SymbolIdDecoder { + contexts: Vec, + code_len: u32, +} + +impl SymbolIdDecoder { + pub(crate) fn new(code_len: u32) -> Self { + // A.3: "The number of contexts required is 2^SBSYMCODELEN, which is less + // than twice the maximum symbol ID." + let num_contexts = 1_usize << code_len; + + Self { + contexts: vec![ArithmeticDecoderContext::default(); num_contexts], + code_len, + } + } + + #[inline(always)] + pub(crate) fn decode(&mut self, decoder: &mut ArithmeticDecoder<'_>) -> u32 { + let mut prev = 1_u32; + + for _ in 0..self.code_len { + let ctx_mask = (1_u32 << (self.code_len + 1)) - 1; + let ctx_idx = (prev & ctx_mask) as usize; + let d = decoder.read_bit(&mut self.contexts[ctx_idx]); + + prev = (prev << 1) | d; + } + + prev -= 1 << self.code_len; + prev + } +}