changeset 7860:fd8810d50c99

8005530: [lcms] Improve performance of ColorConverOp for default destinations Reviewed-by: prr, jgodinez
author bae
date Thu, 07 Mar 2013 14:05:21 +0400
parents f3368a3fc023
children 8e9b133dcec9
files make/sun/cmm/lcms/Makefile make/sun/cmm/lcms/mapfile-vers makefiles/CompileNativeLibraries.gmk makefiles/mapfiles/liblcms/mapfile-vers src/share/classes/sun/java2d/cmm/lcms/LCMS.java src/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java src/share/demo/java2d/J2DBench/build.xml src/share/demo/java2d/J2DBench/resources/cmm_images/img_icc_large.jpg src/share/demo/java2d/J2DBench/resources/cmm_images/img_icc_medium.jpg src/share/demo/java2d/J2DBench/resources/cmm_images/img_icc_small.jpg src/share/demo/java2d/J2DBench/src/j2dbench/tests/cmm/ColorConversionTests.java src/share/demo/java2d/J2DBench/src/j2dbench/tests/cmm/EmbeddedProfileTests.java src/share/native/sun/java2d/cmm/lcms/LCMS.c
diffstat 14 files changed, 572 insertions(+), 182 deletions(-) [+]
line wrap: on
line diff
--- a/make/sun/cmm/lcms/Makefile	Tue Mar 05 17:18:55 2013 +0400
+++ b/make/sun/cmm/lcms/Makefile	Thu Mar 07 14:05:21 2013 +0400
@@ -28,6 +28,9 @@
 LIBRARY = lcms
 PRODUCT = sun
 
+# Use highest level of optimization on this library
+OPTIMIZATION_LEVEL = HIGHEST
+
 include $(BUILDDIR)/common/Defs.gmk
 
 #
