On Tue, 13 Oct 2026 11:54:50 GMT, Jayathirth D V <[email protected]> wrote:

>> 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.

> 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.

The comment is accurate. It should be in the range 1->255 for 8 bit JPEG.
If someone created a JPEG plugin that supported non-baseline JPEG, ie 12 bit 
samples, then their implementation would have to allow 1->65535 but our 
implementation does not and should not. It is baseline 8 bit only.
So it is appropriate for the public API to allow 16-bit (to allow for that 
other theoretical plugin) but the plugin implementation in each case will do 
what is appropriate for what it supports. Which is why this check is in the 
implementation.

And the JPEG standard says
B.2.4.1 Quantization table-specification syntax
..
Pq: Quantization table element precision – Specifies the precision of the Qk 
values. Value 0 indicates 8-bit Qk
values; value 1 indicates 16-bit Qk values. Pq shall be zero for 8 bit sample 
precision P (see B.2.2).

So allowing 16 bit quantization for an 8 bit JPEG contravenes the spec.

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

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

Reply via email to