changeset 1000:467e4f25965c

6733501: Apply IcedTea little cms patches Reviewed-by: bae, prr
author avu
date Fri, 20 Mar 2009 20:05:22 +0300
parents 9318628e8eee
children e43ea83ca696
files src/share/native/sun/java2d/cmm/lcms/LCMS.c src/share/native/sun/java2d/cmm/lcms/cmsio0.c src/share/native/sun/java2d/cmm/lcms/lcms.h test/sun/java2d/cmm/ProfileOp/ReadWriteProfileTest.java
diffstat 4 files changed, 374 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/native/sun/java2d/cmm/lcms/LCMS.c	Mon Mar 16 11:46:26 2009 -0700
+++ b/src/share/native/sun/java2d/cmm/lcms/LCMS.c	Fri Mar 20 20:05:22 2009 +0300
@@ -30,6 +30,41 @@
 #include "Disposer.h"
 #include "lcms.h"
 
+
+#define ALIGNLONG(x) (((x)+3) & ~(3))         // Aligns to DWORD boundary
+
+#ifdef USE_BIG_ENDIAN
+#define AdjustEndianess32(a)
+#else
+
+static
+void AdjustEndianess32(LPBYTE pByte)
+{
+    BYTE temp1;
+    BYTE temp2;
+
+    temp1 = *pByte++;
+    temp2 = *pByte++;
+    *(pByte-1) = *pByte;
+    *pByte++ = temp2;
+    *(pByte-3) = *pByte;
+    *pByte = temp1;
+}
+
+#endif
+
+// Transports to properly encoded values - note that icc profiles does use
+// big endian notation.
+
+static
+icInt32Number TransportValue32(icInt32Number Value)
+{
+    icInt32Number Temp = Value;
+
+    AdjustEndianess32((LPBYTE) &Temp);
+    return Temp;
+}
+
 #define SigMake(a,b,c,d) \
                     ( ( ((int) ((unsigned char) (a))) << 24) | \
                       ( ((int) ((unsigned char) (b))) << 16) | \
@@ -182,6 +217,8 @@
 
     sProf.pf = cmsOpenProfileFromMem((LPVOID)dataArray, (DWORD) dataSize);
 
+    (*env)->ReleaseByteArrayElements (env, data, dataArray, 0);
+
     if (sProf.pf == NULL) {
         JNU_ThrowIllegalArgumentException(env, "Invalid profile data");
     }
@@ -345,7 +382,23 @@
 JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_setTagData
   (JNIEnv *env, jobject obj, jlong id, jint tagSig, jbyteArray data)
 {
-    fprintf(stderr, "setTagData operation is not implemented");
+    cmsHPROFILE profile;
+    storeID_t sProf;
+    jbyte* dataArray;
+    int tagSize;
+
+    if (tagSig == SigHead) {
+        J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_setTagData on icSigHead not "
+                      "permitted");
+        return;
+    }
+
+    sProf.j = id;
+    profile = (cmsHPROFILE) sProf.pf;
+    dataArray = (*env)->GetByteArrayElements(env, data, 0);
+    tagSize =(*env)->GetArrayLength(env, data);
+    _cmsModifyTagData(profile, (icTagSignature) tagSig, dataArray, tagSize);
+    (*env)->ReleaseByteArrayElements(env, data, dataArray, 0);
 }
 
 void* getILData (JNIEnv *env, jobject img, jint* pDataType,
@@ -507,3 +560,174 @@
 
     PF_ID_fID = (*env)->GetFieldID (env, Pf, "ID", "J");
 }
+
+BOOL _cmsModifyTagData(cmsHPROFILE hProfile, icTagSignature sig,
+                       void *data, size_t size)
+{
+    BOOL isNew;
+    int i, idx, delta, count;
+    LPBYTE padChars[3] = {0, 0, 0};
+    LPBYTE beforeBuf, afterBuf, ptr;
+    size_t beforeSize, afterSize;
+    icUInt32Number profileSize, temp;
+    LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+    isNew = FALSE;
+    idx = _cmsSearchTag(Icc, sig, FALSE);
+    if (idx < 0) {
+        isNew = TRUE;
+        idx = Icc->TagCount++;
+        if (Icc->TagCount >= MAX_TABLE_TAG) {
+            J2dRlsTraceLn1(J2D_TRACE_ERROR, "_cmsModifyTagData: Too many tags "
+                           "(%d)\n", Icc->TagCount);
+            Icc->TagCount = MAX_TABLE_TAG-1;
+            return FALSE;
+        }
+    }
+
+    /* Read in size from header */
+    Icc->Seek(Icc, 0);
+    Icc->Read(&profileSize, sizeof(icUInt32Number), 1, Icc);
+    AdjustEndianess32((LPBYTE) &profileSize);
+
+    /* Compute the change in profile size */
+    if (isNew) {
+        delta = sizeof(icTag) + ALIGNLONG(size);
+    } else {
+        delta = ALIGNLONG(size) - ALIGNLONG(Icc->TagSizes[idx]);
+    }
+    /* Add tag to internal structures */
+    ptr = malloc(size);
+    if (ptr == NULL) {
+        if(isNew) {
+            Icc->TagCount--;
+        }
+        J2dRlsTraceLn(J2D_TRACE_ERROR, "_cmsModifyTagData: ptr == NULL");
+        return FALSE;
+    }
+
+    if (!Icc->Grow(Icc, delta)) {
+        free(ptr);
+        if(isNew) {
+            Icc->TagCount--;
+        }
+        J2dRlsTraceLn(J2D_TRACE_ERROR,
+                      "_cmsModifyTagData: Icc->Grow() == FALSE");
+        return FALSE;
+    }
+
+    /* Compute size of tag data before/after the modified tag */
+    beforeSize = ((isNew)?profileSize:Icc->TagOffsets[idx]) -
+                 Icc->TagOffsets[0];
+    if (Icc->TagCount == (idx + 1)) {
+        afterSize = 0;
+    } else {
+        afterSize = profileSize - Icc->TagOffsets[idx+1];
+    }
+    /* Make copies of the data before/after the modified tag */
+    if (beforeSize > 0) {
+        beforeBuf = malloc(beforeSize);
+        if (!beforeBuf) {
+            if(isNew) {
+                Icc->TagCount--;
+            }
+            free(ptr);
+            J2dRlsTraceLn(J2D_TRACE_ERROR,
+                          "_cmsModifyTagData: beforeBuf == NULL");
+            return FALSE;
+        }
+        Icc->Seek(Icc, Icc->TagOffsets[0]);
+        Icc->Read(beforeBuf, beforeSize, 1, Icc);
+    }
+
+    if (afterSize > 0) {
+        afterBuf = malloc(afterSize);
+        if (!afterBuf) {
+            free(ptr);
+            if(isNew) {
+                Icc->TagCount--;
+            }
+            if (beforeSize > 0) {
+                free(beforeBuf);
+            }
+            J2dRlsTraceLn(J2D_TRACE_ERROR,
+                          "_cmsModifyTagData: afterBuf == NULL");
+            return FALSE;
+        }
+        Icc->Seek(Icc, Icc->TagOffsets[idx+1]);
+        Icc->Read(afterBuf, afterSize, 1, Icc);
+    }
+
+    CopyMemory(ptr, data, size);
+    Icc->TagSizes[idx] = size;
+    Icc->TagNames[idx] = sig;
+    if (Icc->TagPtrs[idx]) {
+        free(Icc->TagPtrs[idx]);
+    }
+    Icc->TagPtrs[idx] = ptr;
+    if (isNew) {
+        Icc->TagOffsets[idx] = profileSize;
+    }
+
+
+    /* Update the profile size in the header */
+    profileSize += delta;
+    Icc->Seek(Icc, 0);
+    temp = TransportValue32(profileSize);
+    Icc->Write(Icc, sizeof(icUInt32Number), &temp);
+
+
+    /* Adjust tag offsets: if the tag is new, we must account
+       for the new tag table entry; otherwise, only those tags after
+       the modified tag are changed (by delta) */
+    if (isNew) {
+        for (i = 0; i < Icc->TagCount; ++i) {
+            Icc->TagOffsets[i] += sizeof(icTag);
+        }
+    } else {
+        for (i = idx+1; i < Icc->TagCount; ++i) {
+            Icc->TagOffsets[i] += delta;
+        }
+    }
+
+    /* Write out a new tag table */
+    count = 0;
+    for (i = 0; i < Icc->TagCount; ++i) {
+        if (Icc->TagNames[i] != 0) {
+            ++count;
+        }
+    }
+    Icc->Seek(Icc, sizeof(icHeader));
+    temp = TransportValue32(count);
+    Icc->Write(Icc, sizeof(icUInt32Number), &temp);
+
+    for (i = 0; i < Icc->TagCount; ++i) {
+        if (Icc->TagNames[i] != 0) {
+            icTag tag;
+            tag.sig = TransportValue32(Icc->TagNames[i]);
+            tag.offset = TransportValue32((icInt32Number) Icc->TagOffsets[i]);
+            tag.size = TransportValue32((icInt32Number) Icc->TagSizes[i]);
+            Icc->Write(Icc, sizeof(icTag), &tag);
+        }
+    }
+
+    /* Write unchanged data before the modified tag */
+    if (beforeSize > 0) {
+        Icc->Write(Icc, beforeSize, beforeBuf);
+        free(beforeBuf);
+    }
+
+    /* Write modified tag data */
+    Icc->Write(Icc, size, data);
+    if (size % 4) {
+        Icc->Write(Icc, 4 - (size % 4), padChars);
+    }
+
+    /* Write unchanged data after the modified tag */
+    if (afterSize > 0) {
+        Icc->Write(Icc, afterSize, afterBuf);
+        free(afterBuf);
+    }
+
+    return TRUE;
+}
--- a/src/share/native/sun/java2d/cmm/lcms/cmsio0.c	Mon Mar 16 11:46:26 2009 -0700
+++ b/src/share/native/sun/java2d/cmm/lcms/cmsio0.c	Fri Mar 20 20:05:22 2009 +0300
@@ -157,8 +157,9 @@
        if (size == 0) return TRUE;
 
        if (ResData != NULL)
