From fb427d62b2c7c715b2ccc5fc62a3d93062ffeeb1 Mon Sep 17 00:00:00 2001 From: MPCoreDeveloper Date: Sun, 15 Nov 2026 09:00:32 +0100 Subject: [PATCH] Add the DCT reference fixtures and settle the pixel contract Two Pillow-written JPEGs (16 x 16 RGB at 4:4:4, and the same bitmap as one component) and the bytes Pillow decodes from them are committed under Assets/jpeg, so the baseline DCT decoder starts from a reference rather than a description. The test project copies Assets/**/*.jpg to the output. ADR-005 records the contract the slice settles: Data stays the stored JPEG bytes and DecodedPixels is filled from the new decoder, so a caller can have either. The baseline (SOF0) decoder is the next step, against these fixtures and the 2-level tolerance. --- docs/adr/0005-image-filter-decoders.md | 23 +++++++++++++++++- .../Assets/jpeg/jpeg-444-expected.bin | Bin 0 -> 768 bytes .../Assets/jpeg/jpeg-444.jpg | Bin 0 -> 735 bytes .../Assets/jpeg/jpeg-gray-expected.bin | Bin 0 -> 256 bytes .../Assets/jpeg/jpeg-gray.jpg | Bin 0 -> 385 bytes .../SharpCorePDF.Tests.csproj | 1 + 6 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-444-expected.bin create mode 100644 tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-444.jpg create mode 100644 tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-gray-expected.bin create mode 100644 tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-gray.jpg diff --git a/docs/adr/0005-image-filter-decoders.md b/docs/adr/0005-image-filter-decoders.md index 9e670db..07112e6 100644 --- a/docs/adr/0005-image-filter-decoders.md +++ b/docs/adr/0005-image-filter-decoders.md @@ -108,12 +108,33 @@ Two facts the first attempt established, so the next one does not rediscover the Both slices are shipped: the Group 4 fixture decodes every line against this data, the Group 3 fixture decodes as well, and `PdfCcittFilterTests` pins both. +## Fixtures for the DCT slice (2026-11-15) + +The baseline decoder starts from a 4:4:4 reference so that chroma upsampling cannot mask a bug: + +- `tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-444.jpg` — 16 x 16 RGB, quality 95, `subsampling=0`, written + by Pillow 13.0.0. +- `jpeg-444-expected.bin` — the 768 bytes Pillow decodes from that file (`RGB`, 3 bytes per pixel). +- `jpeg-gray.jpg` and `jpeg-gray-expected.bin` — the same bitmap as one component (256 bytes). + +The expected bytes are Pillow's decode of the JPEG, not the source pixels: JPEG is lossy, so the reference +is the decoder's output, and the acceptance is a per-sample difference of at most 2. + +**The contract question the slice settles.** A `DCTDecode` image currently reports `Data` as the stored JPEG +bytes and `DecodedPixels` as `null`. The slice keeps `Data` as the stored bytes — it is the payload the +document holds — and fills `DecodedPixels` from the new decoder, so a caller that wants the JPEG still has it +and a caller that wants pixels gets them. `ImagePixels` stops excluding `DCTDecode` once `TryDecodeJpeg` is +wired; the budget rules then apply to the pixel buffer as they do to every other decoded filter. + +Order: SOF0 (baseline) first, against these fixtures; SOF1/SOF2 (extended and progressive) after it, with +their own fixtures and the same ±2 tolerance. + ## Status of the slices | Slice | State | | --- | --- | | `RunLengthDecode` | **Implemented** (2026-11-14) | | `CCITTFaxDecode` | **Implemented** (2026-11-15) | -| `DCTDecode` | Planned — the next slice | +| `DCTDecode` | Fixtures ready (2026-11-15); baseline decoder next | | `JPXDecode` | Planned | | `JBIG2Decode` | Planned | diff --git a/tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-444-expected.bin b/tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-444-expected.bin new file mode 100644 index 0000000000000000000000000000000000000000..4e0b41017dd237701188f819beaef4d8428c6658 GIT binary patch literal 768 zcmV~$y>ru6008i(bYY653A*$WDMRQ&5{U~e4cKxaUGgPivlM&4Pi}`ey+%(g*zFKM z<*tX*p?+GxS(;eNT>=X(l1o_lh~ysal)Ff7J26EPe?X=FegKS(Qx`bEh~Q!#Oe)|q z1Tz|S4TD^Vy4|Ce98f%=DgjlCKs}{&Kwn^4c8d8_q$djOw8C70^t{gA*k%?@cIAM% zJ7CH~RvoeRkBpwNO~A1Cw4f_Nn_&dcIWC@gB?5*AAxv22N|Bd&WQIun}FTq_lR8p}^Ih0DA&D@xa; z!VN|GN|jdiLdlRSW}#+F8w2TqUucGTBhKw)5~1YJX=R3!=jYbG$SaF$@)BGvYDyW) z-*l9^B_l^}c#07y?N}z6+y&LyaX3E#7iQtDn{as*ma6y5o3Of7-S`Efeb{(WZM}o- zGf2*>-T&aPK)cSM1s;7VYReKTDd?W6L4?)~O(*D~t!=ql+eaisPZDh}MTel38#iuG z;^jG9%o&w6qgKUr4ePk|W5>Wf{K&zb6Js~Py$Bzrxc^CKku_JQ$la@?QZUs!X1zvq zWHz^nVVXM!#2k?BknE4h(MQtH$T8Jhq3t4PeJxmO-mc5G4z0$f-NM!mu{)Oa*tPaO z`zW-Z$JWctdP6xS+AVXADmd$T7s>7q&@na#kFZOK+qIm1$2s)e=YjhwcHU<0A7fsH z8P=wT---SM$!{wDw(9+?4T<6Jn#10~@Y%rs&G(N-{z>BfmHDS*!MDt4gAa8fXh`9< z9JFCTw9&2++UDq~9rm5zr5~OI!TUHk&BA}d`39ZnlW}u4YUPrh)tKCmy5A>zThY@_ z(zoK{7ttFp`SUC~J&*oLlMf)(nd~8-Zi#7I%7~IZQPVvwJ2cY1nf-33uif;$pPq*4 L-%0u*&Eo$7dq8&r literal 0 HcmV?d00001 diff --git a/tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-444.jpg b/tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-444.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dbbf203a23318593650dd8267a438a528b3d81a3 GIT binary patch literal 735 zcmex=iF;N$`UAd82aiwDF383NJD#LCRf%Eivc4pu@E@&5pWAP0j0g8(z5AOn*i zBeNjm|04|YKzFi&odL?6mQqXwbzED#l4gO`Kd};u4Zls%q*Qnp!5NX66=_R?aT2 zZtfnQUcn)uVc`*xQOPN(Y3Ui6S;Zx#W#tu>Rn0A}ZS5VMU6UqHnL2IyjG40*Enc#8 z+42=DS8dw7W$U)>J9h3mboj{8W5-XNJay^vm8;jT-?(|};iJb-o<4j2;^nK4pFV&2 z`tAFVpT9u3cne5k^%pP@7@0tEzz*^kBU3pLGYhh?DjKp0IR>&P778mFHFAhJ zO4hWdoJa_{6qMq#KM2+-|s_YzPty@ zJgqPO&mgql|H1l`A9nm_F#f~+;qj6B=KFRBfCkE}e^>t~{N*3fFEe85Xa4>9aDT^t L2KIgR|8D{SA};`O literal 0 HcmV?d00001 diff --git a/tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-gray-expected.bin b/tests/SharpCorePDF.Tests/Assets/jpeg/jpeg-gray-expected.bin new file mode 100644 index 0000000000000000000000000000000000000000..8637891fc129f4b8b0e22c18d1cb7d07daa9acb0 GIT binary patch literal 256 zcmZQz;}MdOQ_(UovvKn93yIyuHU-*;Bl~_)8;H%v2M$*1INx>YVMpcb@syLYd7yaaP;)W>vw^M&R@2A tiF;N$`UAd82aiwDH{e}I9TgF%2nfRRy<;r|f^d7$B}U^9U>Kma2XGYcyl zJ5Vn-P{CFKpqB)&N!40_7Q41X+a?4ISBp0~6Vm3Pp?>CobercG`GQH0a_7 z72~9$CQdFfaS2H&RW)@DO)V2sGjj_|D`yv1H+K(Dui%i-u<(e;sN|H?wDgS3tm2Z= zvhs?`s^*r~w)T$Bu1S-pOr17;#>`oZ7B5-4Z25|nt2S-kvUS_`9Xod&I(+2lvEwIB zp1O4T%GGPvZ`{1~@X_NZPoF)1@$%KjPoKYh{r3IG&tLyru_&7R@U>mRDWW%=*? zXE?It+dr<~%nzo&DX}l^Rlod0`{n(n_>HIjJ^!a#H~(OKLXFVhzVPq%AGN>y +