--- a/make/sun/cmm/lcms/mapfile-vers	Tue Mar 05 17:18:55 2013 +0400
+++ b/make/sun/cmm/lcms/mapfile-vers	Thu Mar 07 14:05:21 2013 +0400
@@ -27,13 +27,12 @@
 
 SUNWprivate_1.1 {
 	global:
-        Java_sun_java2d_cmm_lcms_LCMS_loadProfile;
-        Java_sun_java2d_cmm_lcms_LCMS_freeProfile;
+        Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative;
+        Java_sun_java2d_cmm_lcms_LCMS_freeProfileNative;
         Java_sun_java2d_cmm_lcms_LCMS_getProfileSize;
         Java_sun_java2d_cmm_lcms_LCMS_getProfileData;
-        Java_sun_java2d_cmm_lcms_LCMS_getTagSize;
-        Java_sun_java2d_cmm_lcms_LCMS_getTagData;
-        Java_sun_java2d_cmm_lcms_LCMS_setTagData;
+        Java_sun_java2d_cmm_lcms_LCMS_getTagNative;
+        Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative;
         Java_sun_java2d_cmm_lcms_LCMS_colorConvert;
         Java_sun_java2d_cmm_lcms_LCMS_getProfileID;
         Java_sun_java2d_cmm_lcms_LCMS_initLCMS;
--- a/makefiles/CompileNativeLibraries.gmk	Tue Mar 05 17:18:55 2013 +0400
+++ b/makefiles/CompileNativeLibraries.gmk	Thu Mar 07 14:05:21 2013 +0400
@@ -1218,7 +1218,7 @@
                 OUTPUT_DIR:=$(INSTALL_LIBRARIES_HERE),\
                 SRC:=$(JDK_TOPDIR)/src/share/native/sun/java2d/cmm/lcms,\
 		LANG:=C,\
-		OPTIMIZATION:=LOW, \
+		OPTIMIZATION:=HIGHEST, \
 		CFLAGS:=$(filter-out -xc99=%none,$(CFLAGS_JDKLIB)) \
 			$(SHARED_LIBRARY_FLAGS) \
 			-I$(JDK_TOPDIR)/src/share/native/sun/java2d \
--- a/makefiles/mapfiles/liblcms/mapfile-vers	Tue Mar 05 17:18:55 2013 +0400
+++ b/makefiles/mapfiles/liblcms/mapfile-vers	Thu Mar 07 14:05:21 2013 +0400
@@ -27,13 +27,12 @@
 
 SUNWprivate_1.1 {
 	global:
-        Java_sun_java2d_cmm_lcms_LCMS_loadProfile;
-        Java_sun_java2d_cmm_lcms_LCMS_freeProfile;
+        Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative;
+        Java_sun_java2d_cmm_lcms_LCMS_freeProfileNative;
         Java_sun_java2d_cmm_lcms_LCMS_getProfileSize;
         Java_sun_java2d_cmm_lcms_LCMS_getProfileData;
-        Java_sun_java2d_cmm_lcms_LCMS_getTagSize;
-        Java_sun_java2d_cmm_lcms_LCMS_getTagData;
-        Java_sun_java2d_cmm_lcms_LCMS_setTagData;
+        Java_sun_java2d_cmm_lcms_LCMS_getTagNative;
+        Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative;
         Java_sun_java2d_cmm_lcms_LCMS_colorConvert;
         Java_sun_java2d_cmm_lcms_LCMS_getProfileID;
         Java_sun_java2d_cmm_lcms_LCMS_initLCMS;
--- a/src/share/classes/sun/java2d/cmm/lcms/LCMS.java	Tue Mar 05 17:18:55 2013 +0400
+++ b/src/share/classes/sun/java2d/cmm/lcms/LCMS.java	Thu Mar 07 14:05:21 2013 +0400
@@ -25,29 +25,91 @@
 
 package sun.java2d.cmm.lcms;
 
-import java.awt.color.ColorSpace;
 import java.awt.color.ICC_Profile;
-import java.awt.color.CMMException;
+import java.util.Arrays;
+import java.util.HashMap;
 import sun.java2d.cmm.ColorTransform;
 import sun.java2d.cmm.PCMM;
-import sun.java2d.cmm.lcms.LCMS;
-import sun.java2d.cmm.lcms.LCMSTransform;
 
 public class LCMS implements PCMM {
 
     /* methods invoked from ICC_Profile */
-    public native long loadProfile(byte[] data);
+    @Override
+    public long loadProfile(byte[] data) {
+        long id = loadProfileNative(data);
 
-    public native void freeProfile(long profileID);
+        if (id != 0L) {
+            if (profiles == null) {
+                profiles = new HashMap<>();
+            }
+            profiles.put(id, new TagCache(id));
+        }
+        return id;
+    }
+
+    private native long loadProfileNative(byte[] data);
+
+    @Override
+    public void freeProfile(long profileID) {
+        TagCache c = profiles.remove(profileID);
+        if (c != null) {
+            c.clear();
+        }
+        if (profiles.isEmpty()) {
+            profiles = null;
+        }
+        freeProfileNative(profileID);
+    }
+
+    private native void freeProfileNative(long profileID);
 
     public native synchronized int getProfileSize(long profileID);
 
     public native synchronized void getProfileData(long profileID, byte[] data);
 
-    public native synchronized int getTagSize(long profileID, int tagSignature);
-    public native synchronized void getTagData(long profileID, int tagSignature,
-                                               byte[] data);
-    public native synchronized void setTagData(long profileID, int tagSignature,
+    @Override
+    public synchronized int getTagSize(long profileID, int tagSignature) {
+        TagCache cache = profiles.get(profileID);
+
+        if (cache ==  null) {
+            cache = new TagCache(profileID);
+            profiles.put(profileID, cache);
+        }
+
+        TagData t = cache.getTag(tagSignature);
+        return t == null ? 0 : t.getSize();
+    }
+
+    private static native byte[] getTagNative(long profileID, int signature);
+
+    @Override
+    public synchronized void getTagData(long profileID, int tagSignature,
+                                               byte[] data)
+    {
+        TagCache cache = profiles.get(profileID);
+
+        if (cache ==  null) {
+            cache = new TagCache(profileID);
+            profiles.put(profileID, cache);
+        }
+
+        TagData t = cache.getTag(tagSignature);
+        if (t != null) {
+            t.copyDataTo(data);
+        }
+    }
+
+    @Override
+    public synchronized void setTagData(long profileID, int tagSignature, byte[] data) {
+        TagCache cache = profiles.get(profileID);
+
+        if (cache != null) {
+            cache.clear();
+        }
+        setTagDataNative(profileID, tagSignature, data);
+    }
+
+    private native synchronized void setTagDataNative(long profileID, int tagSignature,
                                                byte[] data);
 
     public static native long getProfileID(ICC_Profile profile);
@@ -103,4 +165,59 @@
 
         initLCMS(LCMSTransform.class, LCMSImageLayout.class, ICC_Profile.class);
     }
+
+    private static class TagData {
+        private int signature;
+        private byte[] data;
+
+        TagData(int sig, byte[] data) {
+            this.signature = sig;
+            this.data = data;
+        }
+
+        int getSize() {
+            return data.length;
+        }
+
+        byte[] getData() {
+            return Arrays.copyOf(data, data.length);
+        }
+
+        void copyDataTo(byte[] dst) {
+            System.arraycopy(data, 0, dst, 0, data.length);
+        }
+
+        int getSignature() {
+            return signature;
+        }
+    }
+
+    private static class TagCache  {
+        private long profileID;
+        private HashMap<Integer, TagData> tags;
+
+        TagCache(long id) {
+            profileID = id;
+
+            tags = new HashMap<>();
+        }
+
+        TagData getTag(int sig) {
+            TagData t = tags.get(sig);
+            if (t == null) {
+                byte[] tagData = getTagNative(profileID, sig);
+                if (tagData != null) {
+                    t = new TagData(sig, tagData);
+                    tags.put(sig, t);
+                }
+            }
+            return t;
+        }
+
+        void clear() {
+            tags.clear();
+        }
+    }
+
+    private static HashMap<Long, TagCache> profiles;
 }
--- a/src/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java	Tue Mar 05 17:18:55 2013 +0400
+++ b/src/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java	Thu Mar 07 14:05:21 2013 +0400
@@ -22,26 +22,19 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package sun.java2d.cmm.lcms;
 
-import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.ComponentColorModel;
-import java.awt.image.Raster;
-import java.awt.image.WritableRaster;
-import java.awt.image.SinglePixelPackedSampleModel;
 import java.awt.image.ComponentSampleModel;
 import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferByte;
-import java.awt.image.DataBufferUShort;
-import java.awt.image.DataBufferInt;
 import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
 import sun.awt.image.ByteComponentRaster;
 import sun.awt.image.ShortComponentRaster;
 import sun.awt.image.IntegerComponentRaster;
 
-
 class LCMSImageLayout {
 
     public static int BYTES_SH(int x) {
@@ -49,47 +42,34 @@
     }
 
     public static int EXTRA_SH(int x) {
-        return x<<7;
+        return x << 7;
     }
 
     public static int CHANNELS_SH(int x) {
-        return x<<3;
+        return x << 3;
     }
-
-    public static final int SWAPFIRST   = 1<<14;
-
-    public static final int DOSWAP      = 1<<10;
-
+    public static final int SWAPFIRST = 1 << 14;
+    public static final int DOSWAP = 1 << 10;
     public static final int PT_RGB_8 =
-        CHANNELS_SH(3) | BYTES_SH(1);
-
+            CHANNELS_SH(3) | BYTES_SH(1);
     public static final int PT_GRAY_8 =
-        CHANNELS_SH(1) | BYTES_SH(1);
-
+            CHANNELS_SH(1) | BYTES_SH(1);
     public static final int PT_GRAY_16 =
-        CHANNELS_SH(1) | BYTES_SH(2);
-
+            CHANNELS_SH(1) | BYTES_SH(2);
     public static final int PT_RGBA_8 =
-        EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1);
-
+            EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1);
     public static final int PT_ARGB_8 =
-        EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1) | SWAPFIRST;
-
+            EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1) | SWAPFIRST;
     public static final int PT_BGR_8 =