-           CopyMemory(ResData ->Block + Icc ->UsedSpace, Ptr, size);
+           CopyMemory(ResData ->Block + ResData ->Pointer, Ptr, size);
 
+       ResData->Pointer += size;
        Icc->UsedSpace += size;
 
        return TRUE;
@@ -166,6 +167,22 @@
 
 
 static
+BOOL MemoryGrow(struct _lcms_iccprofile_struct* Icc, size_t size)
+{
+    FILEMEM* ResData = (FILEMEM*) Icc->stream;
+
+    void* newBlock = realloc(ResData->Block, ResData->Size + size);
+
+    if (!newBlock) {
+        return FALSE;
+    }
+    ResData->Block = newBlock;
+    ResData->Size += size;
+    return TRUE;
+}
+
+
+static
 BOOL MemoryClose(struct _lcms_iccprofile_struct* Icc)
 {
     FILEMEM* ResData = (FILEMEM*) Icc ->stream;
@@ -239,6 +256,13 @@
 
 
 static
+BOOL FileGrow(struct _lcms_iccprofile_struct* Icc, size_t size)
+{
+  return TRUE;
+}
+
+
+static
 BOOL FileClose(struct _lcms_iccprofile_struct* Icc)
 {
     return fclose((FILE*) Icc ->stream);
@@ -382,6 +406,7 @@
     NewIcc ->Seek  = FileSeek;
     NewIcc ->Tell  = FileTell;
     NewIcc ->Close = FileClose;
+    NewIcc ->Grow  = FileGrow;
     NewIcc ->Write = NULL;
 
     NewIcc ->IsWrite = FALSE;
@@ -419,7 +444,8 @@
     NewIcc ->Seek  = MemorySeek;
     NewIcc ->Tell  = MemoryTell;
     NewIcc ->Close = MemoryClose;
-    NewIcc ->Write = NULL;
+    NewIcc ->Grow  = MemoryGrow;
+    NewIcc ->Write = MemoryWrite;
 
     NewIcc ->IsWrite = FALSE;
 
--- a/src/share/native/sun/java2d/cmm/lcms/lcms.h	Mon Mar 16 11:46:26 2009 -0700
+++ b/src/share/native/sun/java2d/cmm/lcms/lcms.h	Fri Mar 20 20:05:22 2009 +0300
@@ -1838,6 +1838,7 @@
                BOOL   (* Seek)(struct _lcms_iccprofile_struct* Icc, size_t offset);
                BOOL   (* Close)(struct _lcms_iccprofile_struct* Icc);
                size_t (* Tell)(struct _lcms_iccprofile_struct* Icc);
+               BOOL   (* Grow)(struct _lcms_iccprofile_struct* Icc, size_t amount);
 
                // Writting
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/java2d/cmm/ProfileOp/ReadWriteProfileTest.java	Fri Mar 20 20:05:22 2009 +0300
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * @test
+ * @bug 6476665 6523403 6733501
+ * @summary Verifies reading and writing profiles and tags of the standard color
+ * spaces
+ * @run main ReadWriteProfileTest
+ */
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.util.*;
+import java.nio.*;
+import java.util.Hashtable;
+
+public class ReadWriteProfileTest implements Runnable {
+    /* Location of the tag sig counter in 4-byte words */
+    final static int TAG_COUNT_OFFSET = 32;
+
+    /* Location of the tag sig table in 4-byte words */
+    final static int TAG_ELEM_OFFSET = 33;
+
+    static byte[][] profiles;
+    static int [][] tagSigs;
+    static Hashtable<Integer,byte[]> [] tags;
+
+    static int [] cspaces = {ColorSpace.CS_sRGB, ColorSpace.CS_PYCC,
+                             ColorSpace.CS_LINEAR_RGB, ColorSpace.CS_CIEXYZ,
+                             ColorSpace.CS_GRAY};
+
+    static String [] csNames = {"sRGB", "PYCC", "LINEAR_RGB", "CIEXYZ", "GRAY"};
+
+    static void getProfileTags(byte [] data, Hashtable tags) {
+        ByteBuffer byteBuf = ByteBuffer.wrap(data);
+        IntBuffer intBuf = byteBuf.asIntBuffer();
+        int tagCount = intBuf.get(TAG_COUNT_OFFSET);
+        intBuf.position(TAG_ELEM_OFFSET);
+        for (int i = 0; i < tagCount; i++) {
+            int tagSig = intBuf.get();
+            int tagDataOff = intBuf.get();
+            int tagSize = intBuf.get();
+
+            byte [] tagData = new byte[tagSize];
+            byteBuf.position(tagDataOff);
+            byteBuf.get(tagData);
+            tags.put(tagSig, tagData);
+        }
+    }
+
+    static {
+        profiles = new byte[cspaces.length][];
+        tags = new Hashtable[cspaces.length];
+
+        for (int i = 0; i < cspaces.length; i++) {
+            ICC_Profile pf = ICC_Profile.getInstance(cspaces[i]);
+            profiles[i] = pf.getData();
+            tags[i] = new Hashtable();
+            getProfileTags(profiles[i], tags[i]);
+        }
+    }
+
+    public void run() {
+        for (int i = 0; i < cspaces.length; i++) {
+            ICC_Profile pf = ICC_Profile.getInstance(cspaces[i]);
+            byte [] data = pf.getData();
+            pf = ICC_Profile.getInstance(data);
+            if (!Arrays.equals(data, profiles[i])) {
+                System.err.println("Incorrect result of getData() " + "with " +
+                                   csNames[i] + " profile");
+                throw new RuntimeException("Incorrect result of getData()");
+            }
+
+            for (int tagSig : tags[i].keySet()) {
+                byte [] tagData = pf.getData(tagSig);
+                byte [] empty = new byte[tagData.length];
+                pf.setData(tagSig, empty);
+                pf.setData(tagSig, tagData);
+
+                byte [] tagData1 = pf.getData(tagSig);
+
+                if (!Arrays.equals(tagData1, tags[i].get(tagSig)))
+                {
+                    System.err.println("Incorrect result of getData(int) with" +
+                                       " tag " +
+                                       Integer.toHexString(tagSig) +
+                                       " of " + csNames[i] + " profile");
+
+                    throw new RuntimeException("Incorrect result of " +
+                                               "getData(int)");
+                }
+            }
+        }
+    }
+
+    public static void main(String [] args) {
+        ReadWriteProfileTest test = new ReadWriteProfileTest();
+        test.run();
+    }
+}