You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xmlgraphics.apache.org by me...@apache.org on 2012/07/25 11:49:29 UTC

svn commit: r1365504 - in /xmlgraphics/commons/trunk: src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java status.xml test/java/org/apache/xmlgraphics/StandardTestSuite.java test/java/org/apache/xmlgraphics/ps/ImageEncodingHelperTestCase.java

Author: mehdi
Date: Wed Jul 25 09:49:29 2012
New Revision: 1365504

URL: http://svn.apache.org/viewvc?rev=1365504&view=rev
Log:
Bugfix#53599: Performance improvement to bitmap image encoding with RGB DirectColorModel

Modified:
    xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java
    xmlgraphics/commons/trunk/status.xml
    xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/StandardTestSuite.java
    xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/ps/ImageEncodingHelperTestCase.java

Modified: xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java?rev=1365504&r1=1365503&r2=1365504&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java (original)
+++ xmlgraphics/commons/trunk/src/java/org/apache/xmlgraphics/ps/ImageEncodingHelper.java Wed Jul 25 09:49:29 2012
@@ -20,10 +20,12 @@
 package org.apache.xmlgraphics.ps;
 
 import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
 import java.awt.image.ColorModel;
 import java.awt.image.ComponentColorModel;
 import java.awt.image.DataBuffer;
 import java.awt.image.DataBufferByte;
+import java.awt.image.DirectColorModel;
 import java.awt.image.IndexColorModel;
 import java.awt.image.PixelInterleavedSampleModel;
 import java.awt.image.Raster;
@@ -31,6 +33,7 @@ import java.awt.image.RenderedImage;
 import java.awt.image.SampleModel;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Arrays;
 
 import org.apache.xmlgraphics.image.GraphicsUtil;
 
@@ -111,6 +114,10 @@ public class ImageEncodingHelper {
     }
 
     private void writeRGBTo(OutputStream out) throws IOException {
+        boolean encoded = encodeRenderedImageWithDirectColorModelAsRGB(image, out);
+        if (encoded) {
+            return;
+        }
         encodeRenderedImageAsRGB(image, out);
     }
 
@@ -122,7 +129,7 @@ public class ImageEncodingHelper {
      */
     public static void encodeRenderedImageAsRGB(RenderedImage image, OutputStream out)
                 throws IOException {
-        Raster raster = image.getData();
+        Raster raster = getRaster(image);
         Object data;
         int nbands = raster.getNumBands();
         int dataType = raster.getDataBuffer().getDataType();
@@ -143,13 +150,13 @@ public class ImageEncodingHelper {
             data = new double[nbands];
             break;
         default:
-            throw new IllegalArgumentException("Unknown data buffer type: "
-                                               + dataType);
+            throw new IllegalArgumentException("Unknown data buffer type: " + dataType);
         }
 
         ColorModel colorModel = image.getColorModel();
         int w = image.getWidth();
         int h = image.getHeight();
+
         byte[] buf = new byte[w * 3];
         for (int y = 0; y < h; y++) {
             int idx = -1;
@@ -164,6 +171,67 @@ public class ImageEncodingHelper {
     }
 
     /**
+     * Writes a RenderedImage to an OutputStream. This method optimizes the encoding
+     * of the {@link DirectColorModel} as it is returned by {@link ColorModel#getRGBdefault}.
+     * @param image the image
+     * @param out the OutputStream to write the pixels to
+     * @return true if this method encoded this image, false if the image is incompatible
+     * @throws IOException if an I/O error occurs
+     */
+    public static boolean encodeRenderedImageWithDirectColorModelAsRGB(
+            RenderedImage image, OutputStream out) throws IOException {
+        ColorModel cm = image.getColorModel();
+        if (cm.getColorSpace() != ColorSpace.getInstance(ColorSpace.CS_sRGB)) {
+            return false; //Need to go through color management
+        }
+        if (!(cm instanceof DirectColorModel)) {
+            return false; //Only DirectColorModel is supported here
+        }
+        DirectColorModel dcm = (DirectColorModel)cm;
+        final int[] templateMasks = new int[]
+                {0x00ff0000 /*R*/, 0x0000ff00 /*G*/, 0x000000ff /*B*/, 0xff000000 /*A*/};
+        int[] masks = dcm.getMasks();
+        if (!Arrays.equals(templateMasks, masks)) {
+            return false; //no flexibility here right now, might never be used anyway
+        }
+
+        Raster raster = getRaster(image);
+        int dataType = raster.getDataBuffer().getDataType();
+        if (dataType != DataBuffer.TYPE_INT) {
+            return false; //not supported
+        }
+
+        int w = image.getWidth();
+        int h = image.getHeight();
+
+        int[] data = new int[w];
+        byte[] buf = new byte[w * 3];
+        for (int y = 0; y < h; y++) {
+            int idx = -1;
+            raster.getDataElements(0, y, w, 1, data);
+            for (int x = 0; x < w; x++) {
+                int rgb = data[x];
+                buf[++idx] = (byte)(rgb >> 16);
+                buf[++idx] = (byte)(rgb >> 8);
+                buf[++idx] = (byte)(rgb);
+            }
+            out.write(buf);
+        }
+
+        return true;
+    }
+
+    private static Raster getRaster(RenderedImage image) {
+        if (image instanceof BufferedImage) {
+            return ((BufferedImage)image).getRaster();
+        } else {
+            //Note: this copies the image data (double memory consumption)
+            //TODO Investigate encoding in stripes: RenderedImage.copyData(WritableRaster)
+            return image.getData();
+        }
+    }
+
+    /**
      * Converts a byte array containing 24 bit RGB image data to a grayscale
      * image.
      *

Modified: xmlgraphics/commons/trunk/status.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/status.xml?rev=1365504&r1=1365503&r2=1365504&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/status.xml (original)
+++ xmlgraphics/commons/trunk/status.xml Wed Jul 25 09:49:29 2012
@@ -27,6 +27,7 @@
 	  <person name="Adrian Cumiskey"   email="acumiskey@apache.org"     id="AC"/>
 	  <person name="Christian Geisert" email="chrisg@apache.org"        id="CG"/>
 	  <person name="Vincent Hennebert" email="vhennebert@apache.org"    id="VH"/>
+	  <person name="Mehdi Houshmand"   email="mehdi@apache.org"         id="MH"/>
 	  <person name="Clay Leeds"        email="clay@apache.org"          id="CL"/>
 	  <person name="Jeremias Märki"    email="jeremias@apache.org"      id="JM"/>
 	  <person name="Cameron McCormack" email="cam@apache.org"           id="CM"/>
@@ -41,6 +42,9 @@
   </contexts>
   <changes>
     <release version="Trunk" date="n/a">
+      <action context="Code" dev="MH" type="fix" fixes-bug="53599" due-to="JM and MH collaboration">
+        Performance improvement of DirectColorMode bitmap images
+      </action>
       <action context="Code" dev="MH" type="fix" fixes-bug="53570" due-to="Robert Meyer">
         Set the systemID of the StreamSource within the DataURIResolver
       </action>

Modified: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/StandardTestSuite.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/StandardTestSuite.java?rev=1365504&r1=1365503&r2=1365504&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/StandardTestSuite.java (original)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/StandardTestSuite.java Wed Jul 25 09:49:29 2012
@@ -23,6 +23,7 @@ import junit.framework.Test;
 import junit.framework.TestSuite;
 
 import org.apache.xmlgraphics.image.codec.png.PNGEncoderTest;
+import org.apache.xmlgraphics.ps.ImageEncodingHelperTestCase;
 import org.apache.xmlgraphics.ps.PSEscapeTestCase;
 import org.apache.xmlgraphics.ps.dsc.ListenerTestCase;
 import org.apache.xmlgraphics.ps.dsc.events.DSCValueParserTestCase;
@@ -54,6 +55,7 @@ public class StandardTestSuite {
         suite.addTest(new TestSuite(ServiceTest.class));
         suite.addTest(new TestSuite(ClasspathResourceTest.class));
         suite.addTest(new TestSuite(PSEscapeTestCase.class));
+        suite.addTest(new TestSuite(ImageEncodingHelperTestCase.class));
         suite.addTest(new TestSuite(DSCValueParserTestCase.class));
         suite.addTest(new TestSuite(DSCToolsTestCase.class));
         suite.addTest(new TestSuite(ListenerTestCase.class));

Modified: xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/ps/ImageEncodingHelperTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/ps/ImageEncodingHelperTestCase.java?rev=1365504&r1=1365503&r2=1365504&view=diff
==============================================================================
--- xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/ps/ImageEncodingHelperTestCase.java (original)
+++ xmlgraphics/commons/trunk/test/java/org/apache/xmlgraphics/ps/ImageEncodingHelperTestCase.java Wed Jul 25 09:49:29 2012
@@ -19,16 +19,27 @@
 
 package org.apache.xmlgraphics.ps;
 
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import java.awt.Color;
 import java.awt.Graphics2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
+import java.awt.image.DirectColorModel;
+import java.awt.image.WritableRaster;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Arrays;
 
 import junit.framework.TestCase;
 
 import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 public class ImageEncodingHelperTestCase extends TestCase {
 
@@ -52,6 +63,26 @@ public class ImageEncodingHelperTestCase
     }
 
     /**
+     * Tests encodeRenderedImageWithDirectColorModeAsRGB(). Tests the optimised method against the
+     * non-optimised method(encodeRenderedImageAsRGB) to ensure the BufferedImage produced are the
+     * same.
+     * @throws IOException if an I/O error occurs.
+     */
+    public void testEncodeRenderedImageWithDirectColorModelAsRGB() throws IOException {
+        BufferedImage image = new BufferedImage(100, 75, BufferedImage.TYPE_INT_ARGB);
+        image = prepareImage(image);
+
+        ByteArrayOutputStream optimized = new ByteArrayOutputStream();
+        ImageEncodingHelper.encodeRenderedImageWithDirectColorModelAsRGB(image, optimized);
+
+        ByteArrayOutputStream nonoptimized = new ByteArrayOutputStream();
+        ImageEncodingHelper.encodeRenderedImageAsRGB(image, nonoptimized);
+
+        assertTrue(Arrays.equals(nonoptimized.toByteArray(), optimized.toByteArray()));
+
+    }
+
+    /**
      * Tests a BGR versus RBG image. Debugging shows the BGR follows the optimizeWriteTo() (which is intended).
      * The bytes are compared with the RBG image, which happens to follow the writeRGBTo().
      * 
@@ -75,4 +106,45 @@ public class ImageEncodingHelperTestCase
         assertTrue(Arrays.equals(baosBGR.toByteArray(), baosRGB.toByteArray()));
     }
 
+    /**
+     * Tests encodeRenderedImageWithDirectColorModeAsRGB(). Uses mocking to test the method
+     * implementation.
+     * @throws FileNotFoundException if expected file was not found.
+     * @throws IOException if an I/O error occurs.
+     */
+    public void testMockedEncodeRenderedImageWithDirectColorModelAsRGB() throws IOException {
+        BufferedImage image = mock(BufferedImage.class);
+        final int[] templateMasks = new int[] { 0x00ff0000 /*R*/, 0x0000ff00 /*G*/,
+                                                    0x000000ff /*B*/, 0xff000000 /*A*/};
+        DirectColorModel dcm = new DirectColorModel(255, templateMasks[0], templateMasks[1],
+                templateMasks[2], templateMasks[3]);
+
+        WritableRaster raster = mock(WritableRaster.class);
+        DataBuffer buffer = mock(DataBuffer.class);
+
+        when(image.getColorModel()).thenReturn(dcm);
+        when(image.getRaster()).thenReturn(raster);
+        when(raster.getDataBuffer()).thenReturn(buffer);
+        when(buffer.getDataType()).thenReturn(DataBuffer.TYPE_INT);
+        when(image.getWidth()).thenReturn(3);
+        when(image.getHeight()).thenReturn(3);
+        final int expectedValue = 1 + 2 << 8 + 3 << 16;
+        Answer<Object> ans = new Answer<Object>() {
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                int[] data = (int[]) args[4];
+                Arrays.fill(data, expectedValue);
+                return null;
+            }
+        };
+        when(raster.getDataElements(anyInt(), anyInt(), anyInt(), anyInt(), anyObject()))
+                .thenAnswer(ans);
+
+        ByteArrayOutputStream optimized = new ByteArrayOutputStream();
+        ImageEncodingHelper.encodeRenderedImageWithDirectColorModelAsRGB(image, optimized);
+
+        byte[] expectedByteArray = new byte[27];
+        Arrays.fill(expectedByteArray, (byte) expectedValue);
+        assertTrue(Arrays.equals(expectedByteArray, optimized.toByteArray()));
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: commits-help@xmlgraphics.apache.org