From 1328dbf763d27c11deac5ab73350431c7db0fbc3 Mon Sep 17 00:00:00 2001 From: zcronix <99763063+zcronix@users.noreply.github.com> Date: Fri, 3 Oct 2025 04:49:40 -0400 Subject: [PATCH] Handle lossless JPEG with non-zero point transform (#1183) * Handle lossless JPEG with non-zero point transform (Pt parameter) * Add unit test * Inserting a fix for last pixel not being output * Address issues from code review --------- Co-authored-by: Harald Kuhr --- .../plugins/jpeg/JPEGLosslessDecoder.java | 26 +++++++- .../plugins/jpeg/JPEGImageReaderTest.java | 61 ++++++++++++++++++ .../resources/jpeg-lossless/gradient_ls.jpg | Bin 0 -> 16740 bytes .../jpeg-lossless/gradient_ls_pt.jpg | Bin 0 -> 9448 bytes 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 imageio/imageio-jpeg/src/test/resources/jpeg-lossless/gradient_ls.jpg create mode 100644 imageio/imageio-jpeg/src/test/resources/jpeg-lossless/gradient_ls_pt.jpg diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGLosslessDecoder.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGLosslessDecoder.java index 01656649..2fdaacb2 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGLosslessDecoder.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGLosslessDecoder.java @@ -212,7 +212,9 @@ final class JPEGLosslessDecoder { final int[] firstValue = new int[numComp]; for (int i = 0; i < numComp; i++) { - firstValue[i] = (1 << (precision - 1)); + // scan.approxLow is the point transformation (Pt) value + // ref. ISO/IEC 10918-1 H.1.2.1 + firstValue[i] = (1 << (precision - scan.approxLow - 1)); } final int[] pred = new int[numComp]; @@ -232,6 +234,11 @@ final class JPEGLosslessDecoder { output(pred); current = decode(pred, temp, index); } + + if ((current == JPEG.EOI) && (xLoc == xDim - 1) && (yLoc == yDim - 1)) { + // Output value left in pred if EOI is hit while decoding last pixel + output(pred); + } break; //current=MARKER } @@ -268,6 +275,17 @@ final class JPEGLosslessDecoder { // TODO oe: 05.05.2018 Is it correct loop? Content of outputData from previous iteration is always lost. } while ((current != JPEG.EOI) && ((xLoc < xDim) && (yLoc < yDim)) && (scanNum == 0)); + // Apply point transform to output. This must be done after it has finished being + // used for predictive purposes. + if (scan.approxLow != 0) { + for (int componentIndex = 0; componentIndex < numComp; ++componentIndex) { + int[] comp = outputData[componentIndex]; + for (int i = 0; i < comp.length; i++) { + comp[i] = mask & (comp[i] << scan.approxLow); + } + } + } + return outputData; } @@ -329,9 +347,11 @@ final class JPEGLosslessDecoder { private int decodeSingle(final int[] prev, final int[] temp, final int[] index) throws IOException { // At the beginning of the first line and // at the beginning of each restart interval the prediction value of 2P – 1 is used, where P is the input precision. + // If the point transformation parameter (see A.4) is non-zero, the prediction value at the beginning of the first lines and the + // beginning of each restart interval is 2P – Pt – 1 , where Pt is the value of the point transformation parameter if (restarting) { restarting = false; - prev[0] = (1 << (frame.samplePrecision - 1)); + prev[0] = (1 << (frame.samplePrecision - scan.approxLow - 1)); } else { final int[] outputData = this.outputData[0]; @@ -686,7 +706,7 @@ final class JPEGLosslessDecoder { return getPreviousY(data); } else { - return (1 << (frame.samplePrecision - 1)); + return (1 << (frame.samplePrecision - scan.approxLow - 1)); } } diff --git a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java index b0dd0994..12f08716 100644 --- a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java +++ b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java @@ -2033,4 +2033,65 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest> 4) << 4; + + assertEquals(expected, image.getRaster().getSample(x, y, 0)); + } + } + } + finally { + reader.dispose(); + } + } } diff --git a/imageio/imageio-jpeg/src/test/resources/jpeg-lossless/gradient_ls.jpg b/imageio/imageio-jpeg/src/test/resources/jpeg-lossless/gradient_ls.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ec52075ff25c401eecb6ee86ba4aa690783f2f1 GIT binary patch literal 16740 zcmeH~JqiLr3`P?{@TXY|J%p#Q60~0nPv9{;iuH({H_*z;Qmwqn7(*89)5)7{mZ?4- zk1v_}sBdZ3%hgg;Mbtj27a2F-Kh=rUtIX`ju78dWoBEQ0-P--F=CLY=m@YZItgA4h=2%D3M`8Tf<~YbXapJ|2+#-)g3*8oh~NYe0TB?PBfzrgBzQF- z0wP2yuq+w~8i7Wj5om-UKqEK^Mgt-sf)hXlL_ma&0L!A2;MIT#h!CZ~vS=V^1R8-x Ypb>%qjo=^{4TyjUP5=@9MuhtO0EFlcZU6uP literal 0 HcmV?d00001 diff --git a/imageio/imageio-jpeg/src/test/resources/jpeg-lossless/gradient_ls_pt.jpg b/imageio/imageio-jpeg/src/test/resources/jpeg-lossless/gradient_ls_pt.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b27940aba93a9ee5c7958fa3e80892e792694703 GIT binary patch literal 9448 zcmeI2y$!-Z3`V~Lf)GCvNJvObkO3GVRV1hwgE2Bnc7XvXfr^eXj!P7sXztunyri)b zQmKoZHCXvcJY zm^PYg)TmXN77ntO32$i4WlF7-*QQtWqP#YpqT?!O)L`#kIim)9?`hvex8Wq5lnD>M zq(09OZserwHbUYKal-CK31F04)JYVRRNP>R1zlhXSOS((&}+AoMeJ)_Ba4s02jbbaBckk1;G6~!1d2B D=kzJQ literal 0 HcmV?d00001