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