<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
So you'd end up with a BI that is of TYPE_CUSTOM ?<br>
That might make decoding a bit faster but rendering would be very
slow. I'm not sure I'd want that trade-off.<br>
<br>
-phil.<br>
<br>
<br>
<div class="moz-cite-prefix">On 1/6/26 8:59 AM, Jeremy Wood wrote:<br>
</div>
<blockquote type="cite" cite="mid:emdc14f409-accd-48c9-a862-a64b516c7b12@gmail.com">
<style id="css_styles">blockquote.cite { margin-left: 5px; margin-right: 0px; padding-left: 10px; padding-right:0px; border-left: 1px solid #cccccc }blockquote.cite2 {margin-left: 5px; margin-right: 0px; padding-left: 10px; padding-right:0px; border-left: 1px solid #cccccc; margin-top: 3px; padding-top: 0px; }a img { border: 0px; }li[style='text-align: center;'], li[style='text-align: center; '], li[style='text-align: right;'], li[style='text-align: right; '] { list-style-position: inside;}body { font-family: Helvetica; font-size: 9pt; }.quote { margin-left: 1em; margin-right: 1em; border-left: 5px #ebebeb solid; padding-left: 0.3em; }a.em-mention[href] { text-decoration: none; color: inherit; border-radius: 3px; padding-left: 2px; padding-right: 2px; background-color: #e2e2e2; }._em_placeholder {color: gray; border-bottom: 1px dotted lightblue;}._em_placeholder:before{color:gray; content: '{{ ';}._em_placeholder:after{color:gray; content: ' }}';}</style>
I’m exploring the performance of loading BufferedImages. I
recently noticed that ImageIO.read() can be made ~15% faster by
changing the BufferedImage type (so we'd avoid a RGB -> BGR
conversion).
<div><br>
</div>
<div style="">IMO this wouldn’t be too hard to program, but it
might be considered too invasive to accept. Is there any
interest/support in exploring this idea if I submit a PR for it?</div>
<div>
<div><br>
</div>
<div>Specifically this is the performance I’m observing on my
MacBook:</div>
<div><br>
</div>
<div>Benchmark Mode Cnt
Score Error Units</div>
<div>JPEG_Default_vs_RGB.measureDefaultImageType avgt 15
42.589 ± 0.137 ms/op</div>
<div>JPEG_Default_vs_RGB.measureRGBImageType avgt 15
35.624 ± 0.589 ms/op</div>
<div><br>
</div>
<div>The first “default” approach uses
ImageIO.read(inputStream).</div>
<div><br>
</div>
<div>The second “RGB” approach creates a BufferedImage target
with a custom ColorModel that is similar to TYPE_3BYTE_BGR,
except it reverse the colors so they are ordered RGB.</div>
<div><br>
</div>
<div>This derives from the observation that in JPEGImageReader
we create a one-line <font face="Courier New">raster</font>
field that uses this 3-byte RGB model. Later in <font face="Courier New">acceptPixels()</font> we call <font face="Courier New">target.setRect(x, y, raster)</font> .
Here <font face="Courier New">target</font> is the
WritableRaster of the final BufferedImage. By default it will
be a 3-byte BGR. So we’re spending 15+% of our time converting
RGB-encoded data (from <font face="Courier New">raster</font>)
to BGR-encoded data (for <font face="Courier New">target</font>).</div>
</div>
<div><br>
</div>
<div>So the “pros” of my proposal should include a faster loading
time for many JPEG images. I’d argue ImageIO should always
default to the fastest (reasonable) implementation possible.</div>
<div><br>
</div>
<div style="">IMO the major “con” is: <font face="Courier New">target.getType()</font>
would change from <font face="Courier New">BufferedImage.TYPE_3BYTE_BGR</font>
to <font face="Courier New">BufferedImage.TYPE_CUSTOM</font> .
This doesn’t technically violate any documentation that I know
of, but it seems (IMO) like something some clients will have
made assumptions about, and therefore some downstream code may
break. (And maybe other devs here can identify other problems
I’m not anticipating.)</div>
<div style=""><br>
</div>
<div style="">Any thoughts / feedback?</div>
<div style=""><br>
</div>
<div style="">Regards,</div>
<div style=""> - Jeremy</div>
<div style=""><br>
</div>
<div style="">Below is the JMH code used to generate the output
above:</div>
<div style=""><br>
</div>
<div style="">package org.sun.awt.image;</div>
<div style=""><br>
</div>
<div style="">import org.openjdk.jmh.annotations.*;</div>
<div style="">import org.openjdk.jmh.infra.Blackhole;</div>
<div style=""><br>
</div>
<div style="">import javax.imageio.ImageIO;</div>
<div style="">import javax.imageio.ImageReadParam;</div>
<div style="">import javax.imageio.ImageReader;</div>
<div style="">import java.awt.*;</div>
<div style="">import java.awt.color.ColorSpace;</div>
<div style="">import java.awt.image.*;</div>
<div style="">import java.io.ByteArrayInputStream;</div>
<div style="">import java.io.ByteArrayOutputStream;</div>
<div style="">import java.io.IOException;</div>
<div style="">import java.util.Iterator;</div>
<div style="">import java.util.Random;</div>
<div style="">import java.util.concurrent.TimeUnit;</div>
<div style=""><br>
</div>
<div style="">@BenchmarkMode(Mode.AverageTime)</div>
<div style="">@OutputTimeUnit(TimeUnit.MILLISECONDS)</div>
<div style="">@Warmup(iterations = 5, time = 1)</div>
<div style="">@Measurement(iterations = 5, time = 20)</div>
<div style="">@Fork(3)</div>
<div style="">@State(Scope.Thread)</div>
<div style="">public class JPEG_Default_vs_RGB {</div>
<div style=""><br>
</div>
<div style=""> byte[] jpgImageData;</div>
<div style=""><br>
</div>
<div style=""> @Setup</div>
<div style=""> public void setup() throws Exception {</div>
<div style=""> jpgImageData = createImageData(2_500);</div>
<div style=""> }</div>
<div style=""><br>
</div>
<div style=""> @Benchmark</div>
<div style=""> public void measureDefaultImageType(Blackhole
bh) throws Exception {</div>
<div style=""> BufferedImage bi = readJPG(false);</div>
<div style=""> bi.flush();</div>
<div style=""> bh.consume(bi);</div>
<div style=""> }</div>
<div style=""><br>
</div>
<div style=""> @Benchmark</div>
<div style=""> public void measureRGBImageType(Blackhole bh)
throws Exception {</div>
<div style=""> BufferedImage bi = readJPG(true);</div>
<div style=""> bi.flush();</div>
<div style=""> bh.consume(bi);</div>
<div style=""> }</div>
<div style=""><br>
</div>
<div style=""> private BufferedImage readJPG(boolean
useRGBTarget) throws Exception {</div>
<div style=""> Iterator<ImageReader> readers;</div>
<div style=""> try (ByteArrayInputStream byteIn = new
ByteArrayInputStream(jpgImageData)) {</div>
<div style=""> if (!useRGBTarget)</div>
<div style=""> return ImageIO.read(byteIn);</div>
<div style=""><br>
</div>
<div style=""> readers =
ImageIO.getImageReaders(ImageIO.createImageInputStream(byteIn));</div>
<div style=""> if (!readers.hasNext()) {</div>
<div style=""> throw new IOException("No reader
found for the given file.”);</div>
<div style=""> }</div>
<div style=""> }</div>
<div style=""><br>
</div>
<div style=""> ImageReader reader = readers.next();</div>
<div style=""> try (ByteArrayInputStream byteIn = new
ByteArrayInputStream(jpgImageData)) {</div>
<div style="">
reader.setInput(ImageIO.createImageInputStream(byteIn));</div>
<div style=""><br>
</div>
<div style=""> int width = reader.getWidth(0);</div>
<div style=""> int height = reader.getHeight(0);</div>
<div style=""><br>
</div>
<div style=""> // this is copied from how BufferedImage
sets up a TYPE_3BYTE_BGR image,</div>
<div style=""> // except we use {0, 1, 2} to make it an
RGB image:</div>
<div style=""> ColorSpace cs =
ColorSpace.getInstance(ColorSpace.CS_sRGB);</div>
<div style=""> int[] nBits = {8, 8, 8};</div>
<div style=""> int[] bOffs = {0, 1, 2};</div>
<div style=""> ColorModel colorModel = new
ComponentColorModel(cs, nBits, false, false,</div>
<div style=""> Transparency.OPAQUE,</div>
<div style=""> DataBuffer.TYPE_BYTE);</div>
<div style=""> WritableRaster raster =
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,</div>
<div style=""> width, height, width * 3, 3,
bOffs, null);</div>
<div style=""><br>
</div>
<div style=""> BufferedImage rgbImage = new
BufferedImage(colorModel, raster, false, null);</div>
<div style=""><br>
</div>
<div style=""> ImageReadParam param =
reader.getDefaultReadParam();</div>
<div style=""> param.setDestination(rgbImage);</div>
<div style=""><br>
</div>
<div style=""> reader.read(0, param);</div>
<div style=""><br>
</div>
<div style=""> return rgbImage;</div>
<div style=""> } finally {</div>
<div style=""> reader.dispose();</div>
<div style=""> }</div>
<div style=""> }</div>
<div style=""><br>
</div>
<div style=""> /**</div>
<div style=""> * Create a large sample image stored as a JPG</div>
<div style=""> *</div>
<div style=""> * @return the byte representation of the JPG
image.</div>
<div style=""> */</div>
<div style=""> private static byte[] createImageData(int
squareSize) throws Exception {</div>
<div style=""> BufferedImage bi = new
BufferedImage(squareSize, squareSize,</div>
<div style=""> BufferedImage.TYPE_INT_RGB);</div>
<div style=""> Random r = new Random(0);</div>
<div style=""> Graphics2D g = bi.createGraphics();</div>
<div style=""> for (int a = 0; a < 20000; a++) {</div>
<div style=""> g.setColor(new
Color(r.nextInt(0xffffff)));</div>
<div style=""> int radius = 10 + r.nextInt(90);</div>
<div style=""> g.fillOval(r.nextInt(bi.getWidth()),
r.nextInt(bi.getHeight()),</div>
<div style=""> radius, radius);</div>
<div style=""> }</div>
<div style=""> g.dispose();</div>
<div style=""><br>
</div>
<div style=""> try (ByteArrayOutputStream out = new
ByteArrayOutputStream()) {</div>
<div style=""> ImageIO.write(bi, "jpg", out);</div>
<div style=""> return out.toByteArray();</div>
<div style=""> }</div>
<div style=""> }</div>
<div style="">}</div>
</blockquote>
<br>
</body>
</html>