On Mon, 12 Oct 2026 22:52:03 GMT, Sergey Bylokhov <[email protected]> wrote:

>> JPEGQTable.getScaledInstance(scale, false) mentioned above is specified to 
>> return a table "clamped to the range 1..32767" 
>> ([JPEGQTable.java#L179](https://github.com/openjdk/jdk/blob/f2fd22f7a23505bef47227a97a51bc9e1837111e/src/java.desktop/share/classes/javax/imageio/plugins/jpeg/JPEGQTable.java#L179)),
>>  and such a table can be passed to the writer (metadata `dqtable` node, 
>> `JPEGImageWriteParam.setEncodeTables`) and to the reader 
>> (`JPEGImageReadParam.setDecodeTables`). Before this patch the libjpeg seems 
>> writes a 16bit data, and the result decodes. After the patch every entry 
>> above 255 is silently clamped to 255, on the write and decode since the code 
>> path is shared.
>> 
>> So I do not think this is accurate:
>>>ImageIO's native JPEG library is compiled to support baseline JPEG only. 
>>>This means 8 bit quantization values in the range 1 -> 255.
>
> Something like this:
> 
> public final class JpegQTableTest {
> 
>     public static void main(String[] args) throws Exception {
>         byte[] big = write(JPEGQTable.K1Luminance.getScaledInstance(20f, 
> false));
>         byte[] small = write(JPEGQTable.K1Luminance.getScaledInstance(20f, 
> true));
>         int s1 = ImageIO.read(new 
> ByteArrayInputStream(big)).getRaster().getSample(0, 0, 0);
>         int s2 = ImageIO.read(new 
> ByteArrayInputStream(small)).getRaster().getSample(0, 0, 0);
>         System.out.println("bytes: " + big.length + " vs " + small.length);
>         System.out.println("sample: " + s1 + " vs " + s2);
>         if (Arrays.equals(big, small) || s1 == s2) {
>             throw new RuntimeException("Clamped!");
>         }
>     }
> 
>     private static byte[] write(JPEGQTable qTable) throws Exception {
>         BufferedImage img = new BufferedImage(8, 8, 
> BufferedImage.TYPE_BYTE_GRAY);
>         ImageWriter w = ImageIO.getImageWritersByFormatName("JPEG").next();
>         ImageWriteParam p = w.getDefaultWriteParam();
>         ImageTypeSpecifier type = 
> ImageTypeSpecifier.createFromRenderedImage(img);
>         IIOMetadata meta = w.getDefaultImageMetadata(type, p);
>         String fmt = meta.getNativeMetadataFormatName();
>         Node root = meta.getAsTree(fmt);
> 
>         Node markerSeq = null;
>         NodeList kids = root.getChildNodes();
>         for (int i = 0; i < kids.getLength(); i++) {
>             if (kids.item(i).getNodeName().equals("markerSequence")) {
>                 markerSeq = kids.item(i);
>             }
>         }
>         if (markerSeq == null) throw new RuntimeException("no 
> markerSequence");
> 
>         // Replace existing dqt tables
>         for (int i = 0; i < markerSeq.getChildNodes().getLength(); i++) {
>             Node c = markerSeq.getChildNodes().item(i);
>             if (c.getNodeName().equals("dqt")) {
>                 for (int j = 0; j < c.getChildNodes().getLength(); j++) {
>                     IIOMetadataNode qt = 
> (IIOMetadataNode)(c.getChildNodes().item(j));
>                     qt.setUserObject(qTable);
>                 }
>             }
>         }
>         meta.setFromTree(fmt, root);
> 
>         ByteArrayOutputStream baos = new ByteArrayOutputStream();
>         ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
>         w.setOutput(ios);
>         w.write(null, new IIOImage(img, null, meta), p);
>         ios.flush();
>         return baos.toByteArray();
>     }
> }

>From above observation it looks like 8-bit quantization was not strictly 
>enforced. With updated code we are changing the way quantization works.

-------------

PR Review Comment: https://git.openjdk.org/jdk/pull/33217#discussion_r4258281300

Reply via email to