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)

Reply via email to