On Mon, 12 Oct 2026 22:50:45 GMT, Sergey Bylokhov <[email protected]> wrote:
>> I don't fully understand the suggestion. >> But I've created a test for this that validates that the data is corrected >> during write. > > 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(); } } ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/33217#discussion_r4253034057