-        DOSWAP | CHANNELS_SH(3) | BYTES_SH(1);
-
+            DOSWAP | CHANNELS_SH(3) | BYTES_SH(1);
     public static final int PT_ABGR_8 =
-        DOSWAP | EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1);
-
-    public static final int PT_BGRA_8 = EXTRA_SH(1) | CHANNELS_SH(3) |
-        BYTES_SH(1) | DOSWAP | SWAPFIRST;
-
-    public static final int DT_BYTE     = 0;
-    public static final int DT_SHORT    = 1;
-    public static final int DT_INT      = 2;
-    public static final int DT_DOUBLE   = 3;
-
-
+            DOSWAP | EXTRA_SH(1) | CHANNELS_SH(3) | BYTES_SH(1);
+    public static final int PT_BGRA_8 = EXTRA_SH(1) | CHANNELS_SH(3)
+            | BYTES_SH(1) | DOSWAP | SWAPFIRST;
+    public static final int DT_BYTE = 0;
+    public static final int DT_SHORT = 1;
+    public static final int DT_INT = 2;
+    public static final int DT_DOUBLE = 3;
     boolean isIntPacked = false;
     int pixelType;
     int dataType;
@@ -98,25 +78,30 @@
     int nextRowOffset;
     int offset;
 
+    /* This flag indicates whether the image can be processed
+     * at once by doTransfrom() native call. Otherwise, the
+     * image is processed scan by scan.
+     */
+    private boolean imageAtOnce = false;
     Object dataArray;
+
     private LCMSImageLayout(int np, int pixelType, int pixelSize) {
         this.pixelType = pixelType;
         width = np;
         height = 1;
-        nextRowOffset = np*pixelSize;
+        nextRowOffset = np * pixelSize;
         offset = 0;
     }
 
     private LCMSImageLayout(int width, int height, int pixelType,
-                            int pixelSize) {
+            int pixelSize) {
         this.pixelType = pixelType;
         this.width = width;
         this.height = height;
-        nextRowOffset = width*pixelSize;
+        nextRowOffset = width * pixelSize;
         offset = 0;
     }
 
-
     public LCMSImageLayout(byte[] data, int np, int pixelType, int pixelSize) {
         this(np, pixelType, pixelSize);
         dataType = DT_BYTE;
@@ -135,102 +120,218 @@
         dataArray = data;
     }
 
