Gary Lucas created IMAGING-329: ---------------------------------- Summary: Opportunity to enhance speed of PNG Read Key: IMAGING-329 URL: https://issues.apache.org/jira/browse/IMAGING-329 Project: Commons Imaging Issue Type: Improvement Components: Format: PNG Environment: {{{{ }}}} Reporter: Gary Lucas
When reading an image file, the PNG reader makes calls to BufferedImage.setRGB() for each pixel to be set in its output image. The setRGB method has a lot of overhead, and we could speed up processing by calling setRGB on an entire row of pixels rather than one-at-a-time. The expediter loop also makes calls to it's own getRGB method which is generic across all the different PNG formats (32-bit true color, 24-bit true-color, grayscale, indexed color). This action involves a lot of conditional evaluation, switch statements across formats. If we were to implement specific loops for the most common formats (24 and 32 bit true color), we could streamline reading for those formats. I experimented with both of these approaches using a 5000-by-5000 RGB image (24-bit true color without transparency or gamma correction). The results were: Current Version 1-Alpha 3: 0.917 seconds Set entire row of pixels: 0.717 seconds Custom loop for format: 0.609 seconds I note that the saving is not spectacular (it would have been more important a decade ago when computers were slower), particularly since images of such a large size don't usually use PNG as a data format. {code:java} for (int y = 0; y < height; y++) { final byte[] unfiltered = getNextScanline( is, pixelBytesPerScanLine, prev, bytesPerPixel); prev = unfiltered; final BitParser bitParser = new BitParser( unfiltered, bitsPerPixel, bitDepth); for (int x = 0; x < width; x++) { final int rgb = getRGB(bitParser, x); bi.setRGB(x, y, rgb); } } {code} And here's the modified inner loop for writing one row at a time: {code:java} final int []argb = new int[width]; for (int x = 0; x < width; x++) { argb[x] = getRGB(bitParser, x); // from ScanExpediterSimple } bi.setRGB(0, y, width, 1, argb, 0, width); {code} And, finally, here's a modified block that avoids the getRGB method and just processes bytes directly. It has to check on a couple of pre-conditions to see if the data is in one of the frequently used formats, and then processes the bytes from the source file without any of the bit-access methods used by the ScanExpediterSimple class's getRGB method. {code:java} if (pngColorType == PngColorType.TRUE_COLOR && transparencyFilter == null && gammaCorrection == null) { for (int y = 0; y < height; y++) { final byte[] unfiltered = getNextScanline( is, pixelBytesPerScanLine, prev, bytesPerPixel); int k = 0; final int []argb = new int[width]; for (int x = 0; x < width; x++) { int r = unfiltered[k++]&0xff; int g = unfiltered[k++]&0xff; int b = unfiltered[k++]&0xff; argb[x] = 0xff000000 | (r<<16)|(g<<8)|b; } bi.setRGB(0, y, width, 1, argb, 0, width); } return; } {code} -- This message was sent by Atlassian Jira (v8.20.1#820001)