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