-    public LCMSImageLayout(double[] data, int np, int pixelType, int pixelSize)
-    {
+    public LCMSImageLayout(double[] data, int np, int pixelType, int pixelSize) {
         this(np, pixelType, pixelSize);
         dataType = DT_DOUBLE;
         dataArray = data;
     }
 
-    public LCMSImageLayout(BufferedImage image) {
-        ShortComponentRaster shortRaster;
-        IntegerComponentRaster intRaster;
-        ByteComponentRaster byteRaster;
+    private LCMSImageLayout() {
+    }
+
+    /* This method creates a layout object for given image.
+     * Returns null if the image is not supported by current implementation.
+     */
+    public static LCMSImageLayout createImageLayout(BufferedImage image) {
+        LCMSImageLayout l = new LCMSImageLayout();
+
         switch (image.getType()) {
             case BufferedImage.TYPE_INT_RGB:
-                pixelType = PT_ARGB_8;
-                isIntPacked = true;
+                l.pixelType = PT_ARGB_8;
+                l.isIntPacked = true;
                 break;
             case BufferedImage.TYPE_INT_ARGB:
-                pixelType = PT_ARGB_8;
-                isIntPacked = true;
+                l.pixelType = PT_ARGB_8;
+                l.isIntPacked = true;
                 break;
             case BufferedImage.TYPE_INT_BGR:
-                pixelType = PT_ABGR_8;
-                isIntPacked = true;
+                l.pixelType = PT_ABGR_8;
+                l.isIntPacked = true;
                 break;
             case BufferedImage.TYPE_3BYTE_BGR:
-                pixelType = PT_BGR_8;
+                l.pixelType = PT_BGR_8;
                 break;
             case BufferedImage.TYPE_4BYTE_ABGR:
-                pixelType = PT_ABGR_8;
+                l.pixelType = PT_ABGR_8;
                 break;
             case BufferedImage.TYPE_BYTE_GRAY:
-                pixelType = PT_GRAY_8;
+                l.pixelType = PT_GRAY_8;
                 break;
             case BufferedImage.TYPE_USHORT_GRAY:
-                pixelType = PT_GRAY_16;
+                l.pixelType = PT_GRAY_16;
                 break;
             default:
-            // TODO: Add support for some images having
-            // SinglePixelPackedModel and ComponentSampleModel
-                throw new IllegalArgumentException(
-                    "CMMImageLayout - bad image type passed to constructor");
+                /* ColorConvertOp creates component images as
+                 * default destination, so this kind of images
+                 * has to be supported.
+                 */
+                ColorModel cm = image.getColorModel();
+                if (cm instanceof ComponentColorModel) {
+                    ComponentColorModel ccm = (ComponentColorModel) cm;
+
+                    // verify whether the component size is fine
+                    int[] cs = ccm.getComponentSize();
+                    for (int s : cs) {
+                        if (s != 8) {
+                            return null;
+                        }
+                    }
+
+                    return createImageLayout(image.getRaster());
+
+                }
+                return null;
         }
 
-        width = image.getWidth();
-        height = image.getHeight();
+        l.width = image.getWidth();
+        l.height = image.getHeight();
 
         switch (image.getType()) {
             case BufferedImage.TYPE_INT_RGB:
             case BufferedImage.TYPE_INT_ARGB:
             case BufferedImage.TYPE_INT_BGR:
-                intRaster = (IntegerComponentRaster)image.getRaster();
-                nextRowOffset = intRaster.getScanlineStride()*4;
-                offset = intRaster.getDataOffset(0)*4;
-                dataArray = intRaster.getDataStorage();
-                dataType = DT_INT;
+                do {
+                    IntegerComponentRaster intRaster = (IntegerComponentRaster)
+                            image.getRaster();
+                    l.nextRowOffset = intRaster.getScanlineStride() * 4;
+                    l.offset = intRaster.getDataOffset(0) * 4;
+                    l.dataArray = intRaster.getDataStorage();
+                    l.dataType = DT_INT;
+
+                    if (l.nextRowOffset == l.width * 4 * intRaster.getPixelStride()) {
+                        l.imageAtOnce = true;
+                    }
+                } while (false);
                 break;
 
             case BufferedImage.TYPE_3BYTE_BGR:
             case BufferedImage.TYPE_4BYTE_ABGR:
-                byteRaster = (ByteComponentRaster)image.getRaster();
-                nextRowOffset = byteRaster.getScanlineStride();
-                int firstBand = image.getSampleModel().getNumBands() - 1;
-                offset = byteRaster.getDataOffset(firstBand);
-                dataArray = byteRaster.getDataStorage();
-                dataType = DT_BYTE;
+                do {
+                    ByteComponentRaster byteRaster = (ByteComponentRaster)
+                            image.getRaster();
+                    l.nextRowOffset = byteRaster.getScanlineStride();
+                    int firstBand = image.getSampleModel().getNumBands() - 1;
+                    l.offset = byteRaster.getDataOffset(firstBand);
+                    l.dataArray = byteRaster.getDataStorage();
+                    l.dataType = DT_BYTE;
+                    if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) {
+                        l.imageAtOnce = true;
+                    }
+                } while (false);
                 break;
 
             case BufferedImage.TYPE_BYTE_GRAY:
-                byteRaster = (ByteComponentRaster)image.getRaster();
-                nextRowOffset = byteRaster.getScanlineStride();
-                offset = byteRaster.getDataOffset(0);
-                dataArray = byteRaster.getDataStorage();
-                dataType = DT_BYTE;
+                do {
+                    ByteComponentRaster byteRaster = (ByteComponentRaster)
+                            image.getRaster();
+                    l.nextRowOffset = byteRaster.getScanlineStride();
+                    l.offset = byteRaster.getDataOffset(0);
+                    l.dataArray = byteRaster.getDataStorage();
+                    l.dataType = DT_BYTE;
+
+                    if (l.nextRowOffset == l.width * byteRaster.getPixelStride()) {
+                        l.imageAtOnce = true;
+                    }
+                } while (false);
                 break;
 
             case BufferedImage.TYPE_USHORT_GRAY:
-                shortRaster = (ShortComponentRaster)image.getRaster();
-                nextRowOffset = shortRaster.getScanlineStride()*2;
-                offset = shortRaster.getDataOffset(0) * 2;
-                dataArray = shortRaster.getDataStorage();
-                dataType = DT_SHORT;
+                do {
+                    ShortComponentRaster shortRaster = (ShortComponentRaster)
+                            image.getRaster();
+                    l.nextRowOffset = shortRaster.getScanlineStride() * 2;
+                    l.offset = shortRaster.getDataOffset(0) * 2;
+                    l.dataArray = shortRaster.getDataStorage();
+                    l.dataType = DT_SHORT;
+
+                    if (l.nextRowOffset == l.width * 2 * shortRaster.getPixelStride()) {
+                        l.imageAtOnce = true;
+                    }
+                } while (false);
                 break;
+            default:
+                return null;
+        }
+        return l;
+    }
+
+    private static enum BandOrder {
+        DIRECT,
+        INVERTED,
+        ARBITRARY,
+        UNKNOWN;
+
+        public static BandOrder getBandOrder(int[] bandOffsets) {
+            BandOrder order = UNKNOWN;
+
+            int numBands = bandOffsets.length;
+
+            for (int i = 0; (order != ARBITRARY) && (i < bandOffsets.length); i++) {
+                switch (order) {
+                    case UNKNOWN:
+                        if (bandOffsets[i] == i) {
+                            order = DIRECT;
+                        } else if (bandOffsets[i] == (numBands - 1 - i)) {
+                            order = INVERTED;
+                        } else {
+                            order = ARBITRARY;
+                        }
+                        break;
+                    case DIRECT:
+                        if (bandOffsets[i] != i) {
+                            order = ARBITRARY;
+                        }
+                        break;
+                    case INVERTED:
+                        if (bandOffsets[i] != (numBands - 1 - i)) {
+                            order = ARBITRARY;
+                        }
+                        break;
+                }
+            }
+            return order;
         }
     }
 
-    public static boolean isSupported(BufferedImage image) {
-        switch (image.getType()) {
-            case BufferedImage.TYPE_INT_RGB:
-            case BufferedImage.TYPE_INT_ARGB:
-            case BufferedImage.TYPE_INT_BGR:
-            case BufferedImage.TYPE_3BYTE_BGR:
-            case BufferedImage.TYPE_4BYTE_ABGR:
-            case BufferedImage.TYPE_BYTE_GRAY:
-            case BufferedImage.TYPE_USHORT_GRAY:
-                return true;
+    public static LCMSImageLayout createImageLayout(Raster r) {
+        LCMSImageLayout l = new LCMSImageLayout();
+        if (r instanceof ByteComponentRaster) {
+            ByteComponentRaster br = (ByteComponentRaster)r;
+
+            ComponentSampleModel csm = (ComponentSampleModel)r.getSampleModel();
+
+            l.pixelType = CHANNELS_SH(br.getNumBands()) | BYTES_SH(1);
+
+            int[] bandOffsets = csm.getBandOffsets();
+            BandOrder order = BandOrder.getBandOrder(bandOffsets);
+
+            int firstBand = 0;
+            switch (order) {
+                case INVERTED:
+                    l.pixelType |= DOSWAP;
+                    firstBand  = csm.getNumBands() - 1;
+                    break;
+                case DIRECT:
+                    // do nothing
+                    break;
+                default:
+                    // unable to create the image layout;
+                    return null;
+            }
+
+            l.nextRowOffset = br.getScanlineStride();
+            l.offset = br.getDataOffset(firstBand);
+            l.dataArray = br.getDataStorage();
+            l.dataType = DT_BYTE;
+
+            l.width = br.getWidth();
+            l.height = br.getHeight();
+
+            if (l.nextRowOffset == l.width * br.getPixelStride()) {
+                l.imageAtOnce = true;
+            }
+            return l;
         }
-        return false;
+        return null;
     }
 }
--- a/src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java	Tue Mar 05 17:18:55 2013 +0400
+++ b/src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java	Thu Mar 07 14:05:21 2013 +0400
@@ -161,13 +161,18 @@
     }
 
     public void colorConvert(BufferedImage src, BufferedImage dst) {
-        if (LCMSImageLayout.isSupported(src) &&
-            LCMSImageLayout.isSupported(dst))
-        {
-            doTransform(new LCMSImageLayout(src), new LCMSImageLayout(dst));
-            return;
+        LCMSImageLayout srcIL, dstIL;
+
+        dstIL = LCMSImageLayout.createImageLayout(dst);
+
+        if (dstIL != null) {
+            srcIL = LCMSImageLayout.createImageLayout(src);
+            if (srcIL != null) {
+                doTransform(srcIL, dstIL);
+                return;
+            }
         }
-        LCMSImageLayout srcIL, dstIL;
+
         Raster srcRas = src.getRaster();
         WritableRaster dstRas = dst.getRaster();
         ColorModel srcCM = src.getColorModel();
@@ -439,6 +444,14 @@
     public void colorConvert(Raster src, WritableRaster dst) {
 
         LCMSImageLayout srcIL, dstIL;
+        dstIL = LCMSImageLayout.createImageLayout(dst);
+        if (dstIL != null) {
+            srcIL = LCMSImageLayout.createImageLayout(src);
+            if (srcIL != null) {
+                doTransform(srcIL, dstIL);
+                return;
+            }
+        }
         // Can't pass src and dst directly to CMM, so process per scanline
         SampleModel srcSM = src.getSampleModel();
         SampleModel dstSM = dst.getSampleModel();
--- a/src/share/demo/java2d/J2DBench/build.xml	Tue Mar 05 17:18:55 2013 +0400
+++ b/src/share/demo/java2d/J2DBench/build.xml	Thu Mar 07 14:05:21 2013 +0400
@@ -52,7 +52,7 @@
     <javac debug="flase" source="1.5" target="1.5" srcdir="${src}" destdir="${build}"/>
   </target>
 
-  <target name="run" depends="dist" 
+  <target name="run" depends="dist"
     description="run J2DBench" >
     <java jar="${dist}/J2DBench.jar"
        fork="true"
@@ -60,7 +60,7 @@
     </java>
   </target>
 
-  <target name="analyze" depends="dist" 
+  <target name="analyze" depends="dist"
     description="run J2DAnalyzer" >
     <java jar="${dist}/J2DAnalyzer.jar"
        fork="true"
@@ -80,6 +80,10 @@
     <copy todir="${build}/j2dbench/tests/iio/images">
       <fileset dir="${resources}/images" />
     </copy>
+    <mkdir dir="${build}/j2dbench/tests/cmm/images"/>
+    <copy todir="${build}/j2dbench/tests/cmm/images">
+      <fileset dir="${resources}/cmm_images" />
+    </copy>
   </target>
 
   <target name="dist" depends="compile, resources"
@@ -88,14 +92,14 @@
     <mkdir dir="${dist}"/>
 
     <!-- Put everything in ${build} into the J2DBench.jar file -->
-    <jar jarfile="${dist}/J2DBench.jar" basedir="${build}" 
+    <jar jarfile="${dist}/J2DBench.jar" basedir="${build}"
         excludes="j2dbench/report/**" >
       <manifest>
         <attribute name="Built-By" value="${user.name}"/>
 	<attribute name="Main-Class" value="j2dbench.J2DBench"/>
       </manifest>
     </jar>
-    <jar jarfile="${dist}/J2DAnalyzer.jar" basedir="${build}" 
+    <jar jarfile="${dist}/J2DAnalyzer.jar" basedir="${build}"
         includes="j2dbench/report/**" >
       <manifest>
         <attribute name="Built-By" value="${user.name}"/>
Binary file src/share/demo/java2d/J2DBench/resources/cmm_images/img_icc_large.jpg has changed
Binary file src/share/demo/java2d/J2DBench/resources/cmm_images/img_icc_medium.jpg has changed
Binary file src/share/demo/java2d/J2DBench/resources/cmm_images/img_icc_small.jpg has changed
--- a/src/share/demo/java2d/J2DBench/src/j2dbench/tests/cmm/ColorConversionTests.java	Tue Mar 05 17:18:55 2013 +0400
+++ b/src/share/demo/java2d/J2DBench/src/j2dbench/tests/cmm/ColorConversionTests.java	Thu Mar 07 14:05:21 2013 +0400
@@ -51,6 +51,7 @@
 
         DataConversionTests.init();
         ColorConvertOpTests.init();
+        EmbeddedProfileTests.init();
     }
 
     protected ColorConversionTests(Group parent, String nodeName, String description) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/demo/java2d/J2DBench/src/j2dbench/tests/cmm/EmbeddedProfileTests.java	Thu Mar 07 14:05:21 2013 +0400
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ *   - Neither the name of Oracle nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This source code is provided to illustrate the usage of a given feature
+ * or technique and has been deliberately simplified. Additional steps
+ * required for a production-quality application, such as security checks,
+ * input validation and proper error handling, might not be present in
+ * this sample code.
+ */
+
+package j2dbench.tests.cmm;
+
+import j2dbench.Group;
+import j2dbench.Option;
+import j2dbench.Result;
+import j2dbench.TestEnvironment;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.URL;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+
+/* This benchmark verifies how changes in cmm library affects image decoding */
+public class EmbeddedProfileTests extends ColorConversionTests {
+
+    protected static Group grpRoot;
+    protected static Group grpOptionsRoot;
+
+    protected static Option inputImages;
+
+    public static void init() {
+        grpRoot = new Group(colorConvRoot, "embed", "Embedded Profile Tests");
+
+        grpOptionsRoot = new Group(grpRoot, "embedOptions", "Options");
+
+        inputImages = createImageList();
+
+        new ReadImageTest();
+    }
+
+    private static enum IccImageResource {
+        SMALL("images/img_icc_small.jpg", "512x512", "Small: 512x512"),
+        MEDIUM("images/img_icc_medium.jpg", "2048x2048", "Medium: 2048x2048"),
+        LARGE("images/img_icc_large.jpg", "4096x4096", "Large: 4096x4096");
+
+        private IccImageResource(String file, String name, String description) {
+            this.url = CMMTests.class.getResource(file);
+            this.abbrev = name;
+            this.description = description;
+        }
+
+        public final URL url;
+        public final String abbrev;
+        public final String description;
+    }
+
+    private static Option createImageList() {
+        IccImageResource[] images = IccImageResource.values();
+
+        int num = images.length;
+
+        String[] names = new String[num];
+        String[] abbrev = new String[num];
+        String[] descr = new String[num];
+
+        for (int i = 0; i < num; i++) {
+            names[i] = images[i].toString();
+            abbrev[i] = images[i].abbrev;
+            descr[i] = images[i].description;
+        }
+
+         Option list = new Option.ObjectList(grpOptionsRoot,
+                "Images", "Input Images",
+                names, images, abbrev, descr, 1);
+
+         return list;
+    }
+
+    public EmbeddedProfileTests(Group parent, String nodeName, String description) {
+        super(parent, nodeName, description);
+        addDependencies(grpOptionsRoot, true);
+    }
+
+    private static class Context {
+        URL input;
+
+        public Context(TestEnvironment env, Result res) {
+
+            IccImageResource icc_input = (IccImageResource)
+                    env.getModifier(inputImages);
+
+            input = icc_input.url;
+        }
+    }
+
+     public Object initTest(TestEnvironment env, Result res) {
+        return new Context(env, res);
+    }
+
+    public void cleanupTest(TestEnvironment env, Object o) {
+        Context ctx = (Context)o;
+        ctx.input = null;
+    }
+
+    private static class ReadImageTest extends EmbeddedProfileTests {
+        public ReadImageTest() {
+            super(grpRoot, "embd_img_read", "ImageReader.read()");
+        }
+
+        public void runTest(Object octx, int numReps) {
+            final Context ctx = (Context)octx;
+            final URL url = ctx.input;
+            ImageInputStream iis = null;
+            ImageReader reader = null;
+
+            try {
+                iis = ImageIO.createImageInputStream(url.openStream());
+                reader = ImageIO.getImageReaders(iis).next();
+            } catch (IOException e) {
+                throw new RuntimeException("Unable to run the becnhmark", e);
+            }
+
+            do {
+                try {
+                    reader.setInput(iis);
+                    BufferedImage img = reader.read(0);
+                    reader.reset();
+
+                    iis = ImageIO.createImageInputStream(url.openStream());
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            } while (--numReps >= 0);
+        }
+    }
+}
--- a/src/share/native/sun/java2d/cmm/lcms/LCMS.c	Tue Mar 05 17:18:55 2013 +0400
+++ b/src/share/native/sun/java2d/cmm/lcms/LCMS.c	Thu Mar 07 14:05:21 2013 +0400
@@ -117,6 +117,7 @@
 static jfieldID IL_nextRowOffset_fID;
 static jfieldID IL_width_fID;
 static jfieldID IL_height_fID;
+static jfieldID IL_imageAtOnce_fID;
 static jfieldID PF_ID_fID;
 
 JavaVM *javaVM;
@@ -237,7 +238,7 @@
  * Method:    loadProfile
  * Signature: ([B)J
  */
-JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfile
+JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative
   (JNIEnv *env, jobject obj, jbyteArray data)
 {
     jbyte* dataArray;
@@ -284,7 +285,7 @@
  * Method:    freeProfile
  * Signature: (J)V
  */
-JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_freeProfile
+JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_freeProfileNative
   (JNIEnv *env, jobject obj, jlong id)
 {
     storeID_t sProf;
@@ -369,48 +370,22 @@
 static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize);
 static cmsBool _writeCookedTag(cmsHPROFILE pfTarget, cmsTagSignature sig, jbyte *pData, jint size);
 
-/*
- * Class:     sun_java2d_cmm_lcms_LCMS
- * Method:    getTagSize
- * Signature: (JI)I
- */
-JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagSize
-  (JNIEnv *env, jobject obj, jlong id, jint tagSig)
-{
-    storeID_t sProf;
-    TagSignature_t sig;
-    jint result = -1;
-
-    sProf.j = id;
-    sig.j = tagSig;
-
-    if (tagSig == SigHead) {
-        result = sizeof(cmsICCHeader);
-    } else {
-      if (cmsIsTag(sProf.pf, sig.cms)) {
-          result = cmsReadRawTag(sProf.pf, sig.cms, NULL, 0);
-        } else {
-            JNU_ThrowByName(env, "java/awt/color/CMMException",
-                            "ICC profile tag not found");
-        }
-    }
-
-    return result;
-}
 
 /*
  * Class:     sun_java2d_cmm_lcms_LCMS
  * Method:    getTagData
  * Signature: (JI[B)V
  */
-JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagData
-  (JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
+JNIEXPORT jbyteArray JNICALL Java_sun_java2d_cmm_lcms_LCMS_getTagNative
+  (JNIEnv *env, jobject obj, jlong id, jint tagSig)
 {
     storeID_t sProf;
     TagSignature_t sig;
     cmsInt32Number tagSize;
 
-    jbyte* dataArray;
+    jbyte* dataArray = NULL;
+    jbyteArray data = NULL;
+
     jint bufSize;
 
     sProf.j = id;
@@ -419,12 +394,14 @@
     if (tagSig == SigHead) {
         cmsBool status;
 
-        bufSize =(*env)->GetArrayLength(env, data);
+        // allocate java array
+        bufSize = sizeof(cmsICCHeader);
+        data = (*env)->NewByteArray(env, bufSize);
 
-        if (bufSize < sizeof(cmsICCHeader)) {
-           JNU_ThrowByName(env, "java/awt/color/CMMException",
-                            "Insufficient buffer capacity");
-           return;
+        if (data == NULL) {
+            JNU_ThrowByName(env, "java/awt/color/CMMException",
+                            "Unable to allocate buffer");
+            return NULL;
         }
 
         dataArray = (*env)->GetByteArrayElements (env, data, 0);
@@ -432,7 +409,7 @@
         if (dataArray == NULL) {
            JNU_ThrowByName(env, "java/awt/color/CMMException",
                             "Unable to get buffer");
-           return;
+           return NULL;
         }
 
         status = _getHeaderInfo(sProf.pf, dataArray, bufSize);
@@ -442,9 +419,10 @@
         if (!status) {
             JNU_ThrowByName(env, "java/awt/color/CMMException",
                             "ICC Profile header not found");
+            return NULL;
         }
 
-        return;
+        return data;
     }
 
     if (cmsIsTag(sProf.pf, sig.cms)) {
@@ -452,16 +430,15 @@
     } else {
         JNU_ThrowByName(env, "java/awt/color/CMMException",
                         "ICC profile tag not found");
-        return;
+        return NULL;
     }
 
-    // verify data buffer capacity
-    bufSize = (*env)->GetArrayLength(env, data);
-
-    if (tagSize < 0 || 0 > bufSize || tagSize > bufSize) {
+    // allocate java array
+    data = (*env)->NewByteArray(env, tagSize);
+    if (data == NULL) {
         JNU_ThrowByName(env, "java/awt/color/CMMException",
-                        "Insufficient buffer capacity.");
-        return;
+                        "Unable to allocate buffer");
+        return NULL;
     }
 
     dataArray = (*env)->GetByteArrayElements (env, data, 0);
@@ -469,7 +446,7 @@
     if (dataArray == NULL) {
         JNU_ThrowByName(env, "java/awt/color/CMMException",
                         "Unable to get buffer");
-        return;
+        return NULL;
     }
 
     bufSize = cmsReadRawTag(sProf.pf, sig.cms, dataArray, tagSize);
@@ -479,8 +456,9 @@
     if (bufSize != tagSize) {
         JNU_ThrowByName(env, "java/awt/color/CMMException",
                         "Can not get tag data.");
+        return NULL;
     }
-    return;
+    return data;
 }
 
 /*
@@ -488,7 +466,7 @@
  * Method:    setTagData
  * Signature: (JI[B)V
  */
-JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagData
+JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagDataNative
   (JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
 {
     storeID_t sProf;
@@ -586,6 +564,7 @@
     char* inputRow;
     char* outputRow;
     jobject srcData, dstData;
+    jboolean srcAtOnce = JNI_FALSE, dstAtOnce = JNI_FALSE;
 
     srcOffset = (*env)->GetIntField (env, src, IL_offset_fID);
     srcNextRowOffset = (*env)->GetIntField (env, src, IL_nextRowOffset_fID);
@@ -594,6 +573,9 @@
     width = (*env)->GetIntField (env, src, IL_width_fID);
     height = (*env)->GetIntField (env, src, IL_height_fID);
 
+    srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID);
+    dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID);
+
     sTrans.j = (*env)->GetLongField (env, trans, Trans_ID_fID);
 
     if (sTrans.xf == NULL) {
@@ -625,10 +607,14 @@
     inputRow = (char*)inputBuffer + srcOffset;
     outputRow = (char*)outputBuffer + dstOffset;
 
-    for (i = 0; i < height; i++) {
-        cmsDoTransform(sTrans.xf, inputRow, outputRow, width);
-        inputRow += srcNextRowOffset;
-        outputRow += dstNextRowOffset;
+    if (srcAtOnce && dstAtOnce) {
+        cmsDoTransform(sTrans.xf, inputRow, outputRow, width * height);
+    } else {
+        for (i = 0; i < height; i++) {
+            cmsDoTransform(sTrans.xf, inputRow, outputRow, width);
+            inputRow += srcNextRowOffset;
+            outputRow += dstNextRowOffset;
+        }
     }
 
     releaseILData(env, inputBuffer, srcDType, srcData);
@@ -670,6 +656,7 @@
     IL_width_fID = (*env)->GetFieldID (env, IL, "width", "I");
     IL_height_fID = (*env)->GetFieldID (env, IL, "height", "I");
     IL_offset_fID = (*env)->GetFieldID (env, IL, "offset", "I");
+    IL_imageAtOnce_fID = (*env)->GetFieldID (env, IL, "imageAtOnce", "Z");
     IL_nextRowOffset_fID = (*env)->GetFieldID (env, IL, "nextRowOffset", "I");
 
     PF_ID_fID = (*env)->GetFieldID (env, Pf, "ID", "J");