# HG changeset patch # User prr # Date 1512666710 28800 # Node ID d544bfa4f3d5e91986e54a2733ba3aa7881a7a44 # Parent 52873fd3b5bdf00ab595580d6c7428e66784bdb4 8183032: Upgrade to LittleCMS 2.9 Reviewed-by: serb, psadhukhan, mschoene, rhalade diff -r 52873fd3b5bd -r d544bfa4f3d5 THIRD_PARTY_README --- a/THIRD_PARTY_README Wed Jan 31 22:55:12 2018 -0800 +++ b/THIRD_PARTY_README Thu Dec 07 09:11:50 2017 -0800 @@ -62,10 +62,11 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -%% This notice is provided with respect to littlecms, which may be included with this software: +%% This notice is provided with respect to Little CMS 2.9, which is +included with OpenJDK 6: Little cms -Copyright (C) 1998-2004 Marti Maria +Copyright (C) 1998-2017 Marti Maria Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff -r 52873fd3b5bd -r d544bfa4f3d5 make/sun/cmm/lcms/FILES_c_unix.gmk --- a/make/sun/cmm/lcms/FILES_c_unix.gmk Wed Jan 31 22:55:12 2018 -0800 +++ b/make/sun/cmm/lcms/FILES_c_unix.gmk Thu Dec 07 09:11:50 2017 -0800 @@ -25,24 +25,29 @@ FILES_c = \ cmscam02.c \ - cmscam97.c \ cmscgats.c \ cmscnvrt.c \ cmserr.c \ cmsgamma.c \ cmsgmt.c \ + cmshalf.c \ cmsintrp.c \ cmsio0.c \ cmsio1.c \ cmslut.c \ - cmsmatsh.c \ + cmsmd5.c \ cmsmtrx.c \ cmsnamed.c \ + cmsopt.c \ cmspack.c \ cmspcs.c \ + cmsplugin.c \ cmsps2.c \ cmssamp.c \ + cmssm.c \ + cmstypes.c \ cmsvirt.c \ cmswtpnt.c \ cmsxform.c \ + cmsalpha.c \ LCMS.c diff -r 52873fd3b5bd -r d544bfa4f3d5 make/sun/cmm/lcms/FILES_c_windows.gmk --- a/make/sun/cmm/lcms/FILES_c_windows.gmk Wed Jan 31 22:55:12 2018 -0800 +++ b/make/sun/cmm/lcms/FILES_c_windows.gmk Thu Dec 07 09:11:50 2017 -0800 @@ -25,24 +25,29 @@ FILES_c = \ cmscam02.c \ - cmscam97.c \ cmscgats.c \ cmscnvrt.c \ cmserr.c \ cmsgamma.c \ cmsgmt.c \ + cmshalf.c \ cmsintrp.c \ cmsio0.c \ cmsio1.c \ cmslut.c \ - cmsmatsh.c \ + cmsmd5.c \ cmsmtrx.c \ cmsnamed.c \ + cmsopt.c \ cmspack.c \ cmspcs.c \ + cmsplugin.c \ cmsps2.c \ cmssamp.c \ + cmssm.c \ + cmstypes.c \ cmsvirt.c \ cmswtpnt.c \ cmsxform.c \ + cmsalpha.c \ LCMS.c diff -r 52873fd3b5bd -r d544bfa4f3d5 make/sun/cmm/lcms/mapfile-vers --- a/make/sun/cmm/lcms/mapfile-vers Wed Jan 31 22:55:12 2018 -0800 +++ b/make/sun/cmm/lcms/mapfile-vers Thu Dec 07 09:11:50 2017 -0800 @@ -27,13 +27,11 @@ SUNWprivate_1.1 { global: - Java_sun_java2d_cmm_lcms_LCMS_loadProfile; - Java_sun_java2d_cmm_lcms_LCMS_freeProfile; - 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_loadProfileNative; + Java_sun_java2d_cmm_lcms_LCMS_getProfileSizeNative; + Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative; + 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; diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/java/awt/color/ICC_Profile.java --- a/src/share/classes/java/awt/color/ICC_Profile.java Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/classes/java/awt/color/ICC_Profile.java Thu Dec 07 09:11:50 2017 -0800 @@ -37,6 +37,8 @@ import sun.java2d.cmm.PCMM; import sun.java2d.cmm.CMSManager; +import sun.java2d.cmm.Profile; +import sun.java2d.cmm.ProfileDataVerifier; import sun.java2d.cmm.ProfileDeferralMgr; import sun.java2d.cmm.ProfileDeferralInfo; import sun.java2d.cmm.ProfileActivator; @@ -92,7 +94,7 @@ private static final long serialVersionUID = -3938515861990936766L; - transient long ID; + private transient Profile cmmProfile; private transient ProfileDeferralInfo deferralInfo; private transient ProfileActivator profileActivator; @@ -725,8 +727,8 @@ /** * Constructs an ICC_Profile object with a given ID. */ - ICC_Profile(long ID) { - this.ID = ID; + ICC_Profile(Profile p) { + this.cmmProfile = p; } @@ -749,8 +751,8 @@ * Frees the resources associated with an ICC_Profile object. */ protected void finalize () { - if (ID != 0) { - CMSManager.getModule().freeProfile(ID); + if (cmmProfile != null) { + CMSManager.getModule().freeProfile(cmmProfile); } else if (profileActivator != null) { ProfileDeferralMgr.unregisterDeferral(profileActivator); } @@ -768,39 +770,41 @@ public static ICC_Profile getInstance(byte[] data) { ICC_Profile thisProfile; - long theID; + Profile p = null; if (ProfileDeferralMgr.deferring) { ProfileDeferralMgr.activateProfiles(); } + ProfileDataVerifier.verify(data); + try { - theID = CMSManager.getModule().loadProfile(data); + p = CMSManager.getModule().loadProfile(data); } catch (CMMException c) { throw new IllegalArgumentException("Invalid ICC Profile Data"); } try { - if ((getColorSpaceType (theID) == ColorSpace.TYPE_GRAY) && - (getData (theID, icSigMediaWhitePointTag) != null) && - (getData (theID, icSigGrayTRCTag) != null)) { - thisProfile = new ICC_ProfileGray (theID); + if ((getColorSpaceType (p) == ColorSpace.TYPE_GRAY) && + (getData (p, icSigMediaWhitePointTag) != null) && + (getData (p, icSigGrayTRCTag) != null)) { + thisProfile = new ICC_ProfileGray (p); } - else if ((getColorSpaceType (theID) == ColorSpace.TYPE_RGB) && - (getData (theID, icSigMediaWhitePointTag) != null) && - (getData (theID, icSigRedColorantTag) != null) && - (getData (theID, icSigGreenColorantTag) != null) && - (getData (theID, icSigBlueColorantTag) != null) && - (getData (theID, icSigRedTRCTag) != null) && - (getData (theID, icSigGreenTRCTag) != null) && - (getData (theID, icSigBlueTRCTag) != null)) { - thisProfile = new ICC_ProfileRGB (theID); + else if ((getColorSpaceType (p) == ColorSpace.TYPE_RGB) && + (getData (p, icSigMediaWhitePointTag) != null) && + (getData (p, icSigRedColorantTag) != null) && + (getData (p, icSigGreenColorantTag) != null) && + (getData (p, icSigBlueColorantTag) != null) && + (getData (p, icSigRedTRCTag) != null) && + (getData (p, icSigGreenTRCTag) != null) && + (getData (p, icSigBlueTRCTag) != null)) { + thisProfile = new ICC_ProfileRGB (p); } else { - thisProfile = new ICC_Profile (theID); + thisProfile = new ICC_Profile (p); } } catch (CMMException c) { - thisProfile = new ICC_Profile (theID); + thisProfile = new ICC_Profile (p); } return thisProfile; } @@ -1114,7 +1118,7 @@ fileName); } try { - ID = CMSManager.getModule().loadProfile(profileData); + cmmProfile = CMSManager.getModule().loadProfile(profileData); } catch (CMMException c) { ProfileDataException pde = new ProfileDataException("Invalid ICC Profile Data" + fileName); @@ -1224,14 +1228,14 @@ causing a deferred profile to be loaded */ } - return getColorSpaceType(ID); + return getColorSpaceType(cmmProfile); } - static int getColorSpaceType(long profileID) { + static int getColorSpaceType(Profile p) { byte[] theHeader; int theColorSpaceSig, theColorSpace; - theHeader = getData(profileID, icSigHead); + theHeader = getData(p, icSigHead); theColorSpaceSig = intFromBigEndian(theHeader, icHdrColorSpace); theColorSpace = iccCStoJCS (theColorSpaceSig); return theColorSpace; @@ -1253,15 +1257,15 @@ if (ProfileDeferralMgr.deferring) { ProfileDeferralMgr.activateProfiles(); } - return getPCSType(ID); + return getPCSType(cmmProfile); } - static int getPCSType(long profileID) { + static int getPCSType(Profile p) { byte[] theHeader; int thePCSSig, thePCS; - theHeader = getData(profileID, icSigHead); + theHeader = getData(p, icSigHead); thePCSSig = intFromBigEndian(theHeader, icHdrPcs); thePCS = iccCStoJCS(thePCSSig); return thePCS; @@ -1321,12 +1325,12 @@ PCMM mdl = CMSManager.getModule(); /* get the number of bytes needed for this profile */ - profileSize = mdl.getProfileSize(ID); + profileSize = mdl.getProfileSize(cmmProfile); profileData = new byte [profileSize]; /* get the data for the profile */ - mdl.getProfileData(ID, profileData); + mdl.getProfileData(cmmProfile, profileData); return profileData; } @@ -1353,11 +1357,11 @@ ProfileDeferralMgr.activateProfiles(); } - return getData(ID, tagSignature); + return getData(cmmProfile, tagSignature); } - static byte[] getData(long profileID, int tagSignature) { + static byte[] getData(Profile p, int tagSignature) { int tagSize; byte[] tagData; @@ -1365,12 +1369,12 @@ PCMM mdl = CMSManager.getModule(); /* get the number of bytes needed for this tag */ - tagSize = mdl.getTagSize(profileID, tagSignature); + tagSize = mdl.getTagSize(p, tagSignature); tagData = new byte[tagSize]; /* get an array for the tag */ /* get the tag's data */ - mdl.getTagData(profileID, tagSignature, tagData); + mdl.getTagData(p, tagSignature, tagData); } catch(CMMException c) { tagData = null; } @@ -1395,7 +1399,7 @@ ProfileDeferralMgr.activateProfiles(); } - CMSManager.getModule().setTagData(ID, tagSignature, tagData); + CMSManager.getModule().setTagData(cmmProfile, tagSignature, tagData); } /** diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/java/awt/color/ICC_ProfileGray.java --- a/src/share/classes/java/awt/color/ICC_ProfileGray.java Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/classes/java/awt/color/ICC_ProfileGray.java Thu Dec 07 09:11:50 2017 -0800 @@ -35,7 +35,7 @@ package java.awt.color; -import java.awt.image.LookupTable; +import sun.java2d.cmm.Profile; import sun.java2d.cmm.ProfileDeferralInfo; /** @@ -76,8 +76,8 @@ /** * Constructs a new ICC_ProfileGray from a CMM ID. */ - ICC_ProfileGray(long ID) { - super(ID); + ICC_ProfileGray(Profile p) { + super(p); } /** diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/java/awt/color/ICC_ProfileRGB.java --- a/src/share/classes/java/awt/color/ICC_ProfileRGB.java Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/classes/java/awt/color/ICC_ProfileRGB.java Thu Dec 07 09:11:50 2017 -0800 @@ -35,7 +35,7 @@ package java.awt.color; -import java.awt.image.LookupTable; +import sun.java2d.cmm.Profile; import sun.java2d.cmm.ProfileDeferralInfo; /** @@ -114,8 +114,8 @@ * @param ID The CMM ID for the profile. * */ - ICC_ProfileRGB(long ID) { - super(ID); + ICC_ProfileRGB(Profile p) { + super(p); } /** diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/sun/java2d/cmm/CMSManager.java --- a/src/share/classes/sun/java2d/cmm/CMSManager.java Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/classes/sun/java2d/cmm/CMSManager.java Thu Dec 07 09:11:50 2017 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2010, Oracle and/or its affiliates. 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 @@ -97,53 +97,53 @@ cName = tcmm.getClass().getName(); } - public long loadProfile(byte[] data) { + public Profile loadProfile(byte[] data) { System.err.print(cName + ".loadProfile"); - long profileID = tcmm.loadProfile(data); - System.err.println("(ID=" + profileID + ")"); - return profileID; + Profile p = tcmm.loadProfile(data); + System.err.printf("(ID=%s)\n", p.toString()); + return p; } - public void freeProfile(long profileID) { - System.err.println(cName + ".freeProfile(ID=" + profileID + ")"); - tcmm.freeProfile(profileID); + public void freeProfile(Profile p) { + System.err.printf(cName + ".freeProfile(ID=%s)\n", p.toString()); + tcmm.freeProfile(p); } - public int getProfileSize(long profileID) { - System.err.print(cName + ".getProfileSize(ID=" + profileID + ")"); - int size = tcmm.getProfileSize(profileID); + public int getProfileSize(Profile p) { + System.err.print(cName + ".getProfileSize(ID=" + p + ")"); + int size = tcmm.getProfileSize(p); System.err.println("=" + size); return size; } - public void getProfileData(long profileID, byte[] data) { - System.err.print(cName + ".getProfileData(ID=" + profileID + ") "); + public void getProfileData(Profile p, byte[] data) { + System.err.print(cName + ".getProfileData(ID=" + p + ") "); System.err.println("requested " + data.length + " byte(s)"); - tcmm.getProfileData(profileID, data); + tcmm.getProfileData(p, data); } - public int getTagSize(long profileID, int tagSignature) { - System.err.print(cName + ".getTagSize(ID=" + profileID + - ", TagSig=" + tagSignature + ")"); - int size = tcmm.getTagSize(profileID, tagSignature); + public int getTagSize(Profile p, int tagSignature) { + System.err.printf(cName + ".getTagSize(ID=%x, TagSig=%s)", + p, signatureToString(tagSignature)); + int size = tcmm.getTagSize(p, tagSignature); System.err.println("=" + size); return size; } - public void getTagData(long profileID, int tagSignature, + public void getTagData(Profile p, int tagSignature, byte[] data) { - System.err.print(cName + ".getTagData(ID=" + profileID + - ", TagSig=" + tagSignature + ")"); + System.err.printf(cName + ".getTagData(ID=%x, TagSig=%s)", + p, signatureToString(tagSignature)); System.err.println(" requested " + data.length + " byte(s)"); - tcmm.getTagData(profileID, tagSignature, data); + tcmm.getTagData(p, tagSignature, data); } - public void setTagData(long profileID, int tagSignature, + public void setTagData(Profile p, int tagSignature, byte[] data) { - System.err.print(cName + ".setTagData(ID=" + profileID + + System.err.print(cName + ".setTagData(ID=" + p + ", TagSig=" + tagSignature + ")"); System.err.println(" sending " + data.length + " byte(s)"); - tcmm.setTagData(profileID, tagSignature, data); + tcmm.setTagData(p, tagSignature, data); } /* methods for creating ColorTransforms */ @@ -158,5 +158,13 @@ System.err.println(cName + ".createTransform(ColorTransform[])"); return tcmm.createTransform(transforms); } + + private static String signatureToString(int sig) { + return String.format("%c%c%c%c", + (char)(0xff & (sig >> 24)), + (char)(0xff & (sig >> 16)), + (char)(0xff & (sig >> 8)), + (char)(0xff & (sig ))); + } } } diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/sun/java2d/cmm/PCMM.java --- a/src/share/classes/sun/java2d/cmm/PCMM.java Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/classes/sun/java2d/cmm/PCMM.java Thu Dec 07 09:11:50 2017 -0800 @@ -32,13 +32,13 @@ public interface PCMM { /* methods invoked from ICC_Profile */ - public long loadProfile(byte[] data); - public void freeProfile(long profileID); - public int getProfileSize(long profileID); - public void getProfileData(long profileID, byte[] data); - public void getTagData(long profileID, int tagSignature, byte[] data); - public int getTagSize(long profileID, int tagSignature); - public void setTagData(long profileID, int tagSignature, byte[] data); + public Profile loadProfile(byte[] data); + public void freeProfile(Profile p); + public int getProfileSize(Profile p); + public void getProfileData(Profile p, byte[] data); + public void getTagData(Profile p, int tagSignature, byte[] data); + public int getTagSize(Profile p, int tagSignature); + public void setTagData(Profile p, int tagSignature, byte[] data); /* methods for creating ColorTransforms */ public ColorTransform createTransform(ICC_Profile profile, int renderType, diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/sun/java2d/cmm/Profile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/java2d/cmm/Profile.java Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.java2d.cmm; + +import java.awt.color.CMMException; + +public class Profile { + private final long nativePtr; + + protected Profile(long ptr) { + nativePtr = ptr; + } + + protected final long getNativePtr() { + if (nativePtr == 0L) { + throw new CMMException("Invalid profile: ptr is null"); + } + return nativePtr; + } +} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/sun/java2d/cmm/ProfileDataVerifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/java2d/cmm/ProfileDataVerifier.java Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.java2d.cmm; + +public class ProfileDataVerifier { + /** + * Throws an IllegalArgumentException if the data does not correspond + * to a valid ICC Profile. + * + * @param data the specified profile data. + */ + public static void verify(byte[] data) { + if (data == null) { + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + + if (data.length < TOC_OFFSET) { + // not enough data for profile header + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + + // check profile size + final int size = readInt32(data, 0); + final int tagCount = readInt32(data, HEADER_SIZE); + + if (tagCount < 0 || tagCount > MAX_TAG_COUNT) { + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + + if (size < (TOC_OFFSET + (tagCount * TOC_RECORD_SIZE)) || + size > data.length) + { + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + + final int sig = readInt32(data, 36); + + if (PROFILE_FILE_SIGNATURE != sig) { + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + + // verify table of content + for (int i = 0; i < tagCount; i++) { + final int tag_offset = getTagOffset(i, data); + final int tag_size = getTagSize(i, data); + + if (tag_offset < TOC_OFFSET || tag_offset > size) { + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + + if (tag_size < 0 || + tag_size > (Integer.MAX_VALUE - tag_offset) || + tag_size + tag_offset > size) + { + throw new IllegalArgumentException("Invalid ICC Profile Data"); + } + } + } + + private static int getTagOffset(int idx, byte[] data) { + final int pos = TOC_OFFSET + idx * TOC_RECORD_SIZE + 4; + return readInt32(data, pos); + } + + private static int getTagSize(int idx, byte[] data) { + final int pos = TOC_OFFSET + idx * TOC_RECORD_SIZE + 8; + return readInt32(data, pos); + } + + private static int readInt32(byte[] data, int off) { + int res = 0; + for (int i = 0; i < 4; i++) { + res = res << 8; + + res |= (0xff & data[off++]); + } + return res; + } + + /** + * Lcms limit for the number of tags: 100 + * Kcms limit for the number of tags: N/A + */ + private static final int MAX_TAG_COUNT = 100; + + private static final int HEADER_SIZE = 128; + private static final int TOC_OFFSET = HEADER_SIZE + 4; + private static final int TOC_RECORD_SIZE = 12; + + private static final int PROFILE_FILE_SIGNATURE = 0x61637370; +} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/sun/java2d/cmm/lcms/LCMS.java --- a/src/share/classes/sun/java2d/cmm/lcms/LCMS.java Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/classes/sun/java2d/cmm/lcms/LCMS.java Thu Dec 07 09:11:50 2017 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. 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 @@ -25,35 +25,143 @@ package sun.java2d.cmm.lcms; -import java.awt.color.ColorSpace; +import java.awt.color.CMMException; import java.awt.color.ICC_Profile; -import java.awt.color.CMMException; import sun.java2d.cmm.ColorTransform; import sun.java2d.cmm.PCMM; -import sun.java2d.cmm.lcms.LCMS; -import sun.java2d.cmm.lcms.LCMSTransform; +import sun.java2d.cmm.Profile; +import sun.java2d.cmm.lcms.LCMSProfile.TagData; public class LCMS implements PCMM { /* methods invoked from ICC_Profile */ - public native long loadProfile(byte[] data); + @Override + public Profile loadProfile(byte[] data) { + final Object disposerRef = new Object(); + + final long ptr = loadProfileNative(data, disposerRef); + + if (ptr != 0L) { + return new LCMSProfile(ptr, disposerRef); + } + return null; + } + + private native long loadProfileNative(byte[] data, Object ref); + + private LCMSProfile getLcmsProfile(Profile p) { + if (p instanceof LCMSProfile) { + return (LCMSProfile)p; + } + throw new CMMException("Invalid profile: " + p); + } + - public native void freeProfile(long profileID); + @Override + public void freeProfile(Profile p) { + // we use disposer, so this method does nothing + } + + @Override + public int getProfileSize(final Profile p) { + synchronized (p) { + return getProfileSizeNative(getLcmsProfile(p).getLcmsPtr()); + } + } - public native synchronized int getProfileSize(long profileID); + private native int getProfileSizeNative(long ptr); + + @Override + public void getProfileData(final Profile p, byte[] data) { + synchronized (p) { + getProfileDataNative(getLcmsProfile(p).getLcmsPtr(), data); + } + } + + private native void getProfileDataNative(long ptr, byte[] data); + + @Override + public int getTagSize(Profile p, int tagSignature) { + final LCMSProfile profile = getLcmsProfile(p); - public native synchronized void getProfileData(long profileID, byte[] data); + synchronized (profile) { + TagData t = profile.getTag(tagSignature); + return t == null ? 0 : t.getSize(); + } + } + + static native byte[] getTagNative(long profileID, int signature); + + @Override + public void getTagData(Profile p, int tagSignature, byte[] data) + { + final LCMSProfile profile = getLcmsProfile(p); + + synchronized (profile) { + TagData t = profile.getTag(tagSignature); + if (t != null) { + t.copyDataTo(data); + } + } + } + + @Override + public synchronized void setTagData(Profile p, int tagSignature, byte[] data) { + final LCMSProfile profile = getLcmsProfile(p); - 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, + synchronized (profile) { + profile.clearTagCache(); + + // Now we are going to update the profile with new tag data + // In some cases, we may change the pointer to the native + // profile. + // + // If we fail to write tag data for any reason, the old pointer + // should be used. + setTagDataNative(profile.getLcmsPtr(), + tagSignature, data); + } + } + + /** + * Writes supplied data as a tag into the profile. + * Destroys old profile, if new one was successfully + * created. + * + * Returns valid pointer to new profile. + * + * Throws CMMException if operation fails, preserve old profile from + * destruction. + */ + private native void setTagDataNative(long ptr, int tagSignature, byte[] data); - public static native long getProfileID(ICC_Profile profile); + public synchronized static native LCMSProfile getProfileID(ICC_Profile profile); + + /* Helper method used from LCMSColorTransfrom */ + static long createTransform( + LCMSProfile[] profiles, int renderType, + int inFormatter, boolean isInIntPacked, + int outFormatter, boolean isOutIntPacked, + Object disposerRef) + { + long[] ptrs = new long[profiles.length]; - public static native long createNativeTransform( - long[] profileIDs, int renderType, Object disposerRef); + for (int i = 0; i < profiles.length; i++) { + if (profiles[i] == null) throw new CMMException("Unknown profile ID"); + + ptrs[i] = profiles[i].getLcmsPtr(); + } + + return createNativeTransform(ptrs, renderType, inFormatter, + isInIntPacked, outFormatter, isOutIntPacked, disposerRef); + } + + private static native long createNativeTransform( + long[] profileIDs, int renderType, + int inFormatter, boolean isInIntPacked, + int outFormatter, boolean isOutIntPacked, + Object disposerRef); /** * Constructs ColorTransform object corresponding to an ICC_profile diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java --- a/src/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/classes/sun/java2d/cmm/lcms/LCMSImageLayout.java Thu Dec 07 09:11:50 2017 -0800 @@ -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; @@ -99,7 +79,13 @@ private int nextPixelOffset; int offset; + /* This flag indicates whether the image can be processed + * at once by doTransform() native call. Otherwise, the + * image is processed scan by scan. + */ + private boolean imageAtOnce = false; Object dataArray; + private int dataArrayLength; /* in bytes */ private LCMSImageLayout(int np, int pixelType, int pixelSize) @@ -170,111 +156,183 @@ verify(); } - public LCMSImageLayout(BufferedImage image) throws ImageLayoutException { - 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) throws ImageLayoutException { + 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 = safeMult(4, intRaster.getScanlineStride()); - nextPixelOffset = safeMult(4, intRaster.getPixelStride()); + do { + IntegerComponentRaster intRaster = (IntegerComponentRaster) + image.getRaster(); + l.nextRowOffset = safeMult(4, intRaster.getScanlineStride()); + l.nextPixelOffset = safeMult(4, intRaster.getPixelStride()); + l.offset = safeMult(4, intRaster.getDataOffset(0)); + l.dataArray = intRaster.getDataStorage(); + l.dataArrayLength = 4 * intRaster.getDataStorage().length; + l.dataType = DT_INT; - offset = safeMult(4, intRaster.getDataOffset(0)); - - dataArray = intRaster.getDataStorage(); - dataArrayLength = 4 * intRaster.getDataStorage().length; - dataType = DT_INT; + if (l.nextRowOffset == safeMult(l.width, l.nextPixelOffset)) { + l.imageAtOnce = true; + } + } while (false); break; case BufferedImage.TYPE_3BYTE_BGR: case BufferedImage.TYPE_4BYTE_ABGR: - byteRaster = (ByteComponentRaster)image.getRaster(); - nextRowOffset = byteRaster.getScanlineStride(); - nextPixelOffset = byteRaster.getPixelStride(); + do { + ByteComponentRaster byteRaster = (ByteComponentRaster) + image.getRaster(); + l.nextRowOffset = byteRaster.getScanlineStride(); + l.nextPixelOffset = byteRaster.getPixelStride(); - int firstBand = image.getSampleModel().getNumBands() - 1; - offset = byteRaster.getDataOffset(firstBand); - dataArray = byteRaster.getDataStorage(); - dataArrayLength = byteRaster.getDataStorage().length; - dataType = DT_BYTE; + int firstBand = image.getSampleModel().getNumBands() - 1; + l.offset = byteRaster.getDataOffset(firstBand); + l.dataArray = byteRaster.getDataStorage(); + l.dataArrayLength = byteRaster.getDataStorage().length; + 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(); - nextPixelOffset = byteRaster.getPixelStride(); + do { + ByteComponentRaster byteRaster = (ByteComponentRaster) + image.getRaster(); + l.nextRowOffset = byteRaster.getScanlineStride(); + l.nextPixelOffset = byteRaster.getPixelStride(); - offset = byteRaster.getDataOffset(0); - dataArray = byteRaster.getDataStorage(); - dataArrayLength = byteRaster.getDataStorage().length; - dataType = DT_BYTE; + l.dataArrayLength = byteRaster.getDataStorage().length; + 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 = safeMult(2, shortRaster.getScanlineStride()); - nextPixelOffset = safeMult(2, shortRaster.getPixelStride()); + do { + ShortComponentRaster shortRaster = (ShortComponentRaster) + image.getRaster(); + l.nextRowOffset = safeMult(2, shortRaster.getScanlineStride()); + l.nextPixelOffset = safeMult(2, shortRaster.getPixelStride()); + + l.offset = safeMult(2, shortRaster.getDataOffset(0)); + l.dataArray = shortRaster.getDataStorage(); + l.dataArrayLength = 2 * shortRaster.getDataStorage().length; + l.dataType = DT_SHORT; - offset = safeMult(2, shortRaster.getDataOffset(0)); - dataArray = shortRaster.getDataStorage(); - dataArrayLength = 2 * shortRaster.getDataStorage().length; - dataType = DT_SHORT; + if (l.nextRowOffset == l.width * 2 * shortRaster.getPixelStride()) { + l.imageAtOnce = true; + } + } while (false); break; + default: + return null; } - verify(); + l.verify(); + return l; } - 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; + 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; } - return false; } private void verify() throws ImageLayoutException { @@ -323,6 +381,50 @@ super(message); } } + public static LCMSImageLayout createImageLayout(Raster r) { + LCMSImageLayout l = new LCMSImageLayout(); + if (r instanceof ByteComponentRaster && + r.getSampleModel() instanceof ComponentSampleModel) { + 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.nextPixelOffset = br.getPixelStride(); + + 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 null; + } /** * Derives number of bytes per pixel from the pixel format. diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/sun/java2d/cmm/lcms/LCMSProfile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/java2d/cmm/lcms/LCMSProfile.java Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.java2d.cmm.lcms; + +import java.awt.color.CMMException; +import java.util.Arrays; +import java.util.HashMap; +import sun.java2d.cmm.Profile; + +final class LCMSProfile extends Profile { + private final TagCache tagCache; + + private final Object disposerReferent; + + LCMSProfile(long ptr, Object ref) { + super(ptr); + + disposerReferent = ref; + + tagCache = new TagCache(this); + } + + final long getLcmsPtr() { + return this.getNativePtr(); + } + + TagData getTag(int sig) { + return tagCache.getTag(sig); + } + + void clearTagCache() { + tagCache.clear(); + } + + static class TagCache { + final LCMSProfile profile; + private HashMap tags; + + TagCache(LCMSProfile p) { + profile = p; + tags = new HashMap(); + } + + TagData getTag(int sig) { + TagData t = tags.get(sig); + if (t == null) { + byte[] tagData = LCMS.getTagNative(profile.getNativePtr(), sig); + if (tagData != null) { + t = new TagData(sig, tagData); + tags.put(sig, t); + } + } + return t; + } + + void clear() { + tags.clear(); + } + } + + 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; + } + } +} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java --- a/src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java Thu Dec 07 09:11:50 2017 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. 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 @@ -56,11 +56,19 @@ public class LCMSTransform implements ColorTransform { long ID; + private int inFormatter = 0; + private boolean isInIntPacked = false; + private int outFormatter = 0; + private boolean isOutIntPacked = false; + ICC_Profile[] profiles; - long [] profileIDs; + LCMSProfile[] lcmsProfiles; int renderType; int transformType; + private int numInComponents = -1; + private int numOutComponents = -1; + private Object disposerReferent = new Object(); /* the class initializer */ @@ -76,11 +84,19 @@ /* Actually, it is not a complete transform but just part of it */ profiles = new ICC_Profile[1]; profiles[0] = profile; - profileIDs = new long[1]; - profileIDs[0] = LCMS.getProfileID(profile); + lcmsProfiles = new LCMSProfile[1]; + lcmsProfiles[0] = LCMS.getProfileID(profile); this.renderType = (renderType == ColorTransform.Any)? ICC_Profile.icPerceptual : renderType; this.transformType = transformType; + + /* Note that ICC_Profile.getNumComponents() is quite expensive + * (it may results in a reading of the profile header). + * So, here we cache the number of components of input and + * output profiles for further usage. + */ + numInComponents = profiles[0].getNumComponents(); + numOutComponents = profiles[profiles.length - 1].getNumComponents(); } public LCMSTransform (ColorTransform[] transforms) { @@ -89,44 +105,80 @@ size+=((LCMSTransform)transforms[i]).profiles.length; } profiles = new ICC_Profile[size]; - profileIDs = new long[size]; + lcmsProfiles = new LCMSProfile[size]; int j = 0; for (int i=0; i < transforms.length; i++) { LCMSTransform curTrans = (LCMSTransform)transforms[i]; System.arraycopy(curTrans.profiles, 0, profiles, j, curTrans.profiles.length); - System.arraycopy(curTrans.profileIDs, 0, profileIDs, j, - curTrans.profileIDs.length); + System.arraycopy(curTrans.lcmsProfiles, 0, lcmsProfiles, j, + curTrans.lcmsProfiles.length); j += curTrans.profiles.length; } renderType = ((LCMSTransform)transforms[0]).renderType; - ID = LCMS.createNativeTransform(profileIDs, renderType, - disposerReferent); + + /* Note that ICC_Profile.getNumComponents() is quite expensive + * (it may results in a reading of the profile header). + * So, here we cache the number of components of input and + * output profiles for further usage. + */ + numInComponents = profiles[0].getNumComponents(); + numOutComponents = profiles[profiles.length - 1].getNumComponents(); } public int getNumInComponents() { - return profiles[0].getNumComponents(); + return numInComponents; } public int getNumOutComponents() { - return profiles[profiles.length - 1].getNumComponents(); + return numOutComponents; + } + + private synchronized void doTransform(LCMSImageLayout in, + LCMSImageLayout out) { + // update native transfrom if needed + if (ID == 0L || + inFormatter != in.pixelType || isInIntPacked != in.isIntPacked || + outFormatter != out.pixelType || isOutIntPacked != out.isIntPacked) + { + + if (ID != 0L) { + // Disposer will destroy forgotten transform + disposerReferent = new Object(); + } + inFormatter = in.pixelType; + isInIntPacked = in.isIntPacked; + + outFormatter = out.pixelType; + isOutIntPacked = out.isIntPacked; + + ID = LCMS.createTransform(lcmsProfiles, renderType, + inFormatter, isInIntPacked, + outFormatter, isOutIntPacked, + disposerReferent); + } + + LCMS.colorConvert(this, in, out); } public void colorConvert(BufferedImage src, BufferedImage dst) { - if (LCMSImageLayout.isSupported(src) && - LCMSImageLayout.isSupported(dst)) - { - synchronized(this) { - try { - LCMS.colorConvert(this, new LCMSImageLayout(src), - new LCMSImageLayout(dst)); - } catch (ImageLayoutException e) { - throw new CMMException("Unable to convert images"); + LCMSImageLayout srcIL, dstIL; + try { + if (!dst.getColorModel().hasAlpha()) { + dstIL = LCMSImageLayout.createImageLayout(dst); + + if (dstIL != null) { + srcIL = LCMSImageLayout.createImageLayout(src); + if (srcIL != null) { + doTransform(srcIL, dstIL); + return; + } } } - return; + } catch (ImageLayoutException e) { + throw new CMMException("Unable to convert images"); } - LCMSImageLayout srcIL, dstIL; + Raster srcRas = src.getRaster(); WritableRaster dstRas = dst.getRaster(); ColorModel srcCM = src.getColorModel(); @@ -213,9 +265,8 @@ } } // color convert srcLine to dstLine - synchronized (this) { - LCMS.colorConvert(this, srcIL, dstIL); - } + doTransform(srcIL, dstIL); + // convert dst scanline pixel = null; idx = 0; @@ -275,9 +326,8 @@ } } // color convert srcLine to dstLine - synchronized(this) { - LCMS.colorConvert(this, srcIL, dstIL); - } + doTransform(srcIL, dstIL); + // convert dst scanline pixel = null; idx = 0; @@ -392,9 +442,7 @@ } // color convert srcLine to dstLine - synchronized(this) { - LCMS.colorConvert(this, srcIL, dstIL); - } + doTransform(srcIL, dstIL); // store dst scanline xd = dst.getMinX(); @@ -412,6 +460,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(); @@ -488,9 +544,7 @@ } // color convert srcLine to dstLine - synchronized(this) { - LCMS.colorConvert(this, srcIL, dstIL); - } + doTransform(srcIL, dstIL); // store dst scanline xd = dst.getMinX(); @@ -535,9 +589,8 @@ } // color convert srcLine to dstLine - synchronized(this) { - LCMS.colorConvert(this, srcIL, dstIL); - } + doTransform(srcIL, dstIL); + // store dst scanline xd = dst.getMinX(); idx = 0; @@ -564,23 +617,21 @@ try { LCMSImageLayout srcIL = new LCMSImageLayout( - src, src.length/getNumInComponents(), - LCMSImageLayout.CHANNELS_SH(getNumInComponents()) | - LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2); + src, src.length/getNumInComponents(), + LCMSImageLayout.CHANNELS_SH(getNumInComponents()) | + LCMSImageLayout.BYTES_SH(2), getNumInComponents()*2); LCMSImageLayout dstIL = new LCMSImageLayout( - dst, dst.length/getNumOutComponents(), - LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) | - LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2); + dst, dst.length/getNumOutComponents(), + LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) | + LCMSImageLayout.BYTES_SH(2), getNumOutComponents()*2); - synchronized(this) { - LCMS.colorConvert(this, srcIL, dstIL); - } + doTransform(srcIL, dstIL); + + return dst; } catch (ImageLayoutException e) { throw new CMMException("Unable to convert data"); } - - return dst; } public byte[] colorConvert(byte[] src, byte[] dst) { @@ -590,22 +641,20 @@ try { LCMSImageLayout srcIL = new LCMSImageLayout( - src, src.length/getNumInComponents(), - LCMSImageLayout.CHANNELS_SH(getNumInComponents()) | - LCMSImageLayout.BYTES_SH(1), getNumInComponents()); + src, src.length/getNumInComponents(), + LCMSImageLayout.CHANNELS_SH(getNumInComponents()) | + LCMSImageLayout.BYTES_SH(1), getNumInComponents()); LCMSImageLayout dstIL = new LCMSImageLayout( - dst, dst.length/getNumOutComponents(), - LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) | - LCMSImageLayout.BYTES_SH(1), getNumOutComponents()); + dst, dst.length/getNumOutComponents(), + LCMSImageLayout.CHANNELS_SH(getNumOutComponents()) | + LCMSImageLayout.BYTES_SH(1), getNumOutComponents()); - synchronized(this) { - LCMS.colorConvert(this, srcIL, dstIL); - } + doTransform(srcIL, dstIL); + + return dst; } catch (ImageLayoutException e) { throw new CMMException("Unable to convert data"); } - - return dst; } } diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/LCMS.c --- a/src/share/native/sun/java2d/cmm/lcms/LCMS.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/LCMS.c Thu Dec 07 09:11:50 2017 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2013, Oracle and/or its affiliates. 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 @@ -24,11 +24,14 @@ */ #include +#include +#include #include "sun_java2d_cmm_lcms_LCMS.h" #include "jni_util.h" #include "Trace.h" #include "Disposer.h" -#include "lcms.h" +#include "lcms2.h" +#include "jlong.h" #define ALIGNLONG(x) (((x)+3) & ~(3)) // Aligns to DWORD boundary @@ -38,10 +41,10 @@ #else static -void AdjustEndianess32(LPBYTE pByte) +void AdjustEndianess32(cmsUInt8Number *pByte) { - BYTE temp1; - BYTE temp2; + cmsUInt8Number temp1; + cmsUInt8Number temp2; temp1 = *pByte++; temp2 = *pByte++; @@ -57,11 +60,11 @@ // big endian notation. static -icInt32Number TransportValue32(icInt32Number Value) +cmsInt32Number TransportValue32(cmsInt32Number Value) { - icInt32Number Temp = Value; + cmsInt32Number Temp = Value; - AdjustEndianess32((LPBYTE) &Temp); + AdjustEndianess32((cmsUInt8Number*) &Temp); return Temp; } @@ -84,16 +87,23 @@ /* Default temp profile list size */ #define DF_ICC_BUF_SIZE 32 -#define ERR_MSG_SIZE 20 +#define ERR_MSG_SIZE 256 -typedef union storeID_s { /* store SProfile stuff in a Java Long */ +#ifdef _MSC_VER +# ifndef snprintf +# define snprintf _snprintf +# endif +#endif + +typedef struct lcmsProfile_s { cmsHPROFILE pf; - cmsHTRANSFORM xf; - jobject jobj; - jlong j; -} storeID_t, *storeID_p; +} lcmsProfile_t, *lcmsProfile_p; -static jfieldID Trans_profileIDs_fID; +typedef union { + cmsTagSignature cms; + jint j; +} TagSignature_t, *TagSignature_p; + static jfieldID Trans_renderType_fID; static jfieldID Trans_ID_fID; static jfieldID IL_isIntPacked_fID; @@ -104,34 +114,49 @@ static jfieldID IL_nextRowOffset_fID; static jfieldID IL_width_fID; static jfieldID IL_height_fID; -static jfieldID PF_ID_fID; +static jfieldID IL_imageAtOnce_fID; JavaVM *javaVM; -int errorHandler(int errorCode, const char *errorText) { +void errorHandler(cmsContext ContextID, cmsUInt32Number errorCode, + const char *errorText) { JNIEnv *env; char errMsg[ERR_MSG_SIZE]; - /* We can safely use sprintf here because error message has limited size */ - sprintf(errMsg, "LCMS error %d", errorCode); + + int count = snprintf(errMsg, ERR_MSG_SIZE, + "LCMS error %d: %s", errorCode, errorText); + if (count < 0 || count >= ERR_MSG_SIZE) { + count = ERR_MSG_SIZE - 1; + } + errMsg[count] = 0; (*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL); JNU_ThrowByName(env, "java/awt/color/CMMException", errMsg); - return 1; +} + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { + javaVM = jvm; + + cmsSetLogErrorHandler(errorHandler); + return JNI_VERSION_1_6; } -JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { - javaVM = jvm; +void LCMS_freeProfile(JNIEnv *env, jlong ptr) { + lcmsProfile_p p = (lcmsProfile_p)jlong_to_ptr(ptr); - cmsSetErrorHandler(errorHandler); - return JNI_VERSION_1_6; + if (p != NULL) { + if (p->pf != NULL) { + cmsCloseProfile(p->pf); + } + free(p); + } } void LCMS_freeTransform(JNIEnv *env, jlong ID) { - storeID_t sTrans; - sTrans.j = ID; + cmsHTRANSFORM sTrans = jlong_to_ptr(ID); /* Passed ID is always valid native ref so there is no check for zero */ - cmsDeleteTransform(sTrans.xf); + cmsDeleteTransform(sTrans); } /* @@ -141,268 +166,358 @@ */ JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_createNativeTransform (JNIEnv *env, jclass cls, jlongArray profileIDs, jint renderType, - jobject disposerRef) + jint inFormatter, jboolean isInIntPacked, + jint outFormatter, jboolean isOutIntPacked, jobject disposerRef) { - LPLCMSICCPROFILE _iccArray[DF_ICC_BUF_SIZE]; - LPLCMSICCPROFILE *iccArray = &_iccArray[0]; - cmsHTRANSFORM transform; - storeID_t sTrans; + cmsHPROFILE _iccArray[DF_ICC_BUF_SIZE]; + cmsHPROFILE *iccArray = &_iccArray[0]; + cmsHTRANSFORM sTrans = NULL; int i, j, size; jlong* ids; size = (*env)->GetArrayLength (env, profileIDs); - ids = (*env)->GetPrimitiveArrayCritical(env, profileIDs, 0); + ids = (*env)->GetLongArrayElements(env, profileIDs, 0); + if (ids == NULL) { + // An exception should have already been thrown. + return 0L; + } + +#ifdef _LITTLE_ENDIAN + /* Reversing data packed into int for LE archs */ + if (isInIntPacked) { + inFormatter ^= DOSWAP_SH(1); + } + if (isOutIntPacked) { + outFormatter ^= DOSWAP_SH(1); + } +#endif if (DF_ICC_BUF_SIZE < size*2) { - iccArray = (LPLCMSICCPROFILE*) malloc( - size*2*sizeof(LPLCMSICCPROFILE)); + iccArray = (cmsHPROFILE*) malloc( + size*2*sizeof(cmsHPROFILE)); if (iccArray == NULL) { + (*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0); + J2dRlsTraceLn(J2D_TRACE_ERROR, "getXForm: iccArray == NULL"); - return NULL; + return 0L; } } j = 0; for (i = 0; i < size; i++) { - LPLCMSICCPROFILE icc; - sTrans.j = ids[i]; - icc = sTrans.pf; + cmsColorSpaceSignature cs; + lcmsProfile_p profilePtr = (lcmsProfile_p)jlong_to_ptr(ids[i]); + cmsHPROFILE icc = profilePtr->pf; + iccArray[j++] = icc; /* Middle non-abstract profiles should be doubled before passing to * the cmsCreateMultiprofileTransform function */ + + cs = cmsGetColorSpace(icc); if (size > 2 && i != 0 && i != size - 1 && - icc->ColorSpace != icSigXYZData && - icc->ColorSpace != icSigLabData) + cs != cmsSigXYZData && cs != cmsSigLabData) { iccArray[j++] = icc; } } - sTrans.xf = cmsCreateMultiprofileTransform(iccArray, j, - 0, 0, renderType, 0); + sTrans = cmsCreateMultiprofileTransform(iccArray, j, + inFormatter, outFormatter, renderType, 0); - (*env)->ReleasePrimitiveArrayCritical(env, profileIDs, ids, 0); + (*env)->ReleaseLongArrayElements(env, profileIDs, ids, 0); - if (sTrans.xf == NULL) { + if (sTrans == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_createNativeTransform: " - "sTrans.xf == NULL"); - JNU_ThrowByName(env, "java/awt/color/CMMException", - "Cannot get color transform"); + "sTrans == NULL"); + if ((*env)->ExceptionOccurred(env) == NULL) { + JNU_ThrowByName(env, "java/awt/color/CMMException", + "Cannot get color transform"); + } } else { - Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, sTrans.j); + Disposer_AddRecord(env, disposerRef, LCMS_freeTransform, ptr_to_jlong(sTrans)); } + if (iccArray != &_iccArray[0]) { free(iccArray); } - return sTrans.j; + return ptr_to_jlong(sTrans); } /* * Class: sun_java2d_cmm_lcms_LCMS * Method: loadProfile - * Signature: ([B)J + * Signature: ([B,Lsun/java2d/cmm/lcms/LCMSProfile;)V */ -JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfile - (JNIEnv *env, jobject obj, jbyteArray data) +JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_loadProfileNative + (JNIEnv *env, jobject obj, jbyteArray data, jobject disposerRef) { jbyte* dataArray; jint dataSize; - storeID_t sProf; + lcmsProfile_p sProf = NULL; + cmsHPROFILE pf; + + if (JNU_IsNull(env, data)) { + JNU_ThrowIllegalArgumentException(env, "Invalid profile data"); + return 0L; + } dataArray = (*env)->GetByteArrayElements (env, data, 0); + if (dataArray == NULL) { + // An exception should have already been thrown. + return 0L; + } + dataSize = (*env)->GetArrayLength (env, data); - sProf.pf = cmsOpenProfileFromMem((LPVOID)dataArray, (DWORD) dataSize); + pf = cmsOpenProfileFromMem((const void *)dataArray, + (cmsUInt32Number) dataSize); (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); - if (sProf.pf == NULL) { + if (pf == NULL) { JNU_ThrowIllegalArgumentException(env, "Invalid profile data"); + } else { + /* Sanity check: try to save the profile in order + * to force basic validation. + */ + cmsUInt32Number pfSize = 0; + if (!cmsSaveProfileToMem(pf, NULL, &pfSize) || + pfSize < sizeof(cmsICCHeader)) + { + JNU_ThrowIllegalArgumentException(env, "Invalid profile data"); + + cmsCloseProfile(pf); + pf = NULL; + } } - return sProf.j; -} - -/* - * Class: sun_java2d_cmm_lcms_LCMS - * Method: freeProfile - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_freeProfile - (JNIEnv *env, jobject obj, jlong id) -{ - storeID_t sProf; - - sProf.j = id; - if (cmsCloseProfile(sProf.pf) == 0) { - J2dRlsTraceLn1(J2D_TRACE_ERROR, "LCMS_freeProfile: cmsCloseProfile(%d)" - "== 0", id); - JNU_ThrowByName(env, "java/awt/color/CMMException", - "Cannot close profile"); + if (pf != NULL) { + // create profile holder + sProf = (lcmsProfile_p)malloc(sizeof(lcmsProfile_t)); + if (sProf != NULL) { + // register the disposer record + sProf->pf = pf; + Disposer_AddRecord(env, disposerRef, LCMS_freeProfile, ptr_to_jlong(sProf)); + } else { + cmsCloseProfile(pf); + } } + return ptr_to_jlong(sProf); } /* * Class: sun_java2d_cmm_lcms_LCMS - * Method: getProfileSize + * Method: getProfileSizeNative * Signature: (J)I */ -JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSize +JNIEXPORT jint JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileSizeNative (JNIEnv *env, jobject obj, jlong id) { - LPLCMSICCPROFILE Icc; - storeID_t sProf; - unsigned char pfSize[4]; + lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id); + cmsUInt32Number pfSize = 0; - sProf.j = id; - Icc = (LPLCMSICCPROFILE) sProf.pf; - Icc -> Seek(Icc, 0); - Icc -> Read(pfSize, 4, 1, Icc); - - /* TODO: It's a correct but non-optimal for BE machines code, so should - * be special cased for them - */ - return (pfSize[0]<<24) | (pfSize[1]<<16) | (pfSize[2]<<8) | - pfSize[3]; + if (cmsSaveProfileToMem(sProf->pf, NULL, &pfSize) && ((jint)pfSize > 0)) { + return (jint)pfSize; + } else { + JNU_ThrowByName(env, "java/awt/color/CMMException", + "Can not access specified profile."); + return -1; + } } /* * Class: sun_java2d_cmm_lcms_LCMS - * Method: getProfileData + * Method: getProfileDataNative * Signature: (J[B)V */ -JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileData +JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileDataNative (JNIEnv *env, jobject obj, jlong id, jbyteArray data) { - LPLCMSICCPROFILE Icc; - storeID_t sProf; - unsigned char pfSize[4]; + lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id); jint size; jbyte* dataArray; + cmsUInt32Number pfSize = 0; + cmsBool status; - sProf.j = id; - Icc = (LPLCMSICCPROFILE) sProf.pf; - Icc -> Seek(Icc, 0); - Icc -> Read(pfSize, 4, 1, Icc); + // determine actual profile size + if (!cmsSaveProfileToMem(sProf->pf, NULL, &pfSize)) { + JNU_ThrowByName(env, "java/awt/color/CMMException", + "Can not access specified profile."); + return; + } + + // verify java buffer capacity + size = (*env)->GetArrayLength(env, data); + if (0 >= size || pfSize > (cmsUInt32Number)size) { + JNU_ThrowByName(env, "java/awt/color/CMMException", + "Insufficient buffer capacity."); + return; + } dataArray = (*env)->GetByteArrayElements (env, data, 0); - Icc->Seek(Icc, 0); + if (dataArray == NULL) { + // An exception should have already been thrown. + return; + } + + status = cmsSaveProfileToMem(sProf->pf, dataArray, &pfSize); - /* TODO: It's a correct but non-optimal for BE machines code, so should - * be special cased for them - */ - Icc->Read(dataArray, 1, - (pfSize[0]<<24) | (pfSize[1]<<16) | (pfSize[2]<<8) | pfSize[3], - Icc); (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); + + if (!status) { + JNU_ThrowByName(env, "java/awt/color/CMMException", + "Can not access specified profile."); + return; + } } -/* - * 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) -{ - LPLCMSICCPROFILE Icc; - storeID_t sProf; - int i; - jint result; - - sProf.j = id; - Icc = (LPLCMSICCPROFILE) sProf.pf; +/* Get profile header info */ +static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize); +static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize); +static cmsHPROFILE _writeCookedTag(cmsHPROFILE pfTarget, cmsTagSignature sig, jbyte *pData, jint size); - if (tagSig == SigHead) { - result = sizeof(icHeader); - } else { - i = _cmsSearchTag(Icc, tagSig, FALSE); - if (i >= 0) { - result = Icc->TagSizes[i]; - } else { - JNU_ThrowByName(env, "java/awt/color/CMMException", - "ICC profile tag not found"); - result = -1; - } - } - - 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) { - LPLCMSICCPROFILE Icc; - storeID_t sProf; - jbyte* dataArray; - int i, tagSize; + lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id); + TagSignature_t sig; + cmsUInt32Number tagSize; - sProf.j = id; - Icc = (LPLCMSICCPROFILE) sProf.pf; + jbyte* dataArray = NULL; + jbyteArray data = NULL; + + cmsUInt32Number bufSize; + + sig.j = tagSig; if (tagSig == SigHead) { + cmsBool status; + + // allocate java array + bufSize = sizeof(cmsICCHeader); + data = (*env)->NewByteArray(env, bufSize); + + if (data == NULL) { + // An exception should have already been thrown. + return NULL; + } + dataArray = (*env)->GetByteArrayElements (env, data, 0); - tagSize =(*env)->GetArrayLength(env, data); - Icc -> Seek(Icc, 0); - Icc -> Read(dataArray, sizeof(icHeader), 1, Icc); + + if (dataArray == NULL) { + // An exception should have already been thrown. + return NULL; + } + + status = _getHeaderInfo(sProf->pf, dataArray, bufSize); + (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); - return; + + if (!status) { + JNU_ThrowByName(env, "java/awt/color/CMMException", + "ICC Profile header not found"); + return NULL; + } + + return data; } + if (cmsIsTag(sProf->pf, sig.cms)) { + tagSize = cmsReadRawTag(sProf->pf, sig.cms, NULL, 0); + } else { + JNU_ThrowByName(env, "java/awt/color/CMMException", + "ICC profile tag not found"); + return NULL; + } - i = _cmsSearchTag(Icc, tagSig, FALSE); - if (i >=0) { - tagSize = Icc->TagSizes[i]; - dataArray = (*env)->GetByteArrayElements (env, data, 0); - Icc->Seek(Icc, Icc->TagOffsets[i]); - Icc->Read(dataArray, 1, tagSize, Icc); - (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); - return; + // allocate java array + data = (*env)->NewByteArray(env, tagSize); + if (data == NULL) { + // An exception should have already been thrown. + return NULL; } - JNU_ThrowByName(env, "java/awt/color/CMMException", - "ICC profile tag not found"); - return; + dataArray = (*env)->GetByteArrayElements (env, data, 0); + + if (dataArray == NULL) { + // An exception should have already been thrown. + return NULL; + } + + bufSize = cmsReadRawTag(sProf->pf, sig.cms, dataArray, tagSize); + + (*env)->ReleaseByteArrayElements (env, data, dataArray, 0); + + if (bufSize != tagSize) { + JNU_ThrowByName(env, "java/awt/color/CMMException", + "Can not get tag data."); + return NULL; + } + return data; } -// Modify data for a tag in a profile -LCMSBOOL LCMSEXPORT _cmsModifyTagData(cmsHPROFILE hProfile, - icTagSignature sig, void *data, size_t size); - /* * Class: sun_java2d_cmm_lcms_LCMS * 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) { - cmsHPROFILE profile; - storeID_t sProf; + lcmsProfile_p sProf = (lcmsProfile_p)jlong_to_ptr(id); + cmsHPROFILE pfReplace = NULL; + + TagSignature_t sig; + cmsBool status = FALSE; jbyte* dataArray; int tagSize; - if (tagSig == SigHead) { - J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_setTagData on icSigHead not " - "permitted"); + sig.j = tagSig; + + if (JNU_IsNull(env, data)) { + JNU_ThrowIllegalArgumentException(env, "Can not write tag data."); + return; + } + + tagSize =(*env)->GetArrayLength(env, data); + + dataArray = (*env)->GetByteArrayElements(env, data, 0); + + if (dataArray == NULL) { + // An exception should have already been thrown. 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); + if (tagSig == SigHead) { + status = _setHeaderInfo(sProf->pf, dataArray, tagSize); + } else { + /* + * New strategy for generic tags: create a place holder, + * dump all existing tags there, dump externally supplied + * tag, and return the new profile to the java. + */ + pfReplace = _writeCookedTag(sProf->pf, sig.cms, dataArray, tagSize); + status = (pfReplace != NULL); + } + (*env)->ReleaseByteArrayElements(env, data, dataArray, 0); + + if (!status) { + JNU_ThrowIllegalArgumentException(env, "Can not write tag data."); + } else if (pfReplace != NULL) { + cmsCloseProfile(sProf->pf); + sProf->pf = pfReplace; + } } void* getILData (JNIEnv *env, jobject img, jint* pDataType, @@ -455,8 +570,8 @@ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert (JNIEnv *env, jclass obj, jobject trans, jobject src, jobject dst) { - storeID_t sTrans; - int size, inFmt, outFmt, srcDType, dstDType, outSize, renderType; + cmsHTRANSFORM sTrans = NULL; + int srcDType, dstDType; int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset; int width, height, i; void* inputBuffer; @@ -464,29 +579,21 @@ char* inputRow; char* outputRow; jobject srcData, dstData; + jboolean srcAtOnce = JNI_FALSE, dstAtOnce = JNI_FALSE; - inFmt = (*env)->GetIntField (env, src, IL_pixelType_fID); - outFmt = (*env)->GetIntField (env, dst, IL_pixelType_fID); srcOffset = (*env)->GetIntField (env, src, IL_offset_fID); srcNextRowOffset = (*env)->GetIntField (env, src, IL_nextRowOffset_fID); dstOffset = (*env)->GetIntField (env, dst, IL_offset_fID); dstNextRowOffset = (*env)->GetIntField (env, dst, IL_nextRowOffset_fID); width = (*env)->GetIntField (env, src, IL_width_fID); height = (*env)->GetIntField (env, src, IL_height_fID); -#ifdef _LITTLE_ENDIAN - /* Reversing data packed into int for LE archs */ - if ((*env)->GetBooleanField (env, src, IL_isIntPacked_fID) == JNI_TRUE) { - inFmt ^= DOSWAP_SH(1); - } - if ((*env)->GetBooleanField (env, dst, IL_isIntPacked_fID) == JNI_TRUE) { - outFmt ^= DOSWAP_SH(1); - } -#endif - sTrans.j = (*env)->GetLongField (env, trans, Trans_ID_fID); - cmsChangeBuffersFormat(sTrans.xf, inFmt, outFmt); + + srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID); + dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID); + sTrans = jlong_to_ptr((*env)->GetLongField (env, trans, Trans_ID_fID)); - if (sTrans.xf == NULL) { + if (sTrans == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL"); JNU_ThrowByName(env, "java/awt/color/CMMException", "Cannot get color transform"); @@ -498,8 +605,7 @@ if (inputBuffer == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, ""); - JNU_ThrowByName(env, "java/awt/color/CMMException", - "Cannot get input data"); + // An exception should have already been thrown. return; } @@ -507,18 +613,21 @@ if (outputBuffer == NULL) { releaseILData(env, inputBuffer, srcDType, srcData); - JNU_ThrowByName(env, "java/awt/color/CMMException", - "Cannot get output data"); + // An exception should have already been thrown. return; } 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, inputRow, outputRow, width * height); + } else { + for (i = 0; i < height; i++) { + cmsDoTransform(sTrans, inputRow, outputRow, width); + inputRow += srcNextRowOffset; + outputRow += dstNextRowOffset; + } } releaseILData(env, inputBuffer, srcDType, srcData); @@ -528,12 +637,40 @@ /* * Class: sun_java2d_cmm_lcms_LCMS * Method: getProfileID - * Signature: (Ljava/awt/color/ICC_Profile;)J + * Signature: (Ljava/awt/color/ICC_Profile;)Lsun/java2d/cmm/lcms/LCMSProfile */ -JNIEXPORT jlong JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID +JNIEXPORT jobject JNICALL Java_sun_java2d_cmm_lcms_LCMS_getProfileID (JNIEnv *env, jclass cls, jobject pf) { - return (*env)->GetLongField (env, pf, PF_ID_fID); + jclass clsLcmsProfile; + jobject cmmProfile; + jfieldID fid; + + if (pf == NULL) { + return NULL; + } + fid = (*env)->GetFieldID (env, + (*env)->GetObjectClass(env, pf), + "cmmProfile", "Lsun/java2d/cmm/Profile;"); + if (fid == NULL) { + return NULL; + } + + clsLcmsProfile = (*env)->FindClass(env, + "sun/java2d/cmm/lcms/LCMSProfile"); + if (clsLcmsProfile == NULL) { + return NULL; + } + + cmmProfile = (*env)->GetObjectField (env, pf, fid); + + if (JNU_IsNull(env, cmmProfile)) { + return NULL; + } + if ((*env)->IsInstanceOf(env, cmmProfile, clsLcmsProfile)) { + return cmmProfile; + } + return NULL; } /* @@ -548,207 +685,215 @@ * corresponding classes to avoid problems with invalidating ids by class * unloading */ - Trans_profileIDs_fID = (*env)->GetFieldID (env, Trans, "profileIDs", "[J"); Trans_renderType_fID = (*env)->GetFieldID (env, Trans, "renderType", "I"); + if (Trans_renderType_fID == NULL) { + return; + } Trans_ID_fID = (*env)->GetFieldID (env, Trans, "ID", "J"); + if (Trans_ID_fID == NULL) { + return; + } IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked", "Z"); + if (IL_isIntPacked_fID == NULL) { + return; + } IL_dataType_fID = (*env)->GetFieldID (env, IL, "dataType", "I"); + if (IL_dataType_fID == NULL) { + return; + } IL_pixelType_fID = (*env)->GetFieldID (env, IL, "pixelType", "I"); + if (IL_pixelType_fID == NULL) { + return; + } IL_dataArray_fID = (*env)->GetFieldID(env, IL, "dataArray", "Ljava/lang/Object;"); + if (IL_dataArray_fID == NULL) { + return; + } IL_width_fID = (*env)->GetFieldID (env, IL, "width", "I"); + if (IL_width_fID == NULL) { + return; + } IL_height_fID = (*env)->GetFieldID (env, IL, "height", "I"); + if (IL_height_fID == NULL) { + return; + } IL_offset_fID = (*env)->GetFieldID (env, IL, "offset", "I"); + if (IL_offset_fID == NULL) { + return; + } + IL_imageAtOnce_fID = (*env)->GetFieldID (env, IL, "imageAtOnce", "Z"); + if (IL_imageAtOnce_fID == NULL) { + return; + } IL_nextRowOffset_fID = (*env)->GetFieldID (env, IL, "nextRowOffset", "I"); + if (IL_nextRowOffset_fID == NULL) { + return; + } +} + +static cmsBool _getHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize) +{ + cmsUInt32Number pfSize = 0; + cmsUInt8Number* pfBuffer = NULL; + cmsBool status = FALSE; - PF_ID_fID = (*env)->GetFieldID (env, Pf, "ID", "J"); + if (!cmsSaveProfileToMem(pf, NULL, &pfSize) || + pfSize < sizeof(cmsICCHeader) || + bufferSize < (jint)sizeof(cmsICCHeader)) + { + return FALSE; + } + + pfBuffer = malloc(pfSize); + if (pfBuffer == NULL) { + return FALSE; + } + + // load raw profile data into the buffer + if (cmsSaveProfileToMem(pf, pfBuffer, &pfSize)) { + memcpy(pBuffer, pfBuffer, sizeof(cmsICCHeader)); + status = TRUE; + } + free(pfBuffer); + return status; } -LCMSBOOL _cmsModifyTagData(cmsHPROFILE hProfile, icTagSignature sig, - void *data, size_t size) +static cmsBool _setHeaderInfo(cmsHPROFILE pf, jbyte* pBuffer, jint bufferSize) { - LCMSBOOL 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; + cmsICCHeader pfHeader; + + if (pBuffer == NULL || bufferSize < (jint)sizeof(cmsICCHeader)) { + return FALSE; + } + + memcpy(&pfHeader, pBuffer, sizeof(cmsICCHeader)); + + // now set header fields, which we can access using the lcms2 public API + cmsSetHeaderFlags(pf, pfHeader.flags); + cmsSetHeaderManufacturer(pf, pfHeader.manufacturer); + cmsSetHeaderModel(pf, pfHeader.model); + cmsSetHeaderAttributes(pf, pfHeader.attributes); + cmsSetHeaderProfileID(pf, (cmsUInt8Number*)&(pfHeader.profileID)); + cmsSetHeaderRenderingIntent(pf, pfHeader.renderingIntent); + cmsSetPCS(pf, pfHeader.pcs); + cmsSetColorSpace(pf, pfHeader.colorSpace); + cmsSetDeviceClass(pf, pfHeader.deviceClass); + cmsSetEncodedICCversion(pf, pfHeader.version); + + return TRUE; +} + +/* Returns new profile handler, if it was created successfully, + NULL otherwise. + */ +static cmsHPROFILE _writeCookedTag(const cmsHPROFILE pfTarget, + const cmsTagSignature sig, + jbyte *pData, jint size) +{ + cmsUInt32Number pfSize = 0; + const cmsInt32Number tagCount = cmsGetTagCount(pfTarget); + cmsInt32Number i; + cmsHPROFILE pfSanity = NULL; + + cmsICCHeader hdr; + + cmsHPROFILE p = cmsCreateProfilePlaceholder(NULL); + + if (NULL == p) { + return NULL; + } + memset(&hdr, 0, sizeof(cmsICCHeader)); - 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; + // Populate the placeholder's header according to target profile + hdr.flags = cmsGetHeaderFlags(pfTarget); + hdr.renderingIntent = cmsGetHeaderRenderingIntent(pfTarget); + hdr.manufacturer = cmsGetHeaderManufacturer(pfTarget); + hdr.model = cmsGetHeaderModel(pfTarget); + hdr.pcs = cmsGetPCS(pfTarget); + hdr.colorSpace = cmsGetColorSpace(pfTarget); + hdr.deviceClass = cmsGetDeviceClass(pfTarget); + hdr.version = cmsGetEncodedICCversion(pfTarget); + cmsGetHeaderAttributes(pfTarget, &hdr.attributes); + cmsGetHeaderProfileID(pfTarget, (cmsUInt8Number*)&hdr.profileID); + + cmsSetHeaderFlags(p, hdr.flags); + cmsSetHeaderManufacturer(p, hdr.manufacturer); + cmsSetHeaderModel(p, hdr.model); + cmsSetHeaderAttributes(p, hdr.attributes); + cmsSetHeaderProfileID(p, (cmsUInt8Number*)&(hdr.profileID)); + cmsSetHeaderRenderingIntent(p, hdr.renderingIntent); + cmsSetPCS(p, hdr.pcs); + cmsSetColorSpace(p, hdr.colorSpace); + cmsSetDeviceClass(p, hdr.deviceClass); + cmsSetEncodedICCversion(p, hdr.version); + + // now write the user supplied tag + if (size <= 0 || !cmsWriteRawTag(p, sig, pData, size)) { + cmsCloseProfile(p); + return NULL; + } + + // copy tags from the original profile + for (i = 0; i < tagCount; i++) { + cmsBool isTagReady = FALSE; + const cmsTagSignature s = cmsGetTagSignature(pfTarget, i); + const cmsUInt32Number tagSize = cmsReadRawTag(pfTarget, s, NULL, 0); + + if (s == sig) { + // skip the user supplied tag + continue; + } + + // read raw tag from the original profile + if (tagSize > 0) { + cmsUInt8Number* buf = (cmsUInt8Number*)malloc(tagSize); + if (buf != NULL) { + if (tagSize == cmsReadRawTag(pfTarget, s, buf, tagSize)) { + // now we are ready to write the tag + isTagReady = cmsWriteRawTag(p, s, buf, tagSize); + } + free(buf); + } + } + + if (!isTagReady) { + cmsCloseProfile(p); + return NULL; } } - /* 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; - } - - /* We change the size of Icc here only if we know it'll actually - * grow: if Icc is about to shrink we must wait until we've read - * the previous data. */ - if (delta > 0) { - 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--; + // now we have all tags moved to the new profile. + // do some sanity checks: write it to a memory buffer and read again. + if (cmsSaveProfileToMem(p, NULL, &pfSize)) { + void* buf = malloc(pfSize); + if (buf != NULL) { + // load raw profile data into the buffer + if (cmsSaveProfileToMem(p, buf, &pfSize)) { + pfSanity = cmsOpenProfileFromMem(buf, pfSize); } - 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); - - /* Shrink Icc, if needed. */ - if (delta < 0) { - if (!Icc->Grow(Icc, delta)) { - free(ptr); - if(isNew) { - Icc->TagCount--; - } - J2dRlsTraceLn(J2D_TRACE_ERROR, - "_cmsModifyTagData: Icc->Grow() == FALSE"); - return FALSE; - } - } - - /* 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; + free(buf); } } - /* Write out a new tag table */ - count = 0; - for (i = 0; i < Icc->TagCount; ++i) { - if (Icc->TagNames[i] != 0) { - ++count; + if (pfSanity == NULL) { + // for some reason, we failed to save and read the updated profile + // It likely indicates that the profile is not correct, so we report + // a failure here. + cmsCloseProfile(p); + p = NULL; + } else { + // do final check whether we can read and handle the the target tag. + const void* pTag = cmsReadTag(pfSanity, sig); + if (pTag == NULL) { + // the tag can not be cooked + cmsCloseProfile(p); + p = NULL; } - } - 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); - } + cmsCloseProfile(pfSanity); + pfSanity = NULL; } - /* 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; + return p; } diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsalpha.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsalpha.c Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,588 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Alpha copy ------------------------------------------------------------------------------------------------------------------ + +// Floor to byte, taking care of saturation +cmsINLINE cmsUInt8Number _cmsQuickSaturateByte(cmsFloat64Number d) +{ + d += 0.5; + if (d <= 0) return 0; + if (d >= 255.0) return 255; + + return (cmsUInt8Number) _cmsQuickFloorWord(d); +} + + +// Return the size in bytes of a given formatter +static +cmsUInt32Number trueBytesSize(cmsUInt32Number Format) +{ + cmsUInt32Number fmt_bytes = T_BYTES(Format); + + // For double, the T_BYTES field returns zero + if (fmt_bytes == 0) + return sizeof(double); + + // Otherwise, it is already correct for all formats + return fmt_bytes; +} + + +// Several format converters + +typedef void(*cmsFormatterAlphaFn)(void* dst, const void* src); + + +// From 8 + +static +void copy8(void* dst, const void* src) +{ + memmove(dst, src, 1); +} + +static +void from8to16(void* dst, const void* src) +{ + cmsUInt8Number n = *(cmsUInt8Number*)src; + *(cmsUInt16Number*) dst = FROM_8_TO_16(n); +} + +static +void from8toFLT(void* dst, const void* src) +{ + *(cmsFloat32Number*)dst = (*(cmsUInt8Number*)src) / 255.0f; +} + +static +void from8toDBL(void* dst, const void* src) +{ + *(cmsFloat64Number*)dst = (*(cmsUInt8Number*)src) / 255.0; +} + +static +void from8toHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +// From 16 + +static +void from16to8(void* dst, const void* src) +{ + cmsUInt16Number n = *(cmsUInt16Number*)src; + *(cmsUInt8Number*) dst = FROM_16_TO_8(n); +} + +static +void copy16(void* dst, const void* src) +{ + memmove(dst, src, 2); +} + +void from16toFLT(void* dst, const void* src) +{ + *(cmsFloat32Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f; +} + +void from16toDBL(void* dst, const void* src) +{ + *(cmsFloat64Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f; +} + +static +void from16toHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +// From Float + +static +void fromFLTto8(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); +} + +static +void fromFLTto16(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +} + +static +void copy32(void* dst, const void* src) +{ + memmove(dst, src, sizeof(cmsFloat32Number)); +} + +static +void fromFLTtoDBL(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsFloat64Number*)dst = (cmsFloat64Number)n; +} + +static +void fromFLTtoHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + + +// From HALF + +static +void fromHLFto8(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif + +} + +static +void fromHLFto16(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void fromHLFtoFLT(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + *(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void fromHLFtoDBL(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + *(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +// From double +static +void fromDBLto8(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0); +} + +static +void fromDBLto16(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +} + +static +void fromDBLtoFLT(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsFloat32Number*)dst = (cmsFloat32Number) n; +} + +static +void fromDBLtoHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void copy64(void* dst, const void* src) +{ + memmove(dst, src, sizeof(cmsFloat64Number)); +} + + +// Returns the position (x or y) of the formatter in the table of functions +static +int FormatterPos(cmsUInt32Number frm) +{ + cmsUInt32Number b = T_BYTES(frm); + + if (b == 0 && T_FLOAT(frm)) + return 4; // DBL +#ifndef CMS_NO_HALF_SUPPORT + if (b == 2 && T_FLOAT(frm)) + return 2; // HLF +#endif + if (b == 4 && T_FLOAT(frm)) + return 3; // FLT + if (b == 2 && !T_FLOAT(frm)) + return 1; // 16 + if (b == 1 && !T_FLOAT(frm)) + return 0; // 8 + + return -1; // not recognized +} + +// Obtains a alpha-to-alpha funmction formatter +static +cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out) +{ +static cmsFormatterAlphaFn FormattersAlpha[5][5] = { + + /* from 8 */ { copy8, from8to16, from8toHLF, from8toFLT, from8toDBL }, + /* from 16*/ { from16to8, copy16, from16toHLF, from16toFLT, from16toDBL }, + /* from HLF*/ { fromHLFto8, fromHLFto16, copy16, fromHLFtoFLT, fromHLFtoDBL }, + /* from FLT*/ { fromFLTto8, fromFLTto16, fromFLTtoHLF, copy32, fromFLTtoDBL }, + /* from DBL*/ { fromDBLto8, fromDBLto16, fromDBLtoHLF, fromDBLtoFLT, copy64 }}; + + int in_n = FormatterPos(in); + int out_n = FormatterPos(out); + + if (in_n < 0 || out_n < 0 || in_n > 4 || out_n > 4) { + + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized alpha channel width"); + return NULL; + } + + return FormattersAlpha[in_n][out_n]; +} + + + +// This function computes the distance from each component to the next one in bytes. +static +void ComputeIncrementsForChunky(cmsUInt32Number Format, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + cmsUInt32Number channels[cmsMAXCHANNELS]; + cmsUInt32Number extra = T_EXTRA(Format); + cmsUInt32Number nchannels = T_CHANNELS(Format); + cmsUInt32Number total_chans = nchannels + extra; + cmsUInt32Number i; + cmsUInt32Number channelSize = trueBytesSize(Format); + cmsUInt32Number pixelSize = channelSize * total_chans; + + // Sanity check + if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) + return; + + memset(channels, 0, sizeof(channels)); + + // Separation is independent of starting point and only depends on channel size + for (i = 0; i < extra; i++) + ComponentPointerIncrements[i] = pixelSize; + + // Handle do swap + for (i = 0; i < total_chans; i++) + { + if (T_DOSWAP(Format)) { + channels[i] = total_chans - i - 1; + } + else { + channels[i] = i; + } + } + + // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012 + if (T_SWAPFIRST(Format) && total_chans > 1) { + + cmsUInt32Number tmp = channels[0]; + for (i = 0; i < total_chans-1; i++) + channels[i] = channels[i + 1]; + + channels[total_chans - 1] = tmp; + } + + // Handle size + if (channelSize > 1) + for (i = 0; i < total_chans; i++) { + channels[i] *= channelSize; + } + + for (i = 0; i < extra; i++) + ComponentStartingOrder[i] = channels[i + nchannels]; +} + + + +// On planar configurations, the distance is the stride added to any non-negative +static +void ComputeIncrementsForPlanar(cmsUInt32Number Format, + cmsUInt32Number BytesPerPlane, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + cmsUInt32Number channels[cmsMAXCHANNELS]; + cmsUInt32Number extra = T_EXTRA(Format); + cmsUInt32Number nchannels = T_CHANNELS(Format); + cmsUInt32Number total_chans = nchannels + extra; + cmsUInt32Number i; + cmsUInt32Number channelSize = trueBytesSize(Format); + + // Sanity check + if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) + return; + + memset(channels, 0, sizeof(channels)); + + // Separation is independent of starting point and only depends on channel size + for (i = 0; i < extra; i++) + ComponentPointerIncrements[i] = channelSize; + + // Handle do swap + for (i = 0; i < total_chans; i++) + { + if (T_DOSWAP(Format)) { + channels[i] = total_chans - i - 1; + } + else { + channels[i] = i; + } + } + + // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012 + if (T_SWAPFIRST(Format) && total_chans > 0) { + + cmsUInt32Number tmp = channels[0]; + for (i = 0; i < total_chans - 1; i++) + channels[i] = channels[i + 1]; + + channels[total_chans - 1] = tmp; + } + + // Handle size + for (i = 0; i < total_chans; i++) { + channels[i] *= BytesPerPlane; + } + + for (i = 0; i < extra; i++) + ComponentStartingOrder[i] = channels[i + nchannels]; +} + + + +// Dispatcher por chunky and planar RGB +static +void ComputeComponentIncrements(cmsUInt32Number Format, + cmsUInt32Number BytesPerPlane, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + if (T_PLANAR(Format)) { + + ComputeIncrementsForPlanar(Format, BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements); + } + else { + ComputeIncrementsForChunky(Format, ComponentStartingOrder, ComponentPointerIncrements); + } + +} + + + +// Handles extra channels copying alpha if requested by the flags +void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt32Number i, j, k; + cmsUInt32Number nExtra; + cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS]; + cmsUInt32Number SourceIncrements[cmsMAXCHANNELS]; + cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS]; + cmsUInt32Number DestIncrements[cmsMAXCHANNELS]; + + cmsFormatterAlphaFn copyValueFn; + + // Make sure we need some copy + if (!(p->dwOriginalFlags & cmsFLAGS_COPY_ALPHA)) + return; + + // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves. + if (p->InputFormat == p->OutputFormat && in == out) + return; + + // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time. + nExtra = T_EXTRA(p->InputFormat); + if (nExtra != T_EXTRA(p->OutputFormat)) + return; + + // Anything to do? + if (nExtra == 0) + return; + + // Compute the increments + ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements); + ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements); + + // Check for conversions 8, 16, half, float, dbl + copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat); + + if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly + + cmsUInt8Number* SourcePtr; + cmsUInt8Number* DestPtr; + + cmsUInt32Number SourceStrideIncrement = 0; + cmsUInt32Number DestStrideIncrement = 0; + + // The loop itself + for (i = 0; i < LineCount; i++) { + + // Prepare pointers for the loop + SourcePtr = (cmsUInt8Number*)in + SourceStartingOrder[0] + SourceStrideIncrement; + DestPtr = (cmsUInt8Number*)out + DestStartingOrder[0] + DestStrideIncrement; + + for (j = 0; j < PixelsPerLine; j++) { + + copyValueFn(DestPtr, SourcePtr); + + SourcePtr += SourceIncrements[0]; + DestPtr += DestIncrements[0]; + } + + SourceStrideIncrement += Stride->BytesPerLineIn; + DestStrideIncrement += Stride->BytesPerLineOut; + } + + } + else { // General case with more than one extra channel + + cmsUInt8Number* SourcePtr[cmsMAXCHANNELS]; + cmsUInt8Number* DestPtr[cmsMAXCHANNELS]; + + cmsUInt32Number SourceStrideIncrements[cmsMAXCHANNELS]; + cmsUInt32Number DestStrideIncrements[cmsMAXCHANNELS]; + + memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements)); + memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements)); + + // The loop itself + for (i = 0; i < LineCount; i++) { + + // Prepare pointers for the loop + for (j = 0; j < nExtra; j++) { + + SourcePtr[j] = (cmsUInt8Number*)in + SourceStartingOrder[j] + SourceStrideIncrements[j]; + DestPtr[j] = (cmsUInt8Number*)out + DestStartingOrder[j] + DestStrideIncrements[j]; + } + + for (j = 0; j < PixelsPerLine; j++) { + + for (k = 0; k < nExtra; k++) { + + copyValueFn(DestPtr[k], SourcePtr[k]); + + SourcePtr[k] += SourceIncrements[k]; + DestPtr[k] += DestIncrements[k]; + } + } + + for (j = 0; j < nExtra; j++) { + + SourceStrideIncrements[j] += Stride->BytesPerLineIn; + DestStrideIncrements[j] += Stride->BytesPerLineOut; + } + } + } +} + + diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmscam02.c --- a/src/share/native/sun/java2d/cmm/lcms/cmscam02.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmscam02.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,69 +49,65 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// - +#include "lcms2_internal.h" // CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging. -#include "lcms.h" - - -LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC); -LCMSAPI void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel); -LCMSAPI void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut); -LCMSAPI void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut); - - // ---------- Implementation -------------------------------------------- typedef struct { - double XYZ[3]; - double RGB[3]; - double RGBc[3]; - double RGBp[3]; - double RGBpa[3]; - double a, b, h, e, H, A, J, Q, s, t, C, M; - double abC[2]; - double abs[2]; - double abM[2]; + cmsFloat64Number XYZ[3]; + cmsFloat64Number RGB[3]; + cmsFloat64Number RGBc[3]; + cmsFloat64Number RGBp[3]; + cmsFloat64Number RGBpa[3]; + cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M; + cmsFloat64Number abC[2]; + cmsFloat64Number abs[2]; + cmsFloat64Number abM[2]; -} CAM02COLOR, *LPCAM02COLOR; +} CAM02COLOR; typedef struct { CAM02COLOR adoptedWhite; - double LA, Yb; - double F, c, Nc; - int surround; - double n, Nbb, Ncb, z, FL, D; + cmsFloat64Number LA, Yb; + cmsFloat64Number F, c, Nc; + cmsUInt32Number surround; + cmsFloat64Number n, Nbb, Ncb, z, FL, D; -} cmsCIECAM02, *LPcmsCIECAM02; + cmsContext ContextID; + +} cmsCIECAM02; static -double compute_n(LPcmsCIECAM02 pMod) +cmsFloat64Number compute_n(cmsCIECAM02* pMod) { - return(pMod -> Yb / pMod -> adoptedWhite.XYZ[1]); + return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]); } static -double compute_z(LPcmsCIECAM02 pMod) +cmsFloat64Number compute_z(cmsCIECAM02* pMod) { - return(1.48 + pow(pMod -> n, 0.5)); + return (1.48 + pow(pMod -> n, 0.5)); } static -double computeNbb(LPcmsCIECAM02 pMod) +cmsFloat64Number computeNbb(cmsCIECAM02* pMod) { - return(0.725 * pow((1.0 / pMod -> n), 0.2)); + return (0.725 * pow((1.0 / pMod -> n), 0.2)); } static -double computeFL(LPcmsCIECAM02 pMod) +cmsFloat64Number computeFL(cmsCIECAM02* pMod) { - double k, FL; + cmsFloat64Number k, FL; k = 1.0 / ((5.0 * pMod->LA) + 1.0); FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 * @@ -121,9 +118,9 @@ } static -double computeD(LPcmsCIECAM02 pMod) +cmsFloat64Number computeD(cmsCIECAM02* pMod) { - double D; + cmsFloat64Number D; D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0))); @@ -142,9 +139,10 @@ } static -CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod) +CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) { - int i; + cmsUInt32Number i; + for (i = 0; i < 3; i++) { clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] * (pMod->D / pMod -> adoptedWhite.RGB[i])) + @@ -156,11 +154,9 @@ static -CAM02COLOR CAT02toHPE (CAM02COLOR clr) +CAM02COLOR CAT02toHPE(CAM02COLOR clr) { - - double M[9]; - + cmsFloat64Number M[9]; M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628)); M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698)); @@ -180,10 +176,10 @@ } static -CAM02COLOR NonlinearCompression(CAM02COLOR clr, LPcmsCIECAM02 pMod) +CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod) { - int i; - double temp; + cmsUInt32Number i; + cmsFloat64Number temp; for (i = 0; i < 3; i++) { if (clr.RGBp[i] < 0) { @@ -204,9 +200,9 @@ } static -CAM02COLOR ComputeCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod) +CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) { - double a, b, temp, e, t, r2d, d2r; + cmsFloat64Number a, b, temp, e, t, r2d, d2r; a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0); b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0; @@ -274,10 +270,10 @@ static -CAM02COLOR InverseCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod) +CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) { - double t, e, p1, p2, p3, p4, p5, hr, d2r; + cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r; d2r = 3.141592654 / 180.0; t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) * @@ -327,10 +323,10 @@ } static -CAM02COLOR InverseNonlinearity(CAM02COLOR clr, LPcmsCIECAM02 pMod) +CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod) { - int i; - double c1; + cmsUInt32Number i; + cmsFloat64Number c1; for (i = 0; i < 3; i++) { if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1; @@ -347,7 +343,7 @@ static CAM02COLOR HPEtoCAT02(CAM02COLOR clr) { - double M[9]; + cmsFloat64Number M[9]; M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950)); M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054)); @@ -362,19 +358,19 @@ clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]); clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]); clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]); - return (clr); + return clr; } static -CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod) +CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) { - int i; + cmsUInt32Number i; for (i = 0; i < 3; i++) { clr.RGB[i] = clr.RGBc[i] / ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D); } - return(clr); + return clr; } @@ -385,23 +381,21 @@ clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098); clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326); - return(clr); + return clr; } - - -LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC) +cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC) { - LPcmsCIECAM02 lpMod; - + cmsCIECAM02* lpMod; - if((lpMod = (LPcmsCIECAM02) _cmsMalloc(sizeof(cmsCIECAM02))) == NULL) { - return (LCMSHANDLE) NULL; + _cmsAssert(pVC != NULL); + + if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) { + return NULL; } - - ZeroMemory(lpMod, sizeof(cmsCIECAM02)); + lpMod ->ContextID = ContextID; lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X; lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y; @@ -414,11 +408,6 @@ switch (lpMod -> surround) { - case AVG_SURROUND_4: - lpMod->F = 1.0; // Not included in CAM02 - lpMod->c = 0.69; - lpMod->Nc = 1.0; - break; case CUTSHEET_SURROUND: lpMod->F = 0.8; @@ -432,7 +421,6 @@ lpMod -> Nc = 0.8; break; - case DIM_SURROUND: lpMod -> F = 0.9; lpMod -> c = 0.59; @@ -451,10 +439,8 @@ lpMod -> Nbb = computeNbb(lpMod); lpMod -> FL = computeFL(lpMod); - if (lpMod -> D == D_CALCULATE || - lpMod -> D == D_CALCULATE_DISCOUNT) { - - lpMod -> D = computeD(lpMod); + if (lpMod -> D == D_CALCULATE) { + lpMod -> D = computeD(lpMod); } lpMod -> Ncb = lpMod -> Nbb; @@ -464,21 +450,28 @@ lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite); lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod); - return (LCMSHANDLE) lpMod; + return (cmsHANDLE) lpMod; } -void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel) +void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel) { - LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel; - if (lpMod) _cmsFree(lpMod); + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + if (lpMod) _cmsFree(lpMod ->ContextID, lpMod); } -void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut) +void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut) { CAM02COLOR clr; - LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel; + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + _cmsAssert(lpMod != NULL); + _cmsAssert(pIn != NULL); + _cmsAssert(pOut != NULL); + + memset(&clr, 0, sizeof(clr)); clr.XYZ[0] = pIn ->X; clr.XYZ[1] = pIn ->Y; @@ -495,11 +488,16 @@ pOut ->h = clr.h; } -void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut) +void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut) { CAM02COLOR clr; - LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel; + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + _cmsAssert(lpMod != NULL); + _cmsAssert(pIn != NULL); + _cmsAssert(pOut != NULL); + + memset(&clr, 0, sizeof(clr)); clr.J = pIn -> J; clr.C = pIn -> C; @@ -514,6 +512,4 @@ pOut ->X = clr.XYZ[0]; pOut ->Y = clr.XYZ[1]; pOut ->Z = clr.XYZ[2]; - } - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmscam97.c --- a/src/share/native/sun/java2d/cmm/lcms/cmscam97.c Wed Jan 31 22:55:12 2018 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,750 +0,0 @@ -/* - * 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -// This file is available under and governed by the GNU General Public -// License version 2 only, as published by the Free Software Foundation. -// However, the following notice accompanied the original version of this -// file: -// -// -// Little cms -// Copyright (C) 1998-2007 Marti Maria -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "lcms.h" - - -/* -typedef struct { - double J; - double C; - double h; - - } cmsJCh, FAR* LPcmsJCh; - - -#define AVG_SURROUND_4 0 -#define AVG_SURROUND 1 -#define DIM_SURROUND 2 -#define DARK_SURROUND 3 -#define CUTSHEET_SURROUND 4 - - -typedef struct { - - cmsCIEXYZ whitePoint; - double Yb; - double La; - int surround; - double D_value; - - } cmsViewingConditions, FAR* LPcmsViewingConditions; - - - -LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC); -LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel); -LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut); -LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut); - -*/ - -// ---------- Implementation -------------------------------------------- - -// #define USE_CIECAM97s2 1 - -#ifdef USE_CIECAM97s2 - -# define NOISE_CONSTANT 3.05 -#else -# define NOISE_CONSTANT 2.05 -#endif - - -/* - The model input data are the adapting field luminance in cd/m2 - (normally taken to be 20% of the luminance of white in the adapting field), - LA , the relative tristimulus values of the stimulus, XYZ, the relative - tristimulus values of white in the same viewing conditions, Xw Yw Zw , - and the relative luminance of the background, Yb . Relative tristimulus - values should be expressed on a scale from Y = 0 for a perfect black - to Y = 100 for a perfect reflecting diffuser. Additionally, the - parameters c, for the impact of surround, Nc , a chromatic induction factor, - and F, a factor for degree of adaptation, must be selected according to the - guidelines in table - - All CIE tristimulus values are obtained using the CIE 1931 - Standard Colorimetric Observer (2°). - -*/ - -typedef struct { - - cmsCIEXYZ WP; - int surround; - int calculate_D; - - double Yb; // rel. luminance of background - - cmsCIEXYZ RefWhite; - - double La; // The adapting field luminance in cd/m2 - - double c; // Impact of surround - double Nc; // Chromatic induction factor - double Fll; // Lightness contrast factor (Removed on rev 2) - double F; // Degree of adaptation - - - double k; - double Fl; - - double Nbb; // The background and chromatic brightness induction factors. - double Ncb; - double z; // base exponential nonlinearity - double n; // background induction factor - double D; - - MAT3 MlamRigg; - MAT3 MlamRigg_1; - - MAT3 Mhunt; - MAT3 Mhunt_1; - - MAT3 Mhunt_x_MlamRigg_1; - MAT3 MlamRigg_x_Mhunt_1; - - - VEC3 RGB_subw; - VEC3 RGB_subw_prime; - - double p; - - VEC3 RGB_subwc; - - VEC3 RGB_subaw_prime; - double A_subw; - double Q_subw; - - } cmsCIECAM97s,FAR *LPcmsCIECAM97s; - - - -// Free model structure - -LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel) -{ - LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel; - if (lpMod) _cmsFree(lpMod); -} - -// Partial discounting for adaptation degree computation - -static -double discount(double d, double chan) -{ - return (d * chan + 1 - d); -} - - -// This routine does model exponential nonlinearity on the short wavelenght -// sensitive channel. On CIECAM97s rev 2 this has been reverted to linear. - -static -void FwAdaptationDegree(LPcmsCIECAM97s lpMod, LPVEC3 RGBc, LPVEC3 RGB) -{ - - -#ifdef USE_CIECAM97s2 - RGBc->n[0] = RGB->n[0]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[0]); - RGBc->n[1] = RGB->n[1]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[1]); - RGBc->n[2] = RGB->n[2]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[2]); -#else - - RGBc->n[0] = RGB->n[0]* discount(lpMod->D, 1.0/lpMod->RGB_subw.n[0]); - RGBc->n[1] = RGB->n[1]* discount(lpMod->D, 1.0/lpMod->RGB_subw.n[1]); - - RGBc->n[2] = pow(fabs(RGB->n[2]), lpMod ->p) * discount(lpMod->D, (1.0/pow(lpMod->RGB_subw.n[2], lpMod->p))); - - // If B happens to be negative, Then Bc is also set to be negative - - if (RGB->n[2] < 0) - RGBc->n[2] = -RGBc->n[2]; -#endif -} - - -static -void RvAdaptationDegree(LPcmsCIECAM97s lpMod, LPVEC3 RGBc, LPVEC3 RGB) -{ - - -#ifdef USE_CIECAM97s2 - RGBc->n[0] = RGB->n[0]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[0]); - RGBc->n[1] = RGB->n[1]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[1]); - RGBc->n[2] = RGB->n[2]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[2]); -#else - - RGBc->n[0] = RGB->n[0]/discount(lpMod->D, 1.0/lpMod->RGB_subw.n[0]); - RGBc->n[1] = RGB->n[1]/discount(lpMod->D, 1.0/lpMod->RGB_subw.n[1]); - RGBc->n[2] = pow(fabs(RGB->n[2]), 1.0/lpMod->p)/pow(discount(lpMod->D, 1.0/pow(lpMod->RGB_subw.n[2], lpMod->p)), 1.0/lpMod->p); - if (RGB->n[2] < 0) - RGBc->n[2] = -RGBc->n[2]; -#endif -} - - - -static -void PostAdaptationConeResponses(LPcmsCIECAM97s lpMod, LPVEC3 RGBa_prime, LPVEC3 RGBprime) -{ - if (RGBprime->n[0]>=0.0) { - - RGBa_prime->n[0]=((40.0*pow(lpMod -> Fl * RGBprime->n[0]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[0]/100.0, 0.73)+2))+1; - } - else - { - RGBa_prime->n[0]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[0])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[0])/100.0, 0.73)+2))+1; - } - - if (RGBprime->n[1]>=0.0) - { - RGBa_prime->n[1]=((40.0*pow(lpMod -> Fl * RGBprime->n[1]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[1]/100.0, 0.73)+2))+1; - } - else - { - RGBa_prime->n[1]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[1])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[1])/100.0, 0.73)+2))+1; - } - - if (RGBprime->n[2]>=0.0) - { - RGBa_prime->n[2]=((40.0*pow(lpMod -> Fl * RGBprime->n[2]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[2]/100.0, 0.73)+2))+1; - } - else - { - RGBa_prime->n[2]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[2])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[2])/100.0, 0.73)+2))+1; - } -} - - -// Compute hue quadrature, eccentricity factor, e - -static -void ComputeHueQuadrature(double h, double* H, double* e) -{ - - -#define IRED 0 -#define IYELLOW 1 -#define IGREEN 2 -#define IBLUE 3 - - double e_tab[] = {0.8, 0.7, 1.0, 1.2}; - double H_tab[] = { 0, 100, 200, 300}; - int p1, p2; - double e1, e2, h1, h2; - - - if (h >= 20.14 && h < 90.0) { // Red - - p1 = IRED; - p2 = IYELLOW; - } - else - if (h >= 90.0 && h < 164.25) { // Yellow - - p1 = IYELLOW; - p2 = IGREEN; - } - else - if (h >= 164.25 && h < 237.53) { // Green - - p1 = IGREEN; - p2 = IBLUE; } - else { // Blue - - p1 = IBLUE; - p2 = IRED; - } - - e1 = e_tab[p1]; e2 = e_tab[p2]; - h1 = H_tab[p1]; h2 = H_tab[p2]; - - - - *e = e1 + ((e2-e1)*(h-h1)/(h2 - h1)); - *H = h1 + (100. * (h - h1) / e1) / ((h - h1)/e1 + (h2 - h) / e2); - -#undef IRED -#undef IYELLOW -#undef IGREEN -#undef IBLUE - -} - - - - - - -LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC) -{ - LPcmsCIECAM97s lpMod; - VEC3 tmp; - - if((lpMod = (LPcmsCIECAM97s) _cmsMalloc(sizeof(cmsCIECAM97s))) == NULL) { - return (LCMSHANDLE) NULL; - } - - - lpMod->WP.X = pVC->whitePoint.X; - lpMod->WP.Y = pVC->whitePoint.Y; - lpMod->WP.Z = pVC->whitePoint.Z; - - lpMod->Yb = pVC->Yb; - lpMod->La = pVC->La; - - lpMod->surround = pVC->surround; - - lpMod->RefWhite.X = 100.0; - lpMod->RefWhite.Y = 100.0; - lpMod->RefWhite.Z = 100.0; - -#ifdef USE_CIECAM97s2 - - VEC3init(&lpMod->MlamRigg.v[0], 0.8562, 0.3372, -0.1934); - VEC3init(&lpMod->MlamRigg.v[1], -0.8360, 1.8327, 0.0033); - VEC3init(&lpMod->MlamRigg.v[2], 0.0357,-0.0469, 1.0112); - - VEC3init(&lpMod->MlamRigg_1.v[0], 0.9874, -0.1768, 0.1894); - VEC3init(&lpMod->MlamRigg_1.v[1], 0.4504, 0.4649, 0.0846); - VEC3init(&lpMod->MlamRigg_1.v[2],-0.0139, 0.0278, 0.9861); - -#else - // Bradford transform: Lam-Rigg cone responses - VEC3init(&lpMod->MlamRigg.v[0], 0.8951, 0.2664, -0.1614); - VEC3init(&lpMod->MlamRigg.v[1], -0.7502, 1.7135, 0.0367); - VEC3init(&lpMod->MlamRigg.v[2], 0.0389, -0.0685, 1.0296); - - - // Inverse of Lam-Rigg - VEC3init(&lpMod->MlamRigg_1.v[0], 0.98699, -0.14705, 0.15996); - VEC3init(&lpMod->MlamRigg_1.v[1], 0.43231, 0.51836, 0.04929); - VEC3init(&lpMod->MlamRigg_1.v[2], -0.00853, 0.04004, 0.96849); - -#endif - - // Hunt-Pointer-Estevez cone responses - VEC3init(&lpMod->Mhunt.v[0], 0.38971, 0.68898, -0.07868); - VEC3init(&lpMod->Mhunt.v[1], -0.22981, 1.18340, 0.04641); - VEC3init(&lpMod->Mhunt.v[2], 0.0, 0.0, 1.0); - - // Inverse of Hunt-Pointer-Estevez - VEC3init(&lpMod->Mhunt_1.v[0], 1.91019, -1.11214, 0.20195); - VEC3init(&lpMod->Mhunt_1.v[1], 0.37095, 0.62905, 0.0); - VEC3init(&lpMod->Mhunt_1.v[2], 0.0, 0.0, 1.0); - - - if (pVC->D_value == -1.0) - lpMod->calculate_D = 1; - else - if (pVC->D_value == -2.0) - lpMod->calculate_D = 2; - else { - lpMod->calculate_D = 0; - lpMod->D = pVC->D_value; - } - - // Table I (revised) - - switch (lpMod->surround) { - - case AVG_SURROUND_4: - lpMod->F = 1.0; - lpMod->c = 0.69; - lpMod->Fll = 0.0; // Not included on Rev 2 - lpMod->Nc = 1.0; - break; - case AVG_SURROUND: - lpMod->F = 1.0; - lpMod->c = 0.69; - lpMod->Fll = 1.0; - lpMod->Nc = 1.0; - break; - case DIM_SURROUND: - lpMod->F = 0.99; - lpMod->c = 0.59; - lpMod->Fll = 1.0; - lpMod->Nc = 0.95; - break; - case DARK_SURROUND: - lpMod->F = 0.9; - lpMod->c = 0.525; - lpMod->Fll = 1.0; - lpMod->Nc = 0.8; - break; - case CUTSHEET_SURROUND: - lpMod->F = 0.9; - lpMod->c = 0.41; - lpMod->Fll = 1.0; - lpMod->Nc = 0.8; - break; - default: - lpMod->F = 1.0; - lpMod->c = 0.69; - lpMod->Fll = 1.0; - lpMod->Nc = 1.0; - break; - } - - lpMod->k = 1 / (5 * lpMod->La + 1); - lpMod->Fl = lpMod->La * pow(lpMod->k, 4) + 0.1*pow(1 - pow(lpMod->k, 4), 2.0) * pow(5*lpMod->La, 1.0/3.0); - - if (lpMod->calculate_D > 0) { - - lpMod->D = lpMod->F * (1 - 1 / (1 + 2*pow(lpMod->La, 0.25) + pow(lpMod->La, 2)/300.0)); - if (lpMod->calculate_D > 1) - lpMod->D = (lpMod->D + 1.0) / 2; - } - - - // RGB_subw = [MlamRigg][WP/YWp] -#ifdef USE_CIECAM97s2 - MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &lpMod -> WP); -#else - VEC3divK(&tmp, (LPVEC3) &lpMod -> WP, lpMod->WP.Y); - MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &tmp); -#endif - - - - MAT3per(&lpMod -> Mhunt_x_MlamRigg_1, &lpMod -> Mhunt, &lpMod->MlamRigg_1 ); - MAT3per(&lpMod -> MlamRigg_x_Mhunt_1, &lpMod -> MlamRigg, &lpMod -> Mhunt_1 ); - - // p is used on forward model - lpMod->p = pow(lpMod->RGB_subw.n[2], 0.0834); - - FwAdaptationDegree(lpMod, &lpMod->RGB_subwc, &lpMod->RGB_subw); - -#if USE_CIECAM97s2 - MAT3eval(&lpMod->RGB_subw_prime, &lpMod->Mhunt_x_MlamRigg_1, &lpMod -> RGB_subwc); -#else - VEC3perK(&tmp, &lpMod -> RGB_subwc, lpMod->WP.Y); - MAT3eval(&lpMod->RGB_subw_prime, &lpMod->Mhunt_x_MlamRigg_1, &tmp); -#endif - - lpMod->n = lpMod-> Yb / lpMod-> WP.Y; - - lpMod->z = 1 + lpMod->Fll * sqrt(lpMod->n); - lpMod->Nbb = lpMod->Ncb = 0.725 / pow(lpMod->n, 0.2); - - PostAdaptationConeResponses(lpMod, &lpMod->RGB_subaw_prime, &lpMod->RGB_subw_prime); - - lpMod->A_subw=lpMod->Nbb*(2.0*lpMod->RGB_subaw_prime.n[0]+lpMod->RGB_subaw_prime.n[1]+lpMod->RGB_subaw_prime.n[2]/20.0-NOISE_CONSTANT); - - return (LCMSHANDLE) lpMod; -} - - - - -// -// The forward model: XYZ -> JCh -// - -LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ inPtr, LPcmsJCh outPtr) -{ - - LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel; - double a, b, h, s, H1val, es, A; - VEC3 In, RGB, RGBc, RGBprime, RGBa_prime; - - if (inPtr -> Y <= 0.0) { - - outPtr -> J = outPtr -> C = outPtr -> h = 0.0; - return; - } - - // An initial chromatic adaptation transform is used to go from the source - // viewing conditions to corresponding colours under the equal-energy-illuminant - // reference viewing conditions. This is handled differently on rev 2 - - VEC3init(&In, inPtr -> X, inPtr -> Y, inPtr -> Z); // 2.1 - -#ifdef USE_CIECAM97s2 - // Since the chromatic adaptation transform has been linearized, it - // is no longer required to divide the stimulus tristimulus values - // by their own Y tristimulus value prior to the chromatic adaptation. -#else - VEC3divK(&In, &In, inPtr -> Y); -#endif - - MAT3eval(&RGB, &lpMod -> MlamRigg, &In); // 2.2 - - FwAdaptationDegree(lpMod, &RGBc, &RGB); - - // The post-adaptation signals for both the sample and the white are then - // transformed from the sharpened cone responses to the Hunt-Pointer-Estevez - // cone responses. -#ifdef USE_CIECAM97s2 -#else - VEC3perK(&RGBc, &RGBc, inPtr->Y); -#endif - - MAT3eval(&RGBprime, &lpMod->Mhunt_x_MlamRigg_1, &RGBc); - - // The post-adaptation cone responses (for both the stimulus and the white) - // are then calculated. - - PostAdaptationConeResponses(lpMod, &RGBa_prime, &RGBprime); - - // Preliminary red-green and yellow-blue opponent dimensions are calculated - - a = RGBa_prime.n[0] - (12.0 * RGBa_prime.n[1] / 11.0) + RGBa_prime.n[2]/11.0; - b = (RGBa_prime.n[0] + RGBa_prime.n[1] - 2.0 * RGBa_prime.n[2]) / 9.0; - - - // The CIECAM97s hue angle, h, is then calculated - h = (180.0/M_PI)*(atan2(b, a)); - - - while (h < 0) - h += 360.0; - - outPtr->h = h; - - // hue quadrature and eccentricity factors, e, are calculated - - ComputeHueQuadrature(h, &H1val, &es); - - // ComputeHueQuadrature(h, &H1val, &h1, &e1, &h2, &e2, &es); - - - // The achromatic response A - A = lpMod->Nbb * (2.0 * RGBa_prime.n[0] + RGBa_prime.n[1] + RGBa_prime.n[2]/20.0 - NOISE_CONSTANT); - - // CIECAM97s Lightness J - outPtr -> J = 100.0 * pow(A / lpMod->A_subw, lpMod->c * lpMod->z); - - // CIECAM97s saturation s - s = (50 * hypot (a, b) * 100 * es * (10.0/13.0) * lpMod-> Nc * lpMod->Ncb) / (RGBa_prime.n[0] + RGBa_prime.n[1] + 1.05 * RGBa_prime.n[2]); - - // CIECAM97s Chroma C - -#ifdef USE_CIECAM97s2 - // Eq. 26 has been modified to allow accurate prediction of the Munsell chroma scales. - outPtr->C = 0.7487 * pow(s, 0.973) * pow(outPtr->J/100.0, 0.945 * lpMod->n) * (1.64 - pow(0.29, lpMod->n)); - -#else - outPtr->C = 2.44 * pow(s, 0.69) * pow(outPtr->J/100.0, 0.67 * lpMod->n) * (1.64 - pow(0.29, lpMod->n)); -#endif -} - - -// -// The reverse model JCh -> XYZ -// - - -LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh inPtr, LPcmsCIEXYZ outPtr) -{ - LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel; - double J, C, h, A, H1val, es, s, a, b; - double tan_h, sec_h; - double R_suba_prime, G_suba_prime, B_suba_prime; - double R_prime, G_prime, B_prime; - double Y_subc, Y_prime, B_term; - VEC3 tmp; - VEC3 RGB_prime, RGB_subc_Y; - VEC3 Y_over_Y_subc_RGB; - VEC3 XYZ_primeprime_over_Y_subc; -#ifdef USE_CIECAM92s2 - VEC3 RGBY; - VEC3 Out; -#endif - - J = inPtr->J; - h = inPtr->h; - C = inPtr->C; - - if (J <= 0) { - - outPtr->X = 0.0; - outPtr->Y = 0.0; - outPtr->Z = 0.0; - return; - } - - - - // (2) From J Obtain A - - A = pow(J/100.0, 1/(lpMod->c * lpMod->z)) * lpMod->A_subw; - - - // (3), (4), (5) Using H Determine h1, h2, e1, e2 - // e1 and h1 are the values of e and h for the unique hue having the - // nearest lower valur of h and e2 and h2 are the values of e and h for - // the unique hue having the nearest higher value of h. - - - ComputeHueQuadrature(h, &H1val, &es); - - // (7) Calculate s - - s = pow(C / (2.44 * pow(J/100.0, 0.67*lpMod->n) * (1.64 - pow(0.29, lpMod->n))) , (1./0.69)); - - - // (8) Calculate a and b. - // NOTE: sqrt(1 + tan^2) == sec(h) - - tan_h = tan ((M_PI/180.)*(h)); - sec_h = sqrt(1 + tan_h * tan_h); - - if ((h > 90) && (h < 270)) - sec_h = -sec_h; - - a = s * ( A/lpMod->Nbb + NOISE_CONSTANT) / ( sec_h * 50000.0 * es * lpMod->Nc * lpMod->Ncb/ 13.0 + - s * (11.0 / 23.0 + (108.0/23.0) * tan_h)); - - b = a * tan_h; - - //(9) Calculate R'a G'a and B'a - - R_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) + (41.0/61.0) * (11.0/23.0) * a + (288.0/61.0) / 23.0 * b; - G_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) - (81.0/61.0) * (11.0/23.0) * a - (261.0/61.0) / 23.0 * b; - B_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) - (20.0/61.0) * (11.0/23.0) * a - (20.0/61.0) * (315.0/23.0) * b; - - // (10) Calculate R', G' and B' - - if ((R_suba_prime - 1) < 0) { - - R_prime = -100.0 * pow((2.0 - 2.0 * R_suba_prime) / - (39.0 + R_suba_prime), 1.0/0.73); - } - else - { - R_prime = 100.0 * pow((2.0 * R_suba_prime - 2.0) / - (41.0 - R_suba_prime), 1.0/0.73); - } - - if ((G_suba_prime - 1) < 0) - { - G_prime = -100.0 * pow((2.0 - 2.0 * G_suba_prime) / - (39.0 + G_suba_prime), 1.0/0.73); - } - else - { - G_prime = 100.0 * pow((2.0 * G_suba_prime - 2.0) / - (41.0 - G_suba_prime), 1.0/0.73); - } - - if ((B_suba_prime - 1) < 0) - { - B_prime = -100.0 * pow((2.0 - 2.0 * B_suba_prime) / - (39.0 + B_suba_prime), 1.0/0.73); - } - else - { - B_prime = 100.0 * pow((2.0 * B_suba_prime - 2.0) / - (41.0 - B_suba_prime), 1.0/0.73); - } - - - // (11) Calculate RcY, GcY and BcY - - VEC3init(&RGB_prime, R_prime, G_prime, B_prime); - VEC3divK(&tmp, &RGB_prime, lpMod -> Fl); - - MAT3eval(&RGB_subc_Y, &lpMod->MlamRigg_x_Mhunt_1, &tmp); - - - - -#ifdef USE_CIECAM97s2 - - // (12) - - - RvAdaptationDegree(lpMod, &RGBY, &RGB_subc_Y); - MAT3eval(&Out, &lpMod->MlamRigg_1, &RGBY); - - outPtr -> X = Out.n[0]; - outPtr -> Y = Out.n[1]; - outPtr -> Z = Out.n[2]; - -#else - - // (12) Calculate Yc - - Y_subc = 0.43231*RGB_subc_Y.n[0]+0.51836*RGB_subc_Y.n[1]+0.04929*RGB_subc_Y.n[2]; - - // (13) Calculate (Y/Yc)R, (Y/Yc)G and (Y/Yc)B - - VEC3divK(&RGB_subc_Y, &RGB_subc_Y, Y_subc); - RvAdaptationDegree(lpMod, &Y_over_Y_subc_RGB, &RGB_subc_Y); - - // (14) Calculate Y' - Y_prime = 0.43231*(Y_over_Y_subc_RGB.n[0]*Y_subc) + 0.51836*(Y_over_Y_subc_RGB.n[1]*Y_subc) + 0.04929 * (Y_over_Y_subc_RGB.n[2]*Y_subc); - - if (Y_prime < 0 || Y_subc < 0) - { - // Discard to near black point - - outPtr -> X = 0; - outPtr -> Y = 0; - outPtr -> Z = 0; - return; - } - - B_term = pow(Y_prime / Y_subc, (1.0 / lpMod->p) - 1); - - // (15) Calculate X'', Y'' and Z'' - Y_over_Y_subc_RGB.n[2] /= B_term; - MAT3eval(&XYZ_primeprime_over_Y_subc, &lpMod->MlamRigg_1, &Y_over_Y_subc_RGB); - - outPtr->X = XYZ_primeprime_over_Y_subc.n[0] * Y_subc; - outPtr->Y = XYZ_primeprime_over_Y_subc.n[1] * Y_subc; - outPtr->Z = XYZ_primeprime_over_Y_subc.n[2] * Y_subc; -#endif - -} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmscgats.c --- a/src/share/native/sun/java2d/cmm/lcms/cmscgats.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmscgats.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -49,107 +50,34 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -// IT8.7 / CGATS.17-200x handling - -#include "lcms.h" - - -LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8Alloc(void); -LCMSAPI void LCMSEXPORT cmsIT8Free(LCMSHANDLE IT8); - -// Tables - -LCMSAPI int LCMSEXPORT cmsIT8TableCount(LCMSHANDLE IT8); -LCMSAPI int LCMSEXPORT cmsIT8SetTable(LCMSHANDLE IT8, int nTable); - -// Persistence -LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8LoadFromFile(const char* cFileName); -LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8LoadFromMem(void *Ptr, size_t len); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SaveToFile(LCMSHANDLE IT8, const char* cFileName); - -// Properties -LCMSAPI const char* LCMSEXPORT cmsIT8GetSheetType(LCMSHANDLE hIT8); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetSheetType(LCMSHANDLE hIT8, const char* Type); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetComment(LCMSHANDLE hIT8, const char* cComment); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyStr(LCMSHANDLE hIT8, const char* cProp, const char *Str); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyHex(LCMSHANDLE hIT8, const char* cProp, int Val); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char* cSubProp, const char *Val); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyUncooked(LCMSHANDLE hIT8, const char* Key, const char* Buffer); - -LCMSAPI const char* LCMSEXPORT cmsIT8GetProperty(LCMSHANDLE hIT8, const char* cProp); -LCMSAPI double LCMSEXPORT cmsIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp); -LCMSAPI const char* LCMSEXPORT cmsIT8GetPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char *cSubProp); -LCMSAPI int LCMSEXPORT cmsIT8EnumProperties(LCMSHANDLE IT8, const char ***PropertyNames); -LCMSAPI int LCMSEXPORT cmsIT8EnumPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char*** SubpropertyNames); - -// Datasets - -LCMSAPI const char* LCMSEXPORT cmsIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer); - -LCMSAPI const char* LCMSEXPORT cmsIT8GetDataRowCol(LCMSHANDLE IT8, int row, int col); -LCMSAPI double LCMSEXPORT cmsIT8GetDataRowColDbl(LCMSHANDLE IT8, int col, int row); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataRowCol(LCMSHANDLE hIT8, int row, int col, - const char* Val); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataRowColDbl(LCMSHANDLE hIT8, int row, int col, - double Val); - -LCMSAPI const char* LCMSEXPORT cmsIT8GetData(LCMSHANDLE IT8, const char* cPatch, const char* cSample); - - -LCMSAPI double LCMSEXPORT cmsIT8GetDataDbl(LCMSHANDLE IT8, const char* cPatch, const char* cSample); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetData(LCMSHANDLE IT8, const char* cPatch, - const char* cSample, - const char *Val); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataDbl(LCMSHANDLE hIT8, const char* cPatch, - const char* cSample, - double Val); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataFormat(LCMSHANDLE IT8, int n, const char *Sample); -LCMSAPI int LCMSEXPORT cmsIT8EnumDataFormat(LCMSHANDLE IT8, char ***SampleNames); - -LCMSAPI void LCMSEXPORT cmsIT8DefineDblFormat(LCMSHANDLE IT8, const char* Formatter); - -LCMSAPI int LCMSEXPORT cmsIT8SetTableByLabel(LCMSHANDLE hIT8, const char* cSet, - const char* cField, - const char* ExpectedType); - -// ------------------------------------------------------------- Implementation - - -#define SIZEOFLONGMINUS1 (sizeof(long)-1) -#define ALIGNLONG(x) (((x)+SIZEOFLONGMINUS1) & ~(SIZEOFLONGMINUS1)) - -// #define STRICT_CGATS 1 - -#define MAXID 128 // Max lenght of identifier -#define MAXSTR 1024 // Max lenght of string -#define MAXTABLES 255 // Max Number of tables in a single stream -#define MAXINCLUDE 20 // Max number of nested includes +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- + + +#define MAXID 128 // Max length of identifier +#define MAXSTR 1024 // Max length of string +#define MAXTABLES 255 // Max Number of tables in a single stream +#define MAXINCLUDE 20 // Max number of nested includes #define DEFAULT_DBL_FORMAT "%.10g" // Double formatting -#include -#include - -#ifndef NON_WINDOWS -#include -#define DIR_CHAR '\\' +#ifdef CMS_IS_WINDOWS_ +# include +# define DIR_CHAR '\\' #else -#define DIR_CHAR '/' +# define DIR_CHAR '/' #endif + // Symbols - typedef enum { - SNONE, + SUNDEFINED, SINUM, // Integer SDNUM, // Real SIDENT, // Identifier @@ -173,8 +101,8 @@ // How to write the value - typedef enum { + WRITE_UNCOOKED, WRITE_STRINGIFY, WRITE_HEXADECIMAL, @@ -184,7 +112,6 @@ } WRITEMODE; // Linked list of variable names - typedef struct _KeyVal { struct _KeyVal* Next; @@ -194,108 +121,103 @@ char* Value; // Points to value WRITEMODE WriteAs; // How to write the value - } KEYVALUE, *LPKEYVALUE; + } KEYVALUE; // Linked list of memory chunks (Memory sink) - typedef struct _OwnedMem { struct _OwnedMem* Next; void * Ptr; // Point to value - } OWNEDMEM, *LPOWNEDMEM; + } OWNEDMEM; // Suballocator - typedef struct _SubAllocator { - LPBYTE Block; - size_t BlockSize; - size_t Used; - - } SUBALLOCATOR, *LPSUBALLOCATOR; + cmsUInt8Number* Block; + cmsUInt32Number BlockSize; + cmsUInt32Number Used; + + } SUBALLOCATOR; // Table. Each individual table can hold properties and rows & cols - typedef struct _Table { + char SheetType[MAXSTR]; // The first row of the IT8 (the type) + int nSamples, nPatches; // Cols, Rows int SampleID; // Pos of ID - LPKEYVALUE HeaderList; // The properties + KEYVALUE* HeaderList; // The properties char** DataFormat; // The binary stream descriptor char** Data; // The binary stream - } TABLE, *LPTABLE; + } TABLE; // File stream being parsed - typedef struct _FileContext { - char FileName[MAX_PATH]; // File name if being readed from file - FILE* Stream; // File stream or NULL if holded in memory - } FILECTX, *LPFILECTX; - -// This struct hold all information about an openened -// IT8 handler. Only one dataset is allowed. - + char FileName[cmsMAX_PATH]; // File name if being readed from file + FILE* Stream; // File stream or NULL if holded in memory + } FILECTX; + +// This struct hold all information about an open IT8 handler. typedef struct { - char SheetType[MAXSTR]; - - int TablesCount; // How many tables in this stream - int nTable; // The actual table + + cmsUInt32Number TablesCount; // How many tables in this stream + cmsUInt32Number nTable; // The actual table TABLE Tab[MAXTABLES]; // Memory management - - LPOWNEDMEM MemorySink; // The storage backend + OWNEDMEM* MemorySink; // The storage backend SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast // Parser state machine - - SYMBOL sy; // Current symbol - int ch; // Current character - - int inum; // integer value - double dnum; // real value + SYMBOL sy; // Current symbol + int ch; // Current character + + cmsInt32Number inum; // integer value + cmsFloat64Number dnum; // real value + char id[MAXID]; // identifier char str[MAXSTR]; // string // Allowed keywords & datasets. They have visibility on whole stream - - LPKEYVALUE ValidKeywords; - LPKEYVALUE ValidSampleID; + KEYVALUE* ValidKeywords; + KEYVALUE* ValidSampleID; char* Source; // Points to loc. being parsed - int lineno; // line counter for error reporting - - LPFILECTX FileStack[MAXINCLUDE]; // Stack of files being parsed - int IncludeSP; // Include Stack Pointer + cmsInt32Number lineno; // line counter for error reporting + + FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed + cmsInt32Number IncludeSP; // Include Stack Pointer char* MemoryBlock; // The stream if holded in memory - char DoubleFormatter[MAXID]; // Printf-like 'double' formatter - - } IT8, *LPIT8; - - - + char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter + + cmsContext ContextID; // The threading context + + } cmsIT8; + + +// The stream for save operations typedef struct { FILE* stream; // For save-to-file behaviour - LPBYTE Base; - LPBYTE Ptr; // For save-to-mem behaviour - size_t Used; - size_t Max; - - } SAVESTREAM, FAR* LPSAVESTREAM; - - -// ------------------------------------------------------ IT8 parsing routines + cmsUInt8Number* Base; + cmsUInt8Number* Ptr; // For save-to-mem behaviour + cmsUInt32Number Used; + cmsUInt32Number Max; + + } SAVESTREAM; + + +// ------------------------------------------------------ cmsIT8 parsing routines // A keyword @@ -309,14 +231,15 @@ // The keyword->symbol translation table. Sorting is required. static const KEYWORD TabKeys[] = { - {"$INCLUDE", SINCLUDE}, - {".INCLUDE", SINCLUDE}, - {"BEGIN_DATA", SBEGIN_DATA }, - {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT }, + {"$INCLUDE", SINCLUDE}, // This is an extension! + {".INCLUDE", SINCLUDE}, // This is an extension! + + {"BEGIN_DATA", SBEGIN_DATA }, + {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT }, {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID}, - {"END_DATA", SEND_DATA}, - {"END_DATA_FORMAT", SEND_DATA_FORMAT}, - {"KEYWORD", SKEYWORD} + {"END_DATA", SEND_DATA}, + {"END_DATA_FORMAT", SEND_DATA_FORMAT}, + {"KEYWORD", SKEYWORD} }; #define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD)) @@ -325,8 +248,8 @@ // A property typedef struct { - const char *id; - WRITEMODE as; + const char *id; // The identifier + WRITEMODE as; // How is supposed to be written } PROPERTY; static PROPERTY PredefinedProperties[] = { @@ -343,64 +266,64 @@ {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm. {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target. - {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code - // uniquely identifying th e material. This is intend ed to be used for IT8.7 - // physical targets only (i.e . IT8.7/1 a nd IT8.7/2). - - {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and - // model number) to generate the data reported. This data will often - // provide more information about the particular data collected than an - // extensive list of specific details. This is particularly important for - // spectral data or data derived from spectrophotometry. - - {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide - // a guide to the potential for issues of paper fluorescence, etc. - - {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported. - // Where standard conditions have been defined (e.g., SWOP at nominal) - // named conditions may suffice. Otherwise, detailed information is - // needed. - - {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during - // measurement. Allowed values are “black”, “white”, or {"na". - - {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic - -// new in recent specs: + {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code + // uniquely identifying th e material. This is intend ed to be used for IT8.7 + // physical targets only (i.e . IT8.7/1 a nd IT8.7/2). + + {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and + // model number) to generate the data reported. This data will often + // provide more information about the particular data collected than an + // extensive list of specific details. This is particularly important for + // spectral data or data derived from spectrophotometry. + + {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide + // a guide to the potential for issues of paper fluorescence, etc. + + {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported. + // Where standard conditions have been defined (e.g., SWOP at nominal) + // named conditions may suffice. Otherwise, detailed information is + // needed. + + {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during + // measurement. Allowed values are “black”, “white”, or {"na". + + {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic + // below properties are new in recent specs: + {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated - // along with details of the geometry and the aperture size and shape. For example, - // for transmission measurements it is important to identify 0/diffuse, diffuse/0, - // opal or integrating sphere, etc. For reflection it is important to identify 0/45, - // 45/0, sphere (specular included or excluded), etc. - - {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to - // denote the use of filters such as none, D65, Red, Green or Blue. - - {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed - // values are {"yes”, “white”, “none” or “na”. - - {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the - // calculation of various data parameters (2 degree and 10 degree), CIE standard - // illuminant functions used in the calculation of various data parameters (e.g., D50, - // D65, etc.), density status response, etc. If used there shall be at least one - // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute - // in the set shall be {"name" and shall identify the particular parameter used. - // The second shall be {"value" and shall provide the value associated with that name. - // For ASCII data, a string containing the Name and Value attribute pairs shall follow - // the weighting function keyword. A semi-colon separates attribute pairs from each - // other and within the attribute the name and value are separated by a comma. - - {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name - // of the calculation, parameter is the name of the parameter used in the calculation - // and value is the value of the parameter. - - {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc. - - {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target. - - {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table. - - {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table. + // along with details of the geometry and the aperture size and shape. For example, + // for transmission measurements it is important to identify 0/diffuse, diffuse/0, + // opal or integrating sphere, etc. For reflection it is important to identify 0/45, + // 45/0, sphere (specular included or excluded), etc. + + {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to + // denote the use of filters such as none, D65, Red, Green or Blue. + + {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed + // values are {"yes”, “white”, “none” or “na”. + + {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the + // calculation of various data parameters (2 degree and 10 degree), CIE standard + // illuminant functions used in the calculation of various data parameters (e.g., D50, + // D65, etc.), density status response, etc. If used there shall be at least one + // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute + // in the set shall be {"name" and shall identify the particular parameter used. + // The second shall be {"value" and shall provide the value associated with that name. + // For ASCII data, a string containing the Name and Value attribute pairs shall follow + // the weighting function keyword. A semi-colon separates attribute pairs from each + // other and within the attribute the name and value are separated by a comma. + + {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name + // of the calculation, parameter is the name of the parameter used in the calculation + // and value is the value of the parameter. + + {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc. + + {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target. + + {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table. + + {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table. }; #define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY)) @@ -430,7 +353,7 @@ "XYZ_X", // X component of tristimulus data "XYZ_Y", // Y component of tristimulus data "XYZ_Z", // Z component of tristimulus data - "XYY_X" // x component of chromaticity data + "XYY_X", // x component of chromaticity data "XYY_Y", // y component of chromaticity data "XYY_CAPY", // Y component of tristimulus data "LAB_L", // L* component of Lab data @@ -438,9 +361,9 @@ "LAB_B", // b* component of Lab data "LAB_C", // C*ab component of Lab data "LAB_H", // hab component of Lab data - "LAB_DE", // CIE dE - "LAB_DE_94", // CIE dE using CIE 94 - "LAB_DE_CMC", // dE using CMC + "LAB_DE", // CIE dE + "LAB_DE_94", // CIE dE using CIE 94 + "LAB_DE_CMC", // dE using CMC "LAB_DE_2000", // CIE dE using CIE DE 2000 "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) @@ -458,84 +381,95 @@ #define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *)) //Forward declaration of some internal functions +static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size); + +// Checks whatever c is a separator static -void* AllocChunk(LPIT8 it8, size_t size); - -// Checks if c is a separator -static -LCMSBOOL isseparator(int c) +cmsBool isseparator(int c) { - return (c == ' ') || (c == '\t') || (c == '\r'); + return (c == ' ') || (c == '\t') ; } -// Checks whatever if c is a valid identifier char +// Checks whatever c is a valid identifier char static -LCMSBOOL ismiddle(int c) +cmsBool ismiddle(int c) { return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127)); } -// Checks whatsever if c is a valid identifier middle char. +// Checks whatsever c is a valid identifier middle char. static -LCMSBOOL isidchar(int c) +cmsBool isidchar(int c) { return isalnum(c) || ismiddle(c); } -// Checks whatsever if c is a valid identifier first char. +// Checks whatsever c is a valid identifier first char. static -LCMSBOOL isfirstidchar(int c) +cmsBool isfirstidchar(int c) { return !isdigit(c) && ismiddle(c); } -// checks whether the supplied path looks like an absolute path -// NOTE: this function doesn't checks if the path exists or even if it's legal +// Guess whether the supplied path looks like an absolute path static -LCMSBOOL isabsolutepath(const char *path) +cmsBool isabsolutepath(const char *path) { + char ThreeChars[4]; + if(path == NULL) return FALSE; - - if(path[0] == DIR_CHAR) + if (path[0] == 0) + return FALSE; + + strncpy(ThreeChars, path, 3); + ThreeChars[3] = 0; + + if(ThreeChars[0] == DIR_CHAR) return TRUE; -#ifndef NON_WINDOWS - if(isalpha(path[0]) && path[1] == ':') +#ifdef CMS_IS_WINDOWS_ + if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':') return TRUE; #endif return FALSE; } + // Makes a file path based on a given reference path -// NOTE: buffer is assumed to point to at least MAX_PATH bytes -// NOTE: both relPath and basePath are assumed to be no more than MAX_PATH characters long (including the null terminator!) // NOTE: this function doesn't check if the path exists or even if it's legal static -LCMSBOOL _cmsMakePath(const char *relPath, const char *basePath, char *buffer) +cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen) { - if (!isabsolutepath(relPath)) { - - char *tail; - - strncpy(buffer, basePath, MAX_PATH-1); - tail = strrchr(buffer, DIR_CHAR); - if (tail != NULL) { - - size_t len = tail - buffer; - strncpy(tail + 1, relPath, MAX_PATH - len -1); - // TODO: if combined path is longer than MAX_PATH, this should return FALSE! - return TRUE; - } + char *tail; + cmsUInt32Number len; + + // Already absolute? + if (isabsolutepath(relPath)) { + + strncpy(buffer, relPath, MaxLen); + buffer[MaxLen-1] = 0; + return TRUE; } - strncpy(buffer, relPath, MAX_PATH - 1); - buffer[MAX_PATH-1] = 0; + + // No, search for last + strncpy(buffer, basePath, MaxLen); + buffer[MaxLen-1] = 0; + + tail = strrchr(buffer, DIR_CHAR); + if (tail == NULL) return FALSE; // Is not absolute and has no separators?? + + len = (cmsUInt32Number) (tail - buffer); + if (len >= MaxLen) return FALSE; + + // No need to assure zero terminator over here + strncpy(tail + 1, relPath, MaxLen - len); + return TRUE; } // Make sure no exploit is being even tried - static const char* NoMeta(const char* str) { @@ -545,40 +479,37 @@ return str; } - // Syntax error static -LCMSBOOL SynError(LPIT8 it8, const char *Txt, ...) +cmsBool SynError(cmsIT8* it8, const char *Txt, ...) { - char Buffer[256], ErrMsg[1024]; - va_list args; - - va_start(args, Txt); - vsnprintf(Buffer, 255, Txt, args); - Buffer[255] = 0; - va_end(args); - - snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer); - ErrMsg[1023] = 0; - it8->sy = SSYNERROR; - cmsSignalError(LCMS_ERRC_ABORTED, "%s", ErrMsg); - return FALSE; + char Buffer[256], ErrMsg[1024]; + va_list args; + + va_start(args, Txt); + vsnprintf(Buffer, 255, Txt, args); + Buffer[255] = 0; + va_end(args); + + snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer); + ErrMsg[1023] = 0; + it8->sy = SSYNERROR; + cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg); + return FALSE; } // Check if current symbol is same as specified. issue an error else. static -LCMSBOOL Check(LPIT8 it8, SYMBOL sy, const char* Err) +cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err) { if (it8 -> sy != sy) return SynError(it8, NoMeta(Err)); return TRUE; } - - // Read Next character from stream static -void NextCh(LPIT8 it8) +void NextCh(cmsIT8* it8) { if (it8 -> FileStack[it8 ->IncludeSP]->Stream) { @@ -594,9 +525,6 @@ } else it8 ->ch = 0; // EOF } - - - } else { it8->ch = *it8->Source; @@ -609,100 +537,180 @@ static SYMBOL BinSrchKey(const char *id) { - int l = 1; - int r = NUMKEYS; - int x, res; - - while (r >= l) - { - x = (l+r)/2; - res = stricmp(id, TabKeys[x-1].id); - if (res == 0) return TabKeys[x-1].sy; - if (res < 0) r = x - 1; - else l = x + 1; - } - - return SNONE; + int l = 1; + int r = NUMKEYS; + int x, res; + + while (r >= l) + { + x = (l+r)/2; + res = cmsstrcasecmp(id, TabKeys[x-1].id); + if (res == 0) return TabKeys[x-1].sy; + if (res < 0) r = x - 1; + else l = x + 1; + } + + return SUNDEFINED; } // 10 ^n static -double xpow10(int n) +cmsFloat64Number xpow10(int n) { - return pow(10, (double) n); + return pow(10, (cmsFloat64Number) n); } // Reads a Real number, tries to follow from integer number static -void ReadReal(LPIT8 it8, int inum) +void ReadReal(cmsIT8* it8, cmsInt32Number inum) { - it8->dnum = (double) inum; + it8->dnum = (cmsFloat64Number)inum; + + while (isdigit(it8->ch)) { + + it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0'); + NextCh(it8); + } + + if (it8->ch == '.') { // Decimal point + + cmsFloat64Number frac = 0.0; // fraction + int prec = 0; // precision + + NextCh(it8); // Eats dec. point while (isdigit(it8->ch)) { - it8->dnum = it8->dnum * 10.0 + (it8->ch - '0'); - NextCh(it8); + frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0'); + prec++; + NextCh(it8); } - if (it8->ch == '.') { // Decimal point - - double frac = 0.0; // fraction - int prec = 0; // precission - - NextCh(it8); // Eats dec. point - - while (isdigit(it8->ch)) { - - frac = frac * 10.0 + (it8->ch - '0'); - prec++; - NextCh(it8); - } - - it8->dnum = it8->dnum + (frac / xpow10(prec)); + it8->dnum = it8->dnum + (frac / xpow10(prec)); + } + + // Exponent, example 34.00E+20 + if (toupper(it8->ch) == 'E') { + + cmsInt32Number e; + cmsInt32Number sgn; + + NextCh(it8); sgn = 1; + + if (it8->ch == '-') { + + sgn = -1; NextCh(it8); + } + else + if (it8->ch == '+') { + + sgn = +1; + NextCh(it8); + } + + e = 0; + while (isdigit(it8->ch)) { + + cmsInt32Number digit = (it8->ch - '0'); + + if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0) + e = e * 10 + digit; + + NextCh(it8); } - // Exponent, example 34.00E+20 - if (toupper(it8->ch) == 'E') { - - int e; - int sgn; - - NextCh(it8); sgn = 1; - - if (it8->ch == '-') { - - sgn = -1; NextCh(it8); - } - else - if (it8->ch == '+') { - - sgn = +1; - NextCh(it8); - } - - - e = 0; - while (isdigit(it8->ch)) { - - if ((double) e * 10L < INT_MAX) - e = e * 10 + (it8->ch - '0'); - - NextCh(it8); - } - - e = sgn*e; - - it8 -> dnum = it8 -> dnum * xpow10(e); + e = sgn*e; + it8->dnum = it8->dnum * xpow10(e); + } +} + +// Parses a float number +// This can not call directly atof because it uses locale dependent +// parsing, while CCMX files always use . as decimal separator +static +cmsFloat64Number ParseFloatNumber(const char *Buffer) +{ + cmsFloat64Number dnum = 0.0; + int sign = 1; + + // keep safe + if (Buffer == NULL) return 0.0; + + if (*Buffer == '-' || *Buffer == '+') { + + sign = (*Buffer == '-') ? -1 : 1; + Buffer++; + } + + + while (*Buffer && isdigit((int)*Buffer)) { + + dnum = dnum * 10.0 + (*Buffer - '0'); + if (*Buffer) Buffer++; + } + + if (*Buffer == '.') { + + cmsFloat64Number frac = 0.0; // fraction + int prec = 0; // precision + + if (*Buffer) Buffer++; + + while (*Buffer && isdigit((int)*Buffer)) { + + frac = frac * 10.0 + (*Buffer - '0'); + prec++; + if (*Buffer) Buffer++; } + + dnum = dnum + (frac / xpow10(prec)); + } + + // Exponent, example 34.00E+20 + if (*Buffer && toupper(*Buffer) == 'E') { + + int e; + int sgn; + + if (*Buffer) Buffer++; + sgn = 1; + + if (*Buffer == '-') { + + sgn = -1; + if (*Buffer) Buffer++; + } + else + if (*Buffer == '+') { + + sgn = +1; + if (*Buffer) Buffer++; + } + + e = 0; + while (*Buffer && isdigit((int)*Buffer)) { + + cmsInt32Number digit = (*Buffer - '0'); + + if ((cmsFloat64Number)e * 10.0 + digit < (cmsFloat64Number)+2147483647.0) + e = e * 10 + digit; + + if (*Buffer) Buffer++; + } + + e = sgn*e; + dnum = dnum * xpow10(e); + } + + return sign * dnum; } - // Reads next symbol static -void InSymbol(LPIT8 it8) +void InSymbol(cmsIT8* it8) { register char *idptr; register int k; @@ -716,7 +724,6 @@ if (isfirstidchar(it8->ch)) { // Identifier - k = 0; idptr = it8->id; @@ -732,7 +739,7 @@ key = BinSrchKey(it8->id); - if (key == SNONE) it8->sy = SIDENT; + if (key == SUNDEFINED) it8->sy = SIDENT; else it8->sy = key; } @@ -763,7 +770,7 @@ if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10; else j = it8->ch - '0'; - if ((long) it8->inum * 16L > (long) INT_MAX) + if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0) { SynError(it8, "Invalid hexadecimal number"); return; @@ -784,7 +791,7 @@ { j = it8->ch - '0'; - if ((long) it8->inum * 2L > (long) INT_MAX) + if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0) { SynError(it8, "Invalid binary number"); return; @@ -800,14 +807,16 @@ while (isdigit(it8->ch)) { - if ((long) it8->inum * 10L > (long) INT_MAX) { + cmsInt32Number digit = (it8->ch - '0'); + + if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) { ReadReal(it8, it8->inum); it8->sy = SDNUM; it8->dnum *= sign; return; } - it8->inum = it8->inum * 10 + (it8->ch - '0'); + it8->inum = it8->inum * 10 + digit; NextCh(it8); } @@ -827,11 +836,11 @@ if (it8 ->sy == SINUM) { - sprintf(it8->id, "%d", it8->inum); + snprintf(it8->id, 127, "%d", it8->inum); } else { - sprintf(it8->id, it8 ->DoubleFormatter, it8->dnum); + snprintf(it8->id, 127, it8 ->DoubleFormatter, it8->dnum); } k = (int) strlen(it8 ->id); @@ -845,7 +854,6 @@ } while (isidchar(it8->ch)); *idptr = '\0'; - it8->sy = SIDENT; } return; @@ -860,7 +868,6 @@ break; // Eof stream markers - case 0: case -1: it8->sy = SEOF; @@ -868,6 +875,13 @@ // Next line + case '\r': + NextCh(it8); + if (it8 ->ch == '\n') + NextCh(it8); + it8->sy = SEOLN; + it8->lineno++; + break; case '\n': NextCh(it8); @@ -876,17 +890,15 @@ break; // Comment - case '#': NextCh(it8); - while (it8->ch && it8->ch != '\n') + while (it8->ch && it8->ch != '\n' && it8->ch != '\r') NextCh(it8); it8->sy = SCOMMENT; break; - // String. - + // String. case '\'': case '\"': idptr = it8->str; @@ -894,7 +906,7 @@ k = 0; NextCh(it8); - while (k < MAXSTR && it8->ch != sng) { + while (k < (MAXSTR-1) && it8->ch != sng) { if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1; else { @@ -921,10 +933,10 @@ if (it8 -> sy == SINCLUDE) { - LPFILECTX FileNest; - - if(it8 -> IncludeSP >= (MAXINCLUDE-1)) - { + FILECTX* FileNest; + + if(it8 -> IncludeSP >= (MAXINCLUDE-1)) { + SynError(it8, "Too many recursion levels"); return; } @@ -933,15 +945,16 @@ if (!Check(it8, SSTRING, "Filename expected")) return; FileNest = it8 -> FileStack[it8 -> IncludeSP + 1]; - if(FileNest == NULL) - { - FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (LPFILECTX)AllocChunk(it8, sizeof(FILECTX)); + if(FileNest == NULL) { + + FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); //if(FileNest == NULL) - // TODO: how to manage out-of-memory conditions? + // TODO: how to manage out-of-memory conditions? } - if(_cmsMakePath(it8->str, it8->FileStack[it8->IncludeSP]->FileName, FileNest->FileName) == FALSE) - { + if (BuildAbsolutePath(it8->str, + it8->FileStack[it8->IncludeSP]->FileName, + FileNest->FileName, cmsMAX_PATH-1) == FALSE) { SynError(it8, "File path too long"); return; } @@ -962,7 +975,7 @@ // Checks end of line separator static -LCMSBOOL CheckEOLN(LPIT8 it8) +cmsBool CheckEOLN(cmsIT8* it8) { if (!Check(it8, SEOLN, "Expected separator")) return FALSE; while (it8 -> sy == SEOLN) @@ -974,7 +987,7 @@ // Skip a symbol static -void Skip(LPIT8 it8, SYMBOL sy) +void Skip(cmsIT8* it8, SYMBOL sy) { if (it8->sy == sy && it8->sy != SEOF) InSymbol(it8); @@ -983,7 +996,7 @@ // Skip multiple EOLN static -void SkipEOLN(LPIT8 it8) +void SkipEOLN(cmsIT8* it8) { while (it8->sy == SEOLN) { InSymbol(it8); @@ -993,10 +1006,13 @@ // Returns a string holding current value static -LCMSBOOL GetVal(LPIT8 it8, char* Buffer, size_t max, const char* ErrorTitle) +cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle) { switch (it8->sy) { + case SEOLN: // Empty value + Buffer[0]=0; + break; case SIDENT: strncpy(Buffer, it8->id, max); Buffer[max-1]=0; break; @@ -1018,9 +1034,9 @@ // ---------------------------------------------------------- Table static -LPTABLE GetTable(LPIT8 it8) +TABLE* GetTable(cmsIT8* it8) { - if ((it8 -> nTable >= it8 ->TablesCount) || (it8 -> nTable < 0)) { + if ((it8 -> nTable >= it8 ->TablesCount)) { SynError(it8, "Table %d out of sequence", it8 -> nTable); return it8 -> Tab; @@ -1032,75 +1048,70 @@ // ---------------------------------------------------------- Memory management - // Frees an allocator and owned memory -void LCMSEXPORT cmsIT8Free(LCMSHANDLE hIT8) +void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; if (it8 == NULL) return; - if (it8->MemorySink) { - LPOWNEDMEM p; - LPOWNEDMEM n; + OWNEDMEM* p; + OWNEDMEM* n; for (p = it8->MemorySink; p != NULL; p = n) { n = p->Next; - if (p->Ptr) _cmsFree(p->Ptr); - _cmsFree(p); + if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr); + _cmsFree(it8 ->ContextID, p); } } if (it8->MemoryBlock) - _cmsFree(it8->MemoryBlock); - - _cmsFree(it8); + _cmsFree(it8 ->ContextID, it8->MemoryBlock); + + _cmsFree(it8 ->ContextID, it8); } // Allocates a chunk of data, keep linked list static -void* AllocBigBlock(LPIT8 it8, size_t size) +void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size) { - LPOWNEDMEM ptr1; - void* ptr = _cmsMalloc(size); - - if (ptr) { - - ZeroMemory(ptr, size); - ptr1 = (LPOWNEDMEM) _cmsMalloc(sizeof(OWNEDMEM)); - - if (ptr1 == NULL) { - - _cmsFree(ptr); - return NULL; - } - - ZeroMemory(ptr1, sizeof(OWNEDMEM)); - - ptr1-> Ptr = ptr; - ptr1-> Next = it8 -> MemorySink; - it8 -> MemorySink = ptr1; + OWNEDMEM* ptr1; + void* ptr = _cmsMallocZero(it8->ContextID, size); + + if (ptr != NULL) { + + ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM)); + + if (ptr1 == NULL) { + + _cmsFree(it8 ->ContextID, ptr); + return NULL; } - return ptr; + ptr1-> Ptr = ptr; + ptr1-> Next = it8 -> MemorySink; + it8 -> MemorySink = ptr1; + } + + return ptr; } // Suballocator. static -void* AllocChunk(LPIT8 it8, size_t size) +void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) { - size_t free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used; - LPBYTE ptr; - - size = ALIGNLONG(size); - - if (size > free) { + cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used; + cmsUInt8Number* ptr; + + size = _cmsALIGNMEM(size); + + if (size > Free) { if (it8 -> Allocator.BlockSize == 0) @@ -1112,7 +1123,7 @@ it8 ->Allocator.BlockSize = size; it8 ->Allocator.Used = 0; - it8 ->Allocator.Block = (LPBYTE) AllocBigBlock(it8, it8 ->Allocator.BlockSize); + it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize); } ptr = it8 ->Allocator.Block + it8 ->Allocator.Used; @@ -1125,9 +1136,9 @@ // Allocates a string static -char *AllocString(LPIT8 it8, const char* str) +char *AllocString(cmsIT8* it8, const char* str) { - size_t Size = strlen(str)+1; + cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1; char *ptr; @@ -1140,7 +1151,7 @@ // Searches through linked list static -LCMSBOOL IsAvailableOnList(LPKEYVALUE p, const char* Key, const char* Subkey, LPKEYVALUE* LastPtr) +cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr) { if (LastPtr) *LastPtr = p; @@ -1150,10 +1161,10 @@ if (*Key != '#') { // Comments are ignored - if (stricmp(Key, p->Keyword) == 0) - break; + if (cmsstrcasecmp(Key, p->Keyword) == 0) + break; } - } + } if (p == NULL) return FALSE; @@ -1163,9 +1174,11 @@ for (; p != NULL; p = p->NextSubkey) { + if (p ->Subkey == NULL) continue; + if (LastPtr) *LastPtr = p; - if (stricmp(Subkey, p->Subkey) == 0) + if (cmsstrcasecmp(Subkey, p->Subkey) == 0) return TRUE; } @@ -1176,55 +1189,62 @@ // Add a property into a linked list static -LPKEYVALUE AddToList(LPIT8 it8, LPKEYVALUE* Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs) +KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs) { - LPKEYVALUE p; - - // Check if property is already in list (this is an error) + KEYVALUE* p; + KEYVALUE* last; + + + // Check if property is already in list if (IsAvailableOnList(*Head, Key, Subkey, &p)) { - // This may work for editing properties + // This may work for editing properties // return SynError(it8, "duplicate key <%s>", Key); } else { - LPKEYVALUE last = p; - - // Allocate the container - p = (LPKEYVALUE) AllocChunk(it8, sizeof(KEYVALUE)); - if (p == NULL) - { + + last = p; + + // Allocate the container + p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE)); + if (p == NULL) + { SynError(it8, "AddToList: out of memory"); return NULL; - } - - // Store name and value - p->Keyword = AllocString(it8, Key); + } + + // Store name and value + p->Keyword = AllocString(it8, Key); p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey); // Keep the container in our list - if (*Head == NULL) + if (*Head == NULL) { *Head = p; + } else { - if(Subkey != 0 && last != 0) { + if (Subkey != NULL && last != NULL) { + last->NextSubkey = p; // If Subkey is not null, then last is the last property with the same key, // but not necessarily is the last property in the list, so we need to move // to the actual list end - while(last->Next != 0) - last = last->Next; - } - last->Next = p; - } - - p->Next = NULL; + while (last->Next != NULL) + last = last->Next; + } + + if (last != NULL) last->Next = p; + } + + p->Next = NULL; p->NextSubkey = NULL; } p->WriteAs = WriteAs; + if (xValue != NULL) { p->Value = AllocString(it8, xValue); @@ -1237,23 +1257,23 @@ } static -LPKEYVALUE AddAvailableProperty(LPIT8 it8, const char* Key, WRITEMODE as) +KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as) { - return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as); + return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as); } static -LPKEYVALUE AddAvailableSampleID(LPIT8 it8, const char* Key) +KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key) { - return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED); + return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED); } static -void AllocTable(LPIT8 it8) +void AllocTable(cmsIT8* it8) { - LPTABLE t; + TABLE* t; t = it8 ->Tab + it8 ->TablesCount; @@ -1265,9 +1285,9 @@ } -int LCMSEXPORT cmsIT8SetTable(LCMSHANDLE IT8, int nTable) +cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable) { - LPIT8 it8 = (LPIT8) IT8; + cmsIT8* it8 = (cmsIT8*) IT8; if (nTable >= it8 ->TablesCount) { @@ -1283,22 +1303,20 @@ it8 ->nTable = nTable; - return nTable; + return (cmsInt32Number) nTable; } // Init an empty container -LCMSHANDLE LCMSEXPORT cmsIT8Alloc(void) +cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID) { - LPIT8 it8; - int i; - - it8 = (LPIT8) malloc(sizeof(IT8)); + cmsIT8* it8; + cmsUInt32Number i; + + it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8)); if (it8 == NULL) return NULL; - ZeroMemory(it8, sizeof(IT8)); - AllocTable(it8); it8->MemoryBlock = NULL; @@ -1306,6 +1324,7 @@ it8 ->nTable = 0; + it8->ContextID = ContextID; it8->Allocator.Used = 0; it8->Allocator.Block = NULL; it8->Allocator.BlockSize = 0; @@ -1313,18 +1332,18 @@ it8->ValidKeywords = NULL; it8->ValidSampleID = NULL; - it8 -> sy = SNONE; + it8 -> sy = SUNDEFINED; it8 -> ch = ' '; it8 -> Source = NULL; it8 -> inum = 0; it8 -> dnum = 0.0; - it8->FileStack[0] = (LPFILECTX)AllocChunk(it8, sizeof(FILECTX)); + it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); it8->IncludeSP = 0; it8 -> lineno = 1; strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); - strcpy(it8->SheetType, "CGATS.17"); + cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17"); // Initialize predefined properties & data @@ -1335,30 +1354,27 @@ AddAvailableSampleID(it8, PredefinedSampleID[i]); - return (LCMSHANDLE) it8; + return (cmsHANDLE) it8; } -const char* LCMSEXPORT cmsIT8GetSheetType(LCMSHANDLE hIT8) +const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8) { - LPIT8 it8 = (LPIT8) hIT8; - - return it8 ->SheetType; - + return GetTable((cmsIT8*) hIT8)->SheetType; } -LCMSBOOL LCMSEXPORT cmsIT8SetSheetType(LCMSHANDLE hIT8, const char* Type) +cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type) { - LPIT8 it8 = (LPIT8) hIT8; - - strncpy(it8 ->SheetType, Type, MAXSTR-1); - it8 ->SheetType[MAXSTR-1] = 0; + TABLE* t = GetTable((cmsIT8*) hIT8); + + strncpy(t ->SheetType, Type, MAXSTR-1); + t ->SheetType[MAXSTR-1] = 0; return TRUE; } -LCMSBOOL LCMSEXPORT cmsIT8SetComment(LCMSHANDLE hIT8, const char* Val) +cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; if (!Val) return FALSE; if (!*Val) return FALSE; @@ -1366,12 +1382,10 @@ return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL; } - - // Sets a property -LCMSBOOL LCMSEXPORT cmsIT8SetPropertyStr(LCMSHANDLE hIT8, const char* Key, const char *Val) +cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; if (!Val) return FALSE; if (!*Val) return FALSE; @@ -1379,46 +1393,45 @@ return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL; } - -LCMSBOOL LCMSEXPORT cmsIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val) +cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; char Buffer[1024]; - sprintf(Buffer, it8->DoubleFormatter, Val); + snprintf(Buffer, 1023, it8->DoubleFormatter, Val); return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL; } -LCMSBOOL LCMSEXPORT cmsIT8SetPropertyHex(LCMSHANDLE hIT8, const char* cProp, int Val) +cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; char Buffer[1024]; - sprintf(Buffer, "%d", Val); + snprintf(Buffer, 1023, "%u", Val); return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL; } -LCMSBOOL LCMSEXPORT cmsIT8SetPropertyUncooked(LCMSHANDLE hIT8, const char* Key, const char* Buffer) +cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL; } -LCMSBOOL LCMSEXPORT cmsIT8SetPropertyMulti(LCMSHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer) +cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL; } // Gets a property -const char* LCMSEXPORT cmsIT8GetProperty(LCMSHANDLE hIT8, const char* Key) +const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key) { - LPIT8 it8 = (LPIT8) hIT8; - LPKEYVALUE p; + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p)) { @@ -1428,21 +1441,21 @@ } -double LCMSEXPORT cmsIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp) +cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp) { const char *v = cmsIT8GetProperty(hIT8, cProp); - if (v) return atof(v); - else return 0.0; + if (v == NULL) return 0.0; + + return ParseFloatNumber(v); } -const char* LCMSEXPORT cmsIT8GetPropertyMulti(LCMSHANDLE hIT8, const char* Key, const char *SubKey) +const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey) { - LPIT8 it8 = (LPIT8) hIT8; - LPKEYVALUE p; - - if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) - { + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + + if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) { return p -> Value; } return NULL; @@ -1452,9 +1465,9 @@ static -void AllocateDataFormat(LPIT8 it8) +void AllocateDataFormat(cmsIT8* it8) { - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); if (t -> DataFormat) return; // Already allocated @@ -1466,18 +1479,18 @@ t -> nSamples = 10; } - t -> DataFormat = (char**) AllocChunk (it8, (t->nSamples + 1) * sizeof(char *)); - if (t->DataFormat == NULL) - { + t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *)); + if (t->DataFormat == NULL) { + SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array"); } } static -const char *GetDataFormat(LPIT8 it8, int n) +const char *GetDataFormat(cmsIT8* it8, int n) { - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); if (t->DataFormat) return t->DataFormat[n]; @@ -1486,16 +1499,9 @@ } static -LCMSBOOL SetDataFormat(LPIT8 it8, int n, const char *label) +cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) { - LPTABLE t = GetTable(it8); - -#ifdef STRICT_CGATS - if (!IsAvailableOnList(it8-> ValidSampleID, label, NULL, NULL)) { - SynError(it8, "Invalid data format '%s'.", label); - return FALSE; - } -#endif + TABLE* t = GetTable(it8); if (!t->DataFormat) AllocateDataFormat(it8); @@ -1505,7 +1511,6 @@ return FALSE; } - if (t->DataFormat) { t->DataFormat[n] = AllocString(it8, label); } @@ -1514,37 +1519,36 @@ } -LCMSBOOL LCMSEXPORT cmsIT8SetDataFormat(LCMSHANDLE h, int n, const char *Sample) +cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) { - LPIT8 it8 = (LPIT8) h; - return SetDataFormat(it8, n, Sample); + cmsIT8* it8 = (cmsIT8*)h; + return SetDataFormat(it8, n, Sample); } static -void AllocateDataSet(LPIT8 it8) +void AllocateDataSet(cmsIT8* it8) { - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); if (t -> Data) return; // Already allocated t-> nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); t-> nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); - t-> Data = (char**)AllocChunk (it8, (t->nSamples + 1) * (t->nPatches + 1) *sizeof (char*)); - if (t->Data == NULL) - { + t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*)); + if (t->Data == NULL) { + SynError(it8, "AllocateDataSet: Unable to allocate data array"); } } static -char* GetData(LPIT8 it8, int nSet, int nField) +char* GetData(cmsIT8* it8, int nSet, int nField) { - LPTABLE t = GetTable(it8); - int nSamples = t -> nSamples; - int nPatches = t -> nPatches; - + TABLE* t = GetTable(it8); + int nSamples = t -> nSamples; + int nPatches = t -> nPatches; if (nSet >= nPatches || nField >= nSamples) return NULL; @@ -1554,17 +1558,15 @@ } static -LCMSBOOL SetData(LPIT8 it8, int nSet, int nField, const char *Val) +cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val) { - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); if (!t->Data) AllocateDataSet(it8); if (!t->Data) return FALSE; - - if (nSet > t -> nPatches || nSet < 0) { return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches); @@ -1575,7 +1577,6 @@ } - t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val); return TRUE; } @@ -1586,38 +1587,38 @@ // Writes a string to file static -void WriteStr(LPSAVESTREAM f, const char *str) +void WriteStr(SAVESTREAM* f, const char *str) { - - size_t len; + cmsUInt32Number len; if (str == NULL) str = " "; - // Lenghth to write - len = strlen(str); + // Length to write + len = (cmsUInt32Number) strlen(str); f ->Used += len; if (f ->stream) { // Should I write it to a file? - fwrite(str, 1, len, f->stream); + if (fwrite(str, 1, len, f->stream) != len) { + cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser"); + return; + } } else { // Or to a memory block? - if (f ->Base) { // Am I just counting the bytes? if (f ->Used > f ->Max) { - cmsSignalError(LCMS_ERRC_ABORTED, "Write to memory overflows in CGATS parser"); - return; + cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser"); + return; } - CopyMemory(f ->Ptr, str, len); + memmove(f ->Ptr, str, len); f->Ptr += len; - } } @@ -1627,7 +1628,7 @@ // Write formatted static -void Writef(LPSAVESTREAM f, const char* frm, ...) +void Writef(SAVESTREAM* f, const char* frm, ...) { char Buffer[4096]; va_list args; @@ -1642,11 +1643,14 @@ // Writes full header static -void WriteHeader(LPIT8 it8, LPSAVESTREAM fp) +void WriteHeader(cmsIT8* it8, SAVESTREAM* fp) { - LPKEYVALUE p; - LPTABLE t = GetTable(it8); - + KEYVALUE* p; + TABLE* t = GetTable(it8); + + // Writes the type + WriteStr(fp, t->SheetType); + WriteStr(fp, "\n"); for (p = t->HeaderList; (p != NULL); p = p->Next) { @@ -1672,14 +1676,13 @@ if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) { -#ifdef STRICT_CGATS +#ifdef CMS_STRICT_CGATS WriteStr(fp, "KEYWORD\t\""); WriteStr(fp, p->Keyword); WriteStr(fp, "\"\n"); #endif AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED); - } WriteStr(fp, p->Keyword); @@ -1720,10 +1723,10 @@ // Writes the data format static -void WriteDataFormat(LPSAVESTREAM fp, LPIT8 it8) +void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8) { int i, nSamples; - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); if (!t -> DataFormat) return; @@ -1743,10 +1746,10 @@ // Writes data array static -void WriteData(LPSAVESTREAM fp, LPIT8 it8) +void WriteData(SAVESTREAM* fp, cmsIT8* it8) { int i, j; - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); if (!t->Data) return; @@ -1785,19 +1788,17 @@ // Saves whole file -LCMSBOOL LCMSEXPORT cmsIT8SaveToFile(LCMSHANDLE hIT8, const char* cFileName) +cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName) { SAVESTREAM sd; - int i; - LPIT8 it8 = (LPIT8) hIT8; - - ZeroMemory(&sd, sizeof(SAVESTREAM)); + cmsUInt32Number i; + cmsIT8* it8 = (cmsIT8*) hIT8; + + memset(&sd, 0, sizeof(sd)); sd.stream = fopen(cFileName, "wt"); if (!sd.stream) return FALSE; - WriteStr(&sd, it8->SheetType); - WriteStr(&sd, "\n"); for (i=0; i < it8 ->TablesCount; i++) { cmsIT8SetTable(hIT8, i); @@ -1806,23 +1807,23 @@ WriteData(&sd, it8); } - fclose(sd.stream); + if (fclose(sd.stream) != 0) return FALSE; return TRUE; } // Saves to memory -LCMSBOOL LCMSEXPORT cmsIT8SaveToMem(LCMSHANDLE hIT8, void *MemPtr, size_t* BytesNeeded) +cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded) { SAVESTREAM sd; - int i; - LPIT8 it8 = (LPIT8) hIT8; - - ZeroMemory(&sd, sizeof(SAVESTREAM)); + cmsUInt32Number i; + cmsIT8* it8 = (cmsIT8*) hIT8; + + memset(&sd, 0, sizeof(sd)); sd.stream = NULL; - sd.Base = (LPBYTE) MemPtr; + sd.Base = (cmsUInt8Number*) MemPtr; sd.Ptr = sd.Base; sd.Used = 0; @@ -1832,20 +1833,18 @@ else sd.Max = 0; // Just counting the needed bytes - WriteStr(&sd, it8->SheetType); - WriteStr(&sd, "\n"); for (i=0; i < it8 ->TablesCount; i++) { - cmsIT8SetTable(hIT8, i); - WriteHeader(it8, &sd); - WriteDataFormat(&sd, it8); - WriteData(&sd, it8); + cmsIT8SetTable(hIT8, i); + WriteHeader(it8, &sd); + WriteDataFormat(&sd, it8); + WriteData(&sd, it8); } sd.Used++; // The \0 at the very end if (sd.Base) - sd.Ptr = 0; + *sd.Ptr = 0; *BytesNeeded = sd.Used; @@ -1853,13 +1852,13 @@ } -// -------------------------------------------------------------- Higer level parsing +// -------------------------------------------------------------- Higher level parsing static -LCMSBOOL DataFormatSection(LPIT8 it8) +cmsBool DataFormatSection(cmsIT8* it8) { int iField = 0; - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); InSymbol(it8); // Eats "BEGIN_DATA_FORMAT" CheckEOLN(it8); @@ -1897,12 +1896,12 @@ static -LCMSBOOL DataSection (LPIT8 it8) +cmsBool DataSection (cmsIT8* it8) { int iField = 0; int iSet = 0; - char Buffer[MAXSTR]; - LPTABLE t = GetTable(it8); + char Buffer[256]; + TABLE* t = GetTable(it8); InSymbol(it8); // Eats "BEGIN_DATA" CheckEOLN(it8); @@ -1949,11 +1948,11 @@ static -LCMSBOOL HeaderSection(LPIT8 it8) +cmsBool HeaderSection(cmsIT8* it8) { char VarName[MAXID]; char Buffer[MAXSTR]; - LPKEYVALUE Key; + KEYVALUE* Key; while (it8->sy != SEOF && it8->sy != SSYNERROR && @@ -1980,67 +1979,67 @@ case SIDENT: - strncpy(VarName, it8->id, MAXID-1); - VarName[MAXID-1] = 0; - - if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL, &Key)) { - -#ifdef STRICT_CGATS - return SynError(it8, "Undefined keyword '%s'", VarName); + strncpy(VarName, it8->id, MAXID - 1); + VarName[MAXID - 1] = 0; + + if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) { + +#ifdef CMS_STRICT_CGATS + return SynError(it8, "Undefined keyword '%s'", VarName); #else - Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED); - if (Key == NULL) return FALSE; + Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED); + if (Key == NULL) return FALSE; #endif - } - - InSymbol(it8); - if (!GetVal(it8, Buffer, MAXSTR-1, "Property data expected")) return FALSE; - - if(Key->WriteAs != WRITE_PAIR) { - AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer, - (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED); + } + + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE; + + if (Key->WriteAs != WRITE_PAIR) { + AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer, + (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED); + } + else { + const char *Subkey; + char *Nextkey; + if (it8->sy != SSTRING) + return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName); + + // chop the string as a list of "subkey, value" pairs, using ';' as a separator + for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey) + { + char *Value, *temp; + + // identify token pair boundary + Nextkey = (char*)strchr(Subkey, ';'); + if (Nextkey) + *Nextkey++ = '\0'; + + // for each pair, split the subkey and the value + Value = (char*)strrchr(Subkey, ','); + if (Value == NULL) + return SynError(it8, "Invalid value for property '%s'.", VarName); + + // gobble the spaces before the coma, and the coma itself + temp = Value++; + do *temp-- = '\0'; while (temp >= Subkey && *temp == ' '); + + // gobble any space at the right + temp = Value + strlen(Value) - 1; + while (*temp == ' ') *temp-- = '\0'; + + // trim the strings from the left + Subkey += strspn(Subkey, " "); + Value += strspn(Value, " "); + + if (Subkey[0] == 0 || Value[0] == 0) + return SynError(it8, "Invalid value for property '%s'.", VarName); + AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR); } - else { - const char *Subkey; - char *Nextkey; - if (it8->sy != SSTRING) - return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName); - - // chop the string as a list of "subkey, value" pairs, using ';' as a separator - for(Subkey = Buffer; Subkey != NULL; Subkey = Nextkey) - { - char *Value, *temp; - - // identify token pair boundary - Nextkey = (char*) strchr(Subkey, ';'); - if(Nextkey) - *Nextkey++ = '\0'; - - // for each pair, split the subkey and the value - Value = (char*) strrchr(Subkey, ','); - if(Value == NULL) - return SynError(it8, "Invalid value for property '%s'.", VarName); - - // gobble the spaces before the coma, and the coma itself - temp = Value++; - do *temp-- = '\0'; while(temp >= Subkey && *temp == ' '); - - // gobble any space at the right - temp = Value + strlen(Value) - 1; - while(*temp == ' ') *temp-- = '\0'; - - // trim the strings from the left - Subkey += strspn(Subkey, " "); - Value += strspn(Value, " "); - - if(Subkey[0] == 0 || Value[0] == 0) - return SynError(it8, "Invalid value for property '%s'.", VarName); - AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR); - } - } - - InSymbol(it8); - break; + } + + InSymbol(it8); + break; case SEOLN: break; @@ -2058,25 +2057,35 @@ static -LCMSBOOL ParseIT8(LPIT8 it8, LCMSBOOL nosheet) +void ReadType(cmsIT8* it8, char* SheetTypePtr) { - char* SheetTypePtr = it8 ->SheetType; - - if (nosheet == 0) { + cmsInt32Number cnt = 0; // First line is a very special case. while (isseparator(it8->ch)) NextCh(it8); - while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) { - - *SheetTypePtr++= (char) it8 ->ch; + while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) { + + if (cnt++ < MAXSTR) + *SheetTypePtr++= (char) it8 ->ch; NextCh(it8); } - } *SheetTypePtr = 0; +} + + +static +cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet) +{ + char* SheetTypePtr = it8 ->Tab[0].SheetType; + + if (nosheet == 0) { + ReadType(it8, SheetTypePtr); + } + InSymbol(it8); SkipEOLN(it8); @@ -2098,6 +2107,39 @@ AllocTable(it8); it8 ->nTable = it8 ->TablesCount - 1; + + // Read sheet type if present. We only support identifier and string. + // is a type string + // anything else, is not a type string + if (nosheet == 0) { + + if (it8 ->sy == SIDENT) { + + // May be a type sheet or may be a prop value statement. We cannot use insymbol in + // this special case... + while (isseparator(it8->ch)) + NextCh(it8); + + // If a newline is found, then this is a type string + if (it8 ->ch == '\n' || it8->ch == '\r') { + + cmsIT8SetSheetType(it8, it8 ->id); + InSymbol(it8); + } + else + { + // It is not. Just continue + cmsIT8SetSheetType(it8, ""); + } + } + else + // Validate quoted strings + if (it8 ->sy == SSTRING) { + cmsIT8SetSheetType(it8, it8 ->str); + InSymbol(it8); + } + } + } break; @@ -2116,40 +2158,39 @@ -// Init usefull pointers +// Init useful pointers static -void CookPointers(LPIT8 it8) +void CookPointers(cmsIT8* it8) { int idField, i; char* Fld; - int j; - int nOldTable = it8 ->nTable; + cmsUInt32Number j; + cmsUInt32Number nOldTable = it8 ->nTable; for (j=0; j < it8 ->TablesCount; j++) { - LPTABLE t = it8 ->Tab + j; + TABLE* t = it8 ->Tab + j; t -> SampleID = 0; it8 ->nTable = j; for (idField = 0; idField < t -> nSamples; idField++) { - if (t ->DataFormat == NULL) { - SynError(it8, "Undefined DATA_FORMAT"); - return; - + if (t ->DataFormat == NULL){ + SynError(it8, "Undefined DATA_FORMAT"); + return; } Fld = t->DataFormat[idField]; if (!Fld) continue; - if (stricmp(Fld, "SAMPLE_ID") == 0) { - - t -> SampleID = idField; - - for (i=0; i < t -> nPatches; i++) { + if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) { + + t -> SampleID = idField; + + for (i=0; i < t -> nPatches; i++) { char *Data = GetData(it8, i, idField); if (Data) { @@ -2164,13 +2205,13 @@ SetData(it8, i, idField, Buffer); } - } + } } // "LABEL" is an extension. It keeps references to forward tables - if ((stricmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) { + if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) { // Search for table references... for (i=0; i < t -> nPatches; i++) { @@ -2179,15 +2220,15 @@ if (Label) { - int k; + cmsUInt32Number k; // This is the label, search for a table containing // this property for (k=0; k < it8 ->TablesCount; k++) { - LPTABLE Table = it8 ->Tab + k; - LPKEYVALUE p; + TABLE* Table = it8 ->Tab + k; + KEYVALUE* p; if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) { @@ -2195,7 +2236,7 @@ char Buffer[256]; char *Type = p ->Value; - int nTable = k; + int nTable = (int) k; snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type ); @@ -2219,14 +2260,14 @@ // Try to infere if the file is a CGATS/IT8 file at all. Read first line // that should be something like some printable characters plus a \n - +// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line? static -int IsMyBlock(LPBYTE Buffer, size_t n) +int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n) { - int cols = 1, space = 0, quot = 0; - size_t i; - - if (n < 10) return FALSE; // Too small + int words = 1, space = 0, quot = 0; + cmsUInt32Number i; + + if (n < 10) return 0; // Too small if (n > 132) n = 132; @@ -2237,7 +2278,7 @@ { case '\n': case '\r': - return quot == 1 || cols > 2 ? 0 : cols; + return ((quot == 1) || (words > 2)) ? 0 : words; case '\t': case ' ': if(!quot && !space) @@ -2249,32 +2290,33 @@ default: if (Buffer[i] < 32) return 0; if (Buffer[i] > 127) return 0; - cols += space; + words += space; space = 0; break; } } - return FALSE; - + return 0; } static -int IsMyFile(const char* FileName) +cmsBool IsMyFile(const char* FileName) { FILE *fp; - size_t Size; - BYTE Ptr[133]; + cmsUInt32Number Size; + cmsUInt8Number Ptr[133]; fp = fopen(FileName, "rt"); if (!fp) { - cmsSignalError(LCMS_ERRC_ABORTED, "File '%s' not found", FileName); + cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName); return FALSE; } - Size = fread(Ptr, 1, 132, fp); - fclose(fp); + Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp); + + if (fclose(fp) != 0) + return FALSE; Ptr[Size] = '\0'; @@ -2284,24 +2326,28 @@ // ---------------------------------------------------------- Exported routines -LCMSHANDLE LCMSEXPORT cmsIT8LoadFromMem(void *Ptr, size_t len) +cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len) { - LCMSHANDLE hIT8; - LPIT8 it8; - - int type = IsMyBlock((LPBYTE) Ptr, len); + cmsHANDLE hIT8; + cmsIT8* it8; + int type; + + _cmsAssert(Ptr != NULL); + _cmsAssert(len != 0); + + type = IsMyBlock((const cmsUInt8Number*)Ptr, len); if (type == 0) return NULL; - hIT8 = cmsIT8Alloc(); + hIT8 = cmsIT8Alloc(ContextID); if (!hIT8) return NULL; - it8 = (LPIT8) hIT8; - it8 ->MemoryBlock = (char*) _cmsMalloc(len + 1); + it8 = (cmsIT8*) hIT8; + it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1); strncpy(it8 ->MemoryBlock, (const char*) Ptr, len); it8 ->MemoryBlock[len] = 0; - strncpy(it8->FileStack[0]->FileName, "", MAX_PATH-1); + strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1); it8-> Source = it8 -> MemoryBlock; if (!ParseIT8(it8, type-1)) { @@ -2313,7 +2359,7 @@ CookPointers(it8); it8 ->nTable = 0; - _cmsFree(it8->MemoryBlock); + _cmsFree(ContextID, it8->MemoryBlock); it8 -> MemoryBlock = NULL; return hIT8; @@ -2322,17 +2368,20 @@ } -LCMSHANDLE LCMSEXPORT cmsIT8LoadFromFile(const char* cFileName) +cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName) { - LCMSHANDLE hIT8; - LPIT8 it8; - - int type = IsMyFile(cFileName); + cmsHANDLE hIT8; + cmsIT8* it8; + int type; + + _cmsAssert(cFileName != NULL); + + type = IsMyFile(cFileName); if (type == 0) return NULL; - hIT8 = cmsIT8Alloc(); - it8 = (LPIT8) hIT8; + hIT8 = cmsIT8Alloc(ContextID); + it8 = (cmsIT8*) hIT8; if (!hIT8) return NULL; @@ -2344,8 +2393,8 @@ } - strncpy(it8->FileStack[0]->FileName, cFileName, MAX_PATH-1); - it8->FileStack[0]->FileName[MAX_PATH-1] = 0; + strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1); + it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0; if (!ParseIT8(it8, type-1)) { @@ -2357,28 +2406,41 @@ CookPointers(it8); it8 ->nTable = 0; - fclose(it8 ->FileStack[0]->Stream); + if (fclose(it8 ->FileStack[0]->Stream)!= 0) { + cmsIT8Free(hIT8); + return NULL; + } + return hIT8; } -int LCMSEXPORT cmsIT8EnumDataFormat(LCMSHANDLE hIT8, char ***SampleNames) +int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames) { - LPIT8 it8 = (LPIT8) hIT8; - LPTABLE t = GetTable(it8); - + cmsIT8* it8 = (cmsIT8*) hIT8; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + if (SampleNames) *SampleNames = t -> DataFormat; - return t -> nSamples; + return t -> nSamples; } -int LCMSEXPORT cmsIT8EnumProperties(LCMSHANDLE hIT8, const char ***PropertyNames) +cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames) { - LPIT8 it8 = (LPIT8) hIT8; - LPKEYVALUE p; - int n; - const char **Props; - LPTABLE t = GetTable(it8); + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + cmsUInt32Number n; + char **Props; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); // Pass#1 - count properties @@ -2388,7 +2450,7 @@ } - Props = (const char **) AllocChunk(it8, sizeof(char *) * n); + Props = (char **) AllocChunk(it8, sizeof(char *) * n); // Pass#2 - Fill pointers n = 0; @@ -2400,13 +2462,18 @@ return n; } -int LCMSEXPORT cmsIT8EnumPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char ***SubpropertyNames) +cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames) { - LPIT8 it8 = (LPIT8) hIT8; - LPKEYVALUE p, tmp; - int n; + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE *p, *tmp; + cmsUInt32Number n; const char **Props; - LPTABLE t = GetTable(it8); + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + + t = GetTable(it8); if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) { *SubpropertyNames = 0; @@ -2436,11 +2503,11 @@ } static -int LocatePatch(LPIT8 it8, const char* cPatch) +int LocatePatch(cmsIT8* it8, const char* cPatch) { int i; const char *data; - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); for (i=0; i < t-> nPatches; i++) { @@ -2448,7 +2515,7 @@ if (data != NULL) { - if (stricmp(data, cPatch) == 0) + if (cmsstrcasecmp(data, cPatch) == 0) return i; } } @@ -2459,110 +2526,114 @@ static -int LocateEmptyPatch(LPIT8 it8) +int LocateEmptyPatch(cmsIT8* it8) { int i; const char *data; - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); for (i=0; i < t-> nPatches; i++) { data = GetData(it8, i, t->SampleID); if (data == NULL) - return i; - - } - - return -1; + return i; + + } + + return -1; } static -int LocateSample(LPIT8 it8, const char* cSample) +int LocateSample(cmsIT8* it8, const char* cSample) { int i; const char *fld; - LPTABLE t = GetTable(it8); + TABLE* t = GetTable(it8); for (i=0; i < t->nSamples; i++) { fld = GetDataFormat(it8, i); if (fld != NULL) { - if (stricmp(fld, cSample) == 0) - return i; - } + if (cmsstrcasecmp(fld, cSample) == 0) + return i; + } } - // SynError(it8, "Couldn't find data field %s\n", cSample); return -1; } -int LCMSEXPORT cmsIT8GetDataFormat(LCMSHANDLE hIT8, const char* cSample) +int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + return LocateSample(it8, cSample); } -const char* LCMSEXPORT cmsIT8GetDataRowCol(LCMSHANDLE hIT8, int row, int col) +const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); return GetData(it8, row, col); } -double LCMSEXPORT cmsIT8GetDataRowColDbl(LCMSHANDLE hIT8, int row, int col) +cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col) { const char* Buffer; Buffer = cmsIT8GetDataRowCol(hIT8, row, col); - if (Buffer) { - - return atof(Buffer); - - } else - return 0; - + if (Buffer == NULL) return 0.0; + + return ParseFloatNumber(Buffer); } -LCMSBOOL LCMSEXPORT cmsIT8SetDataRowCol(LCMSHANDLE hIT8, int row, int col, const char* Val) +cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); return SetData(it8, row, col, Val); } -LCMSBOOL LCMSEXPORT cmsIT8SetDataRowColDbl(LCMSHANDLE hIT8, int row, int col, double Val) +cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; char Buff[256]; - sprintf(Buff, it8->DoubleFormatter, Val); + _cmsAssert(hIT8 != NULL); + + snprintf(Buff, 255, it8->DoubleFormatter, Val); return SetData(it8, row, col, Buff); } -const char* LCMSEXPORT cmsIT8GetData(LCMSHANDLE hIT8, const char* cPatch, const char* cSample) +const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; int iField, iSet; + _cmsAssert(hIT8 != NULL); iField = LocateSample(it8, cSample); if (iField < 0) { return NULL; } - iSet = LocatePatch(it8, cPatch); if (iSet < 0) { return NULL; @@ -2572,117 +2643,119 @@ } -double LCMSEXPORT cmsIT8GetDataDbl(LCMSHANDLE it8, const char* cPatch, const char* cSample) +cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample) { const char* Buffer; Buffer = cmsIT8GetData(it8, cPatch, cSample); - if (Buffer) { - - return atof(Buffer); - - } else { - - return 0; - } + return ParseFloatNumber(Buffer); } -LCMSBOOL LCMSEXPORT cmsIT8SetData(LCMSHANDLE hIT8, const char* cPatch, - const char* cSample, - const char *Val) +cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; int iField, iSet; - LPTABLE t = GetTable(it8); - + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); iField = LocateSample(it8, cSample); if (iField < 0) return FALSE; - - - if (t-> nPatches == 0) { - - AllocateDataFormat(it8); - AllocateDataSet(it8); - CookPointers(it8); - } - - - if (stricmp(cSample, "SAMPLE_ID") == 0) - { - - iSet = LocateEmptyPatch(it8); - if (iSet < 0) { - return SynError(it8, "Couldn't add more patches '%s'\n", cPatch); - } - - iField = t -> SampleID; + if (t-> nPatches == 0) { + + AllocateDataFormat(it8); + AllocateDataSet(it8); + CookPointers(it8); + } + + if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) { + + iSet = LocateEmptyPatch(it8); + if (iSet < 0) { + return SynError(it8, "Couldn't add more patches '%s'\n", cPatch); } - else { - iSet = LocatePatch(it8, cPatch); - if (iSet < 0) { - return FALSE; - } + + iField = t -> SampleID; + } + else { + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + return FALSE; } - - return SetData(it8, iSet, iField, Val); + } + + return SetData(it8, iSet, iField, Val); } -LCMSBOOL LCMSEXPORT cmsIT8SetDataDbl(LCMSHANDLE hIT8, const char* cPatch, - const char* cSample, - double Val) +cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + cmsFloat64Number Val) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; char Buff[256]; - snprintf(Buff, 255, it8->DoubleFormatter, Val); - return cmsIT8SetData(hIT8, cPatch, cSample, Buff); - + _cmsAssert(hIT8 != NULL); + + snprintf(Buff, 255, it8->DoubleFormatter, Val); + return cmsIT8SetData(hIT8, cPatch, cSample, Buff); } // Buffer should get MAXSTR at least -const char* LCMSEXPORT cmsIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer) +const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer) { - LPIT8 it8 = (LPIT8) hIT8; - LPTABLE t = GetTable(it8); - char* Data = GetData(it8, nPatch, t->SampleID); - - if (!Data) return NULL; - if (!buffer) return Data; - - strncpy(buffer, Data, MAXSTR-1); - buffer[MAXSTR-1] = 0; - return buffer; + cmsIT8* it8 = (cmsIT8*) hIT8; + TABLE* t; + char* Data; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + Data = GetData(it8, nPatch, t->SampleID); + + if (!Data) return NULL; + if (!buffer) return Data; + + strncpy(buffer, Data, MAXSTR-1); + buffer[MAXSTR-1] = 0; + return buffer; } -int LCMSEXPORT cmsIT8GetPatchByName(LCMSHANDLE hIT8, const char *cPatch) +int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch) { - return LocatePatch((LPIT8)hIT8, cPatch); + _cmsAssert(hIT8 != NULL); + + return LocatePatch((cmsIT8*)hIT8, cPatch); } -int LCMSEXPORT cmsIT8TableCount(LCMSHANDLE hIT8) +cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8) { - LPIT8 it8 = (LPIT8) hIT8; - - return it8 ->TablesCount; + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return it8 ->TablesCount; } // This handles the "LABEL" extension. // Label, nTable, Type -int LCMSEXPORT cmsIT8SetTableByLabel(LCMSHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType) +int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType) { const char* cLabelFld; char Type[256], Label[256]; - int nTable; + cmsUInt32Number nTable; + + _cmsAssert(hIT8 != NULL); if (cField != NULL && *cField == 0) cField = "LABEL"; @@ -2693,7 +2766,7 @@ cLabelFld = cmsIT8GetData(hIT8, cSet, cField); if (!cLabelFld) return -1; - if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3) + if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3) return -1; if (ExpectedType != NULL && *ExpectedType == 0) @@ -2701,18 +2774,21 @@ if (ExpectedType) { - if (stricmp(Type, ExpectedType) != 0) return -1; + if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1; } return cmsIT8SetTable(hIT8, nTable); } -LCMSBOOL LCMSEXPORT cmsIT8SetIndexColumn(LCMSHANDLE hIT8, const char* cSample) +cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample) { - LPIT8 it8 = (LPIT8) hIT8; - - int pos = LocateSample(it8, cSample); + cmsIT8* it8 = (cmsIT8*) hIT8; + int pos; + + _cmsAssert(hIT8 != NULL); + + pos = LocateSample(it8, cSample); if(pos == -1) return FALSE; @@ -2721,13 +2797,17 @@ } -void LCMSEXPORT cmsIT8DefineDblFormat(LCMSHANDLE hIT8, const char* Formatter) +void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter) { - LPIT8 it8 = (LPIT8) hIT8; + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); if (Formatter == NULL) strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); else - strcpy(it8->DoubleFormatter, Formatter); + strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter)); + + it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0; } diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmscnvrt.c --- a/src/share/native/sun/java2d/cmm/lcms/cmscnvrt.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmscnvrt.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,178 +49,170 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// -#include "lcms.h" - - +#include "lcms2_internal.h" -/* - This module provides conversion stages for handling intents. +// Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point +// compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS +// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1) +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); -The chain of evaluation in a transform is: +//--------------------------------------------------------------------------------- - PCS1 PCS2 PCS3 PCS4 +// This is the default routine for ICC-style intents. A user may decide to override it by using a plugin. +// Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric +static +cmsPipeline* DefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); -|From | |From | |Conversion | |Preview | |Gamut | |Conversion | |To | |To | -|Input|->|Device|->|Stage 1 |->|handling|->|Checking|->|Stage 2 |->|Device|->|output | - --------- ------- ------------- --------- ---------- ------------- ------- --------- +//--------------------------------------------------------------------------------- - AToB0 prew0 gamut BToA0 -Formatting LUT Adjusting LUT LUT Adjusting LUT Formatting - Intent Intent 1 intent intent Intent 2 Intent +// This is the entry for black-preserving K-only intents, which are non-ICC. Last profile have to be a output profile +// to do the trick (no devicelinks allowed at that position) +static +cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); +//--------------------------------------------------------------------------------- -Some of these LUT may be missing +// This is the entry for black-plane preserving, which are non-ICC. Again, Last profile have to be a output profile +// to do the trick (no devicelinks allowed at that position) +static +cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); -There are two intents involved here, the intent of the transform itself, and the -intent the proof is being done, if is the case. Since the first intent is to be -applied to preview, is the proofing intent. The second intent identifies the -transform intent. Input data of any stage is taked as relative colorimetric -always. +//--------------------------------------------------------------------------------- -NOTES: V4 states than perceptual & saturation intents between mixed v2 & v4 profiles should -scale PCS from a black point equal to ZERO in v2 profiles to the reference media black of -perceptual v4 PCS. Since I found many v2 profiles to be using a perceptual intent with black -point not zero at all, I'm implementing that as a black point compensation from whatever -black from perceptal intent to the reference media black for v4 profiles. +// This is a structure holding implementations for all supported intents. +typedef struct _cms_intents_list { -*/ + cmsUInt32Number Intent; + char Description[256]; + cmsIntentFn Link; + struct _cms_intents_list* Next; + +} cmsIntentsList; +// Built-in intents +static cmsIntentsList DefaultIntents[] = { + + { INTENT_PERCEPTUAL, "Perceptual", DefaultICCintents, &DefaultIntents[1] }, + { INTENT_RELATIVE_COLORIMETRIC, "Relative colorimetric", DefaultICCintents, &DefaultIntents[2] }, + { INTENT_SATURATION, "Saturation", DefaultICCintents, &DefaultIntents[3] }, + { INTENT_ABSOLUTE_COLORIMETRIC, "Absolute colorimetric", DefaultICCintents, &DefaultIntents[4] }, + { INTENT_PRESERVE_K_ONLY_PERCEPTUAL, "Perceptual preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[5] }, + { INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[6] }, + { INTENT_PRESERVE_K_ONLY_SATURATION, "Saturation preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[7] }, + { INTENT_PRESERVE_K_PLANE_PERCEPTUAL, "Perceptual preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[8] }, + { INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] }, + { INTENT_PRESERVE_K_PLANE_SATURATION, "Saturation preserving black plane", BlackPreservingKPlaneIntents, NULL } +}; -int cdecl cmsChooseCnvrt(int Absolute, - int Phase1, LPcmsCIEXYZ BlackPointIn, - LPcmsCIEXYZ WhitePointIn, - LPcmsCIEXYZ IlluminantIn, - LPMAT3 ChromaticAdaptationMatrixIn, +// A pointer to the beginning of the list +_cmsIntentsPluginChunkType _cmsIntentsPluginChunk = { NULL }; - int Phase2, LPcmsCIEXYZ BlackPointOut, - LPcmsCIEXYZ WhitePointOut, - LPcmsCIEXYZ IlluminantOut, - LPMAT3 ChromaticAdaptationMatrixOut, +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginIntentsList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsIntentsPluginChunkType newHead = { NULL }; + cmsIntentsList* entry; + cmsIntentsList* Anterior = NULL; + _cmsIntentsPluginChunkType* head = (_cmsIntentsPluginChunkType*) src->chunks[IntentPlugin]; + + // Walk the list copying all nodes + for (entry = head->Intents; + entry != NULL; + entry = entry ->Next) { - int DoBlackPointCompensation, - double AdaptationState, - _cmsADJFN *fn1, - LPWMAT3 wm, LPWVEC3 wof); + cmsIntentsList *newEntry = ( cmsIntentsList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsIntentsList)); - -// ------------------------------------------------------------------------- + if (newEntry == NULL) + return; -// D50 - Widely used + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; -LCMSAPI LPcmsCIEXYZ LCMSEXPORT cmsD50_XYZ(void) -{ - static cmsCIEXYZ D50XYZ = {D50X, D50Y, D50Z}; + Anterior = newEntry; - return &D50XYZ; + if (newHead.Intents == NULL) + newHead.Intents = newEntry; + } + + ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsIntentsPluginChunkType)); } -LCMSAPI LPcmsCIExyY LCMSEXPORT cmsD50_xyY(void) +void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) { - static cmsCIExyY D50xyY; - cmsXYZ2xyY(&D50xyY, cmsD50_XYZ()); + if (src != NULL) { - return &D50xyY; + // Copy all linked list + DupPluginIntentsList(ctx, src); + } + else { + static _cmsIntentsPluginChunkType IntentsPluginChunkType = { NULL }; + ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx ->MemPool, &IntentsPluginChunkType, sizeof(_cmsIntentsPluginChunkType)); + } } -// ---------------- From LUT to LUT -------------------------- - - -// Calculate m, offset Relativ -> Absolute undoing any chromatic -// adaptation done by the profile. - -#ifdef _MSC_VER -#pragma warning(disable : 4100 4505) -#endif - - - -// join scalings to obtain: -// relative input to absolute and then to relative output - +// Search the list for a suitable intent. Returns NULL if not found static -void Rel2RelStepAbsCoefs(double AdaptationState, - - LPcmsCIEXYZ BlackPointIn, - LPcmsCIEXYZ WhitePointIn, - LPcmsCIEXYZ IlluminantIn, - LPMAT3 ChromaticAdaptationMatrixIn, - - LPcmsCIEXYZ BlackPointOut, - LPcmsCIEXYZ WhitePointOut, - LPcmsCIEXYZ IlluminantOut, - LPMAT3 ChromaticAdaptationMatrixOut, - - LPMAT3 m, LPVEC3 of) +cmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent) { + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); + cmsIntentsList* pt; - VEC3 WtPtIn, WtPtInAdapted; - VEC3 WtPtOut, WtPtOutAdapted; - MAT3 Scale, m1, m2, m3; - - VEC3init(&WtPtIn, WhitePointIn->X, WhitePointIn->Y, WhitePointIn->Z); - MAT3eval(&WtPtInAdapted, ChromaticAdaptationMatrixIn, &WtPtIn); - - VEC3init(&WtPtOut, WhitePointOut->X, WhitePointOut->Y, WhitePointOut->Z); - MAT3eval(&WtPtOutAdapted, ChromaticAdaptationMatrixOut, &WtPtOut); - - VEC3init(&Scale.v[0], WtPtInAdapted.n[0] / WtPtOutAdapted.n[0], 0, 0); - VEC3init(&Scale.v[1], 0, WtPtInAdapted.n[1] / WtPtOutAdapted.n[1], 0); - VEC3init(&Scale.v[2], 0, 0, WtPtInAdapted.n[2] / WtPtOutAdapted.n[2]); - - - // Adaptation state - - if (AdaptationState == 1.0) { + for (pt = ctx -> Intents; pt != NULL; pt = pt -> Next) + if (pt ->Intent == Intent) return pt; - // Observer is fully adapted. Keep chromatic adaptation - - CopyMemory(m, &Scale, sizeof(MAT3)); - - } - else { + for (pt = DefaultIntents; pt != NULL; pt = pt -> Next) + if (pt ->Intent == Intent) return pt; - // Observer is not adapted, undo the chromatic adaptation - m1 = *ChromaticAdaptationMatrixIn; - MAT3inverse(&m1, &m2); - - MAT3per(&m3, &m2, &Scale); - MAT3per(m, &m3, ChromaticAdaptationMatrixOut); - } - - - VEC3init(of, 0.0, 0.0, 0.0); - + return NULL; } - -// The (in)famous black point compensation. Right now implemented as -// a linear scaling in XYZ - +// Black point compensation. Implemented as a linear scaling in XYZ. Black points +// should come relative to the white point. Fills an matrix/offset element m +// which is organized as a 4x4 matrix. static -void ComputeBlackPointCompensationFactors(LPcmsCIEXYZ BlackPointIn, - LPcmsCIEXYZ WhitePointIn, - LPcmsCIEXYZ IlluminantIn, - LPcmsCIEXYZ BlackPointOut, - LPcmsCIEXYZ WhitePointOut, - LPcmsCIEXYZ IlluminantOut, - LPMAT3 m, LPVEC3 of) +void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn, + const cmsCIEXYZ* BlackPointOut, + cmsMAT3* m, cmsVEC3* off) { - - - cmsCIEXYZ RelativeBlackPointIn, RelativeBlackPointOut; - double ax, ay, az, bx, by, bz, tx, ty, tz; - - // At first, convert both black points to relative. - - cmsAdaptToIlluminant(&RelativeBlackPointIn, WhitePointIn, IlluminantIn, BlackPointIn); - cmsAdaptToIlluminant(&RelativeBlackPointOut, WhitePointOut, IlluminantOut, BlackPointOut); + cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz; // Now we need to compute a matrix plus an offset m and of such of // [m]*bpin + off = bpout @@ -229,438 +222,970 @@ // a = (bpout - D50) / (bpin - D50) // b = - D50* (bpout - bpin) / (bpin - D50) + tx = BlackPointIn->X - cmsD50_XYZ()->X; + ty = BlackPointIn->Y - cmsD50_XYZ()->Y; + tz = BlackPointIn->Z - cmsD50_XYZ()->Z; - tx = RelativeBlackPointIn.X - IlluminantIn ->X; - ty = RelativeBlackPointIn.Y - IlluminantIn ->Y; - tz = RelativeBlackPointIn.Z - IlluminantIn ->Z; + ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx; + ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty; + az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz; - ax = (RelativeBlackPointOut.X - IlluminantOut ->X) / tx; - ay = (RelativeBlackPointOut.Y - IlluminantOut ->Y) / ty; - az = (RelativeBlackPointOut.Z - IlluminantOut ->Z) / tz; + bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx; + by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty; + bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz; - bx = - IlluminantOut -> X * (RelativeBlackPointOut.X - RelativeBlackPointIn.X) / tx; - by = - IlluminantOut -> Y * (RelativeBlackPointOut.Y - RelativeBlackPointIn.Y) / ty; - bz = - IlluminantOut -> Z * (RelativeBlackPointOut.Z - RelativeBlackPointIn.Z) / tz; + _cmsVEC3init(&m ->v[0], ax, 0, 0); + _cmsVEC3init(&m ->v[1], 0, ay, 0); + _cmsVEC3init(&m ->v[2], 0, 0, az); + _cmsVEC3init(off, bx, by, bz); + +} - MAT3identity(m); +// Approximate a blackbody illuminant based on CHAD information +static +cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad) +{ + // Convert D50 across inverse CHAD to get the absolute white point + cmsVEC3 d, s; + cmsCIEXYZ Dest; + cmsCIExyY DestChromaticity; + cmsFloat64Number TempK; + cmsMAT3 m1, m2; + + m1 = *Chad; + if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; + + s.n[VX] = cmsD50_XYZ() -> X; + s.n[VY] = cmsD50_XYZ() -> Y; + s.n[VZ] = cmsD50_XYZ() -> Z; + + _cmsMAT3eval(&d, &m2, &s); + + Dest.X = d.n[VX]; + Dest.Y = d.n[VY]; + Dest.Z = d.n[VZ]; + + cmsXYZ2xyY(&DestChromaticity, &Dest); + + if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity)) + return -1.0; + + return TempK; +} + +// Compute a CHAD based on a given temperature +static + void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp) +{ + cmsCIEXYZ White; + cmsCIExyY ChromaticityOfWhite; + + cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp); + cmsxyY2XYZ(&White, &ChromaticityOfWhite); + _cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ()); +} + +// Join scalings to obtain relative input to absolute and then to relative output. +// Result is stored in a 3x3 matrix +static +cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState, + const cmsCIEXYZ* WhitePointIn, + const cmsMAT3* ChromaticAdaptationMatrixIn, + const cmsCIEXYZ* WhitePointOut, + const cmsMAT3* ChromaticAdaptationMatrixOut, + cmsMAT3* m) +{ + cmsMAT3 Scale, m1, m2, m3, m4; + + // TODO: Follow Marc Mahy's recommendation to check if CHAD is same by using M1*M2 == M2*M1. If so, do nothing. + // TODO: Add support for ArgyllArts tag - m->v[VX].n[0] = ax; - m->v[VY].n[1] = ay; - m->v[VZ].n[2] = az; + // Adaptation state + if (AdaptationState == 1.0) { + + // Observer is fully adapted. Keep chromatic adaptation. + // That is the standard V4 behaviour + _cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); + _cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); + _cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); + + } + else { + + // Incomplete adaptation. This is an advanced feature. + _cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); + _cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); + _cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); + + + if (AdaptationState == 0.0) { + + m1 = *ChromaticAdaptationMatrixOut; + _cmsMAT3per(&m2, &m1, &Scale); + // m2 holds CHAD from output white to D50 times abs. col. scaling + + // Observer is not adapted, undo the chromatic adaptation + _cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut); + + m3 = *ChromaticAdaptationMatrixIn; + if (!_cmsMAT3inverse(&m3, &m4)) return FALSE; + _cmsMAT3per(m, &m2, &m4); - VEC3init(of, bx, by, bz); + } else { + + cmsMAT3 MixedCHAD; + cmsFloat64Number TempSrc, TempDest, Temp; + + m1 = *ChromaticAdaptationMatrixIn; + if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; + _cmsMAT3per(&m3, &m2, &Scale); + // m3 holds CHAD from input white to D50 times abs. col. scaling + + TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); + TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); + + if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong + + if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) { + + _cmsMAT3identity(m); + return TRUE; + } + + Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc; + + // Get a CHAD from whatever output temperature to D50. This replaces output CHAD + Temp2CHAD(&MixedCHAD, Temp); + + _cmsMAT3per(m, &m3, &MixedCHAD); + } + + } + return TRUE; } -// Return TRUE if both m and of are empy -- "m" being identity and "of" being 0 +// Just to see if m matrix should be applied +static +cmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number diff = 0; + cmsMAT3 Ident; + int i; + + if (m == NULL && off == NULL) return TRUE; // NULL is allowed as an empty layer + if (m == NULL && off != NULL) return FALSE; // This is an internal error + + _cmsMAT3identity(&Ident); + + for (i=0; i < 3*3; i++) + diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]); + for (i=0; i < 3; i++) + diff += fabs(((cmsFloat64Number*)off)[i]); + + + return (diff < 0.002); +} + + +// Compute the conversion layer static -LCMSBOOL IdentityParameters(LPWMAT3 m, LPWVEC3 of) +cmsBool ComputeConversion(cmsUInt32Number i, + cmsHPROFILE hProfiles[], + cmsUInt32Number Intent, + cmsBool BPC, + cmsFloat64Number AdaptationState, + cmsMAT3* m, cmsVEC3* off) { - WVEC3 wv0; + + int k; + + // m and off are set to identity and this is detected latter on + _cmsMAT3identity(m); + _cmsVEC3init(off, 0, 0, 0); + + // If intent is abs. colorimetric, + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { + + cmsCIEXYZ WhitePointIn, WhitePointOut; + cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; + + _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); + _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); + + _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); + _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); + + if (!ComputeAbsoluteIntent(AdaptationState, + &WhitePointIn, &ChromaticAdaptationMatrixIn, + &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE; + + } + else { + // Rest of intents may apply BPC. + + if (BPC) { + + cmsCIEXYZ BlackPointIn, BlackPointOut; + + cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); + cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); + + // If black points are equal, then do nothing + if (BlackPointIn.X != BlackPointOut.X || + BlackPointIn.Y != BlackPointOut.Y || + BlackPointIn.Z != BlackPointOut.Z) + ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off); + } + } + + // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0, + // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so + // we have first to convert from encoded to XYZ and then convert back to encoded. + // y = Mx + Off + // x = x'c + // y = M x'c + Off + // y = y'c; y' = y / c + // y' = (Mx'c + Off) /c = Mx' + (Off / c) + + for (k=0; k < 3; k++) { + off ->n[k] /= MAX_ENCODEABLE_XYZ; + } + + return TRUE; +} + - VEC3initF(&wv0, 0, 0, 0); +// Add a conversion stage if needed. If a matrix/offset m is given, it applies to XYZ space +static +cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m; + cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off; + + // Handle PCS mismatches. A specialized stage is added to the LUT in such case + switch (InPCS) { + + case cmsSigXYZData: // Input profile operates in XYZ + + switch (OutPCS) { + + case cmsSigXYZData: // XYZ -> XYZ + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + break; + + case cmsSigLabData: // XYZ -> Lab + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) + return FALSE; + break; + + default: + return FALSE; // Colorspace mismatch + } + break; - if (!MAT3isIdentity(m, 0.00001)) return FALSE; - if (!VEC3equal(of, &wv0, 0.00001)) return FALSE; + case cmsSigLabData: // Input profile operates in Lab + + switch (OutPCS) { + + case cmsSigXYZData: // Lab -> XYZ + + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID))) + return FALSE; + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + break; + + case cmsSigLabData: // Lab -> Lab + + if (!IsEmptyLayer(m, off)) { + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) || + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) || + !cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) + return FALSE; + } + break; + + default: + return FALSE; // Mismatch + } + break; + + // On colorspaces other than PCS, check for same space + default: + if (InPCS != OutPCS) return FALSE; + break; + } return TRUE; } - - -// ----------------------------------------- Inter PCS conversions - -// XYZ to XYZ linear scaling. Aso used on Black point compensation - +// Is a given space compatible with another? static -void XYZ2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of) +cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b) { + // If they are same, they are compatible. + if (a == b) return TRUE; - WVEC3 a, r; - - a.n[0] = In[0] << 1; - a.n[1] = In[1] << 1; - a.n[2] = In[2] << 1; + // Check for MCH4 substitution of CMYK + if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE; + if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE; - MAT3evalW(&r, m, &a); + // Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other. + if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE; + if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE; - Out[0] = _cmsClampWord((r.n[VX] + of->n[VX]) >> 1); - Out[1] = _cmsClampWord((r.n[VY] + of->n[VY]) >> 1); - Out[2] = _cmsClampWord((r.n[VZ] + of->n[VZ]) >> 1); + return FALSE; } -// XYZ to Lab, scaling first - +// Default handler for ICC-style intents static -void XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of) +cmsPipeline* DefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) { - WORD XYZ[3]; - - XYZ2XYZ(In, XYZ, m, of); - cmsXYZ2LabEncoded(XYZ, Out); -} - -// Lab to XYZ, then scalling - -static -void Lab2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of) -{ - WORD XYZ[3]; - - cmsLab2XYZEncoded(In, XYZ); - XYZ2XYZ(XYZ, Out, m, of); -} + cmsPipeline* Lut = NULL; + cmsPipeline* Result; + cmsHPROFILE hProfile; + cmsMAT3 m; + cmsVEC3 off; + cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut = cmsSigLabData, CurrentColorSpace; + cmsProfileClassSignature ClassSig; + cmsUInt32Number i, Intent; -// Lab to XYZ, scalling and then, back to Lab + // For safety + if (nProfiles == 0) return NULL; -static -void Lab2XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of) -{ - WORD XYZ[3], XYZ2[3]; + // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' + Result = cmsPipelineAlloc(ContextID, 0, 0); + if (Result == NULL) return NULL; - cmsLab2XYZEncoded(In, XYZ); - XYZ2XYZ(XYZ, XYZ2, m, of); - cmsXYZ2LabEncoded(XYZ2, Out); -} + CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); -// ------------------------------------------------------------------ + for (i=0; i < nProfiles; i++) { -// Dispatcher for XYZ Relative LUT + cmsBool lIsDeviceLink, lIsInput; -static -int FromXYZRelLUT(int Absolute, - LPcmsCIEXYZ BlackPointIn, - LPcmsCIEXYZ WhitePointIn, - LPcmsCIEXYZ IlluminantIn, - LPMAT3 ChromaticAdaptationMatrixIn, + hProfile = hProfiles[i]; + ClassSig = cmsGetDeviceClass(hProfile); + lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); - int Phase2, LPcmsCIEXYZ BlackPointOut, - LPcmsCIEXYZ WhitePointOut, - LPcmsCIEXYZ IlluminantOut, - LPMAT3 ChromaticAdaptationMatrixOut, - - int DoBlackPointCompensation, - double AdaptationState, - _cmsADJFN *fn1, - LPMAT3 m, LPVEC3 of) + // First profile is used as input unless devicelink or abstract + if ((i == 0) && !lIsDeviceLink) { + lIsInput = TRUE; + } + else { + // Else use profile in the input direction if current space is not PCS + lIsInput = (CurrentColorSpace != cmsSigXYZData) && + (CurrentColorSpace != cmsSigLabData); + } -{ - switch (Phase2) { + Intent = TheIntents[i]; - // From relative XYZ to Relative XYZ. + if (lIsInput || lIsDeviceLink) { - case XYZRel: + ColorSpaceIn = cmsGetColorSpace(hProfile); + ColorSpaceOut = cmsGetPCS(hProfile); + } + else { - if (Absolute) - { - // From input relative to absolute, and then - // back to output relative + ColorSpaceIn = cmsGetPCS(hProfile); + ColorSpaceOut = cmsGetColorSpace(hProfile); + } - Rel2RelStepAbsCoefs(AdaptationState, - BlackPointIn, - WhitePointIn, - IlluminantIn, - ChromaticAdaptationMatrixIn, - BlackPointOut, - WhitePointOut, - IlluminantOut, - ChromaticAdaptationMatrixOut, - m, of); - *fn1 = XYZ2XYZ; + if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) { + + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch"); + goto Error; + } + + // If devicelink is found, then no custom intent is allowed and we can + // read the LUT to be applied. Settings don't apply here. + if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) { - } - else - { - // XYZ Relative to XYZ relative, no op required - *fn1 = NULL; - if (DoBlackPointCompensation) { + // Get the involved LUT from the profile + Lut = _cmsReadDevicelinkLUT(hProfile, Intent); + if (Lut == NULL) goto Error; - *fn1 = XYZ2XYZ; - ComputeBlackPointCompensationFactors(BlackPointIn, - WhitePointIn, - IlluminantIn, - BlackPointOut, - WhitePointOut, - IlluminantOut, - m, of); - - } - } - break; + // What about abstract profiles? + if (ClassSig == cmsSigAbstractClass && i > 0) { + if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; + } + else { + _cmsMAT3identity(&m); + _cmsVEC3init(&off, 0, 0, 0); + } - // From relative XYZ to Relative Lab - - case LabRel: - - // First pass XYZ to absolute, then to relative and - // finally to Lab. I use here D50 for output in order - // to prepare the "to Lab" conversion. - - if (Absolute) - { + if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; - Rel2RelStepAbsCoefs(AdaptationState, - BlackPointIn, - WhitePointIn, - IlluminantIn, - ChromaticAdaptationMatrixIn, - BlackPointOut, - WhitePointOut, - IlluminantOut, - ChromaticAdaptationMatrixOut, - m, of); + } + else { - *fn1 = XYZ2Lab; - - } - else - { - // Just Convert to Lab - - MAT3identity(m); - VEC3init(of, 0, 0, 0); - *fn1 = XYZ2Lab; + if (lIsInput) { + // Input direction means non-pcs connection, so proceed like devicelinks + Lut = _cmsReadInputLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + } + else { - if (DoBlackPointCompensation) { - - ComputeBlackPointCompensationFactors(BlackPointIn, - WhitePointIn, - IlluminantIn, - BlackPointOut, - WhitePointOut, - IlluminantOut, - m, of); - } - } - break; + // Output direction means PCS connection. Intent may apply here + Lut = _cmsReadOutputLUT(hProfile, Intent); + if (Lut == NULL) goto Error; - default: return FALSE; - } + if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; + if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; + + } + } + + // Concatenate to the output LUT + if (!cmsPipelineCat(Result, Lut)) + goto Error; + + cmsPipelineFree(Lut); + Lut = NULL; + + // Update current space + CurrentColorSpace = ColorSpaceOut; + } + + // Check for non-negatives clip + if (dwFlags & cmsFLAGS_NONEGATIVES) { - return TRUE; + if (ColorSpaceOut == cmsSigGrayData || + ColorSpaceOut == cmsSigRgbData || + ColorSpaceOut == cmsSigCmykData) { + + cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOf(ColorSpaceOut)); + if (clip == NULL) goto Error; + + if (!cmsPipelineInsertStage(Result, cmsAT_END, clip)) + goto Error; + } + + } + + return Result; + +Error: + + if (Lut != NULL) cmsPipelineFree(Lut); + if (Result != NULL) cmsPipelineFree(Result); + return NULL; + + cmsUNUSED_PARAMETER(dwFlags); } +// Wrapper for DLL calling convention +cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); +} + +// Black preserving intents --------------------------------------------------------------------------------------------- + +// Translate black-preserving intents to ICC ones +static +cmsUInt32Number TranslateNonICCIntents(cmsUInt32Number Intent) +{ + switch (Intent) { + case INTENT_PRESERVE_K_ONLY_PERCEPTUAL: + case INTENT_PRESERVE_K_PLANE_PERCEPTUAL: + return INTENT_PERCEPTUAL; + + case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC: + case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC: + return INTENT_RELATIVE_COLORIMETRIC; + + case INTENT_PRESERVE_K_ONLY_SATURATION: + case INTENT_PRESERVE_K_PLANE_SATURATION: + return INTENT_SATURATION; + + default: return Intent; + } +} + +// Sampler for Black-only preserving CMYK->CMYK transforms + +typedef struct { + cmsPipeline* cmyk2cmyk; // The original transform + cmsToneCurve* KTone; // Black-to-black tone curve + +} GrayOnlyParams; + + +// Preserve black only if that is the only ink used +static +int BlackPreservingGrayOnlySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + GrayOnlyParams* bp = (GrayOnlyParams*) Cargo; + + // If going across black only, keep black only + if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + + // TAC does not apply because it is black ink! + Out[0] = Out[1] = Out[2] = 0; + Out[3] = cmsEvalToneCurve16(bp->KTone, In[3]); + return TRUE; + } + + // Keep normal transform for other colors + bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data); + return TRUE; +} + +// This is the entry for black-preserving K-only intents, which are non-ICC +static +cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + GrayOnlyParams bp; + cmsPipeline* Result; + cmsUInt32Number ICCIntents[256]; + cmsStage* CLUT; + cmsUInt32Number i, nGridPoints; -// From Lab Relative type LUT + // Sanity check + if (nProfiles < 1 || nProfiles > 255) return NULL; -static -int FromLabRelLUT(int Absolute, - LPcmsCIEXYZ BlackPointIn, - LPcmsCIEXYZ WhitePointIn, - LPcmsCIEXYZ IlluminantIn, - LPMAT3 ChromaticAdaptationMatrixIn, - - int Phase2, LPcmsCIEXYZ BlackPointOut, - LPcmsCIEXYZ WhitePointOut, - LPcmsCIEXYZ IlluminantOut, - LPMAT3 ChromaticAdaptationMatrixOut, + // Translate black-preserving intents to ICC ones + for (i=0; i < nProfiles; i++) + ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); - int DoBlackPointCompensation, - double AdaptationState, - - _cmsADJFN *fn1, - LPMAT3 m, LPVEC3 of) -{ - - switch (Phase2) { + // Check for non-cmyk profiles + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData) + return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); - // From Lab Relative to XYZ Relative, very usual case - - case XYZRel: + memset(&bp, 0, sizeof(bp)); - if (Absolute) { // Absolute intent - - // From lab relative, to XYZ absolute, and then, - // back to XYZ relative + // Allocate an empty LUT for holding the result + Result = cmsPipelineAlloc(ContextID, 4, 4); + if (Result == NULL) return NULL; - Rel2RelStepAbsCoefs(AdaptationState, - BlackPointIn, - WhitePointIn, - cmsD50_XYZ(), - ChromaticAdaptationMatrixIn, - BlackPointOut, - WhitePointOut, - IlluminantOut, - ChromaticAdaptationMatrixOut, - m, of); + // Create a LUT holding normal ICC transform + bp.cmyk2cmyk = DefaultICCintents(ContextID, + nProfiles, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); - *fn1 = Lab2XYZ; + if (bp.cmyk2cmyk == NULL) goto Error; - } - else - { - // From Lab relative, to XYZ relative. - - *fn1 = Lab2XYZ; - if (DoBlackPointCompensation) { + // Now, compute the tone curve + bp.KTone = _cmsBuildKToneCurve(ContextID, + 4096, + nProfiles, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); - ComputeBlackPointCompensationFactors(BlackPointIn, - WhitePointIn, - IlluminantIn, - BlackPointOut, - WhitePointOut, - IlluminantOut, - m, of); - - } - } - break; + if (bp.KTone == NULL) goto Error; + // How many gridpoints are we going to use? + nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); - case LabRel: + // Create the CLUT. 16 bits + CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); + if (CLUT == NULL) goto Error; - if (Absolute) { + // This is the one and only MPE in this LUT + if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) + goto Error; - // First pass to XYZ using the input illuminant - // * InIlluminant / D50, then to absolute. Then - // to relative, but for input + // Sample it. We cannot afford pre/post linearization this time. + if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0)) + goto Error; + + // Get rid of xform and tone curve + cmsPipelineFree(bp.cmyk2cmyk); + cmsFreeToneCurve(bp.KTone); + + return Result; - Rel2RelStepAbsCoefs(AdaptationState, - BlackPointIn, - WhitePointIn, IlluminantIn, - ChromaticAdaptationMatrixIn, - BlackPointOut, - WhitePointOut, cmsD50_XYZ(), - ChromaticAdaptationMatrixOut, - m, of); - *fn1 = Lab2XYZ2Lab; - } - else - { // Lab -> Lab relative don't need any adjust unless - // black point compensation +Error: + + if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk); + if (bp.KTone != NULL) cmsFreeToneCurve(bp.KTone); + if (Result != NULL) cmsPipelineFree(Result); + return NULL; + +} + +// K Plane-preserving CMYK to CMYK ------------------------------------------------------------------------------------ - *fn1 = NULL; - if (DoBlackPointCompensation) { +typedef struct { - *fn1 = Lab2XYZ2Lab; - ComputeBlackPointCompensationFactors(BlackPointIn, - WhitePointIn, - IlluminantIn, - BlackPointOut, - WhitePointOut, - IlluminantOut, - m, of); + cmsPipeline* cmyk2cmyk; // The original transform + cmsHTRANSFORM hProofOutput; // Output CMYK to Lab (last profile) + cmsHTRANSFORM cmyk2Lab; // The input chain + cmsToneCurve* KTone; // Black-to-black tone curve + cmsPipeline* LabK2cmyk; // The output profile + cmsFloat64Number MaxError; + + cmsHTRANSFORM hRoundTrip; + cmsFloat64Number MaxTAC; - } - } - break; +} PreserveKPlaneParams; - default: return FALSE; - } +// The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision +static +int BlackPreservingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + int i; + cmsFloat32Number Inf[4], Outf[4]; + cmsFloat32Number LabK[4]; + cmsFloat64Number SumCMY, SumCMYK, Error, Ratio; + cmsCIELab ColorimetricLab, BlackPreservingLab; + PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo; + + // Convert from 16 bits to floating point + for (i=0; i < 4; i++) + Inf[i] = (cmsFloat32Number) (In[i] / 65535.0); + + // Get the K across Tone curve + LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]); + + // If going across black only, keep black only + if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + + Out[0] = Out[1] = Out[2] = 0; + Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0); + return TRUE; + } + + // Try the original transform, + cmsPipelineEvalFloat( Inf, Outf, bp ->cmyk2cmyk); + + // Store a copy of the floating point result into 16-bit + for (i=0; i < 4; i++) + Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0); + + // Maybe K is already ok (mostly on K=0) + if ( fabs(Outf[3] - LabK[3]) < (3.0 / 65535.0) ) { + return TRUE; + } + + // K differ, mesure and keep Lab measurement for further usage + // this is done in relative colorimetric intent + cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); - return TRUE; + // Is not black only and the transform doesn't keep black. + // Obtain the Lab of output CMYK. After that we have Lab + K + cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1); + + // Obtain the corresponding CMY using reverse interpolation + // (K is fixed in LabK[3]) + if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) { + + // Cannot find a suitable value, so use colorimetric xform + // which is already stored in Out[] + return TRUE; + } + + // Make sure to pass through K (which now is fixed) + Outf[3] = LabK[3]; + + // Apply TAC if needed + SumCMY = Outf[0] + Outf[1] + Outf[2]; + SumCMYK = SumCMY + Outf[3]; + + if (SumCMYK > bp ->MaxTAC) { + + Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); + if (Ratio < 0) + Ratio = 0; + } + else + Ratio = 1.0; + + Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0); // C + Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0); // M + Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0); // Y + Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0); + + // Estimate the error (this goes 16 bits to Lab DBL) + cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); + Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); + if (Error > bp -> MaxError) + bp->MaxError = Error; + + return TRUE; } +// This is the entry for black-plane preserving, which are non-ICC +static +cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + PreserveKPlaneParams bp; + cmsPipeline* Result = NULL; + cmsUInt32Number ICCIntents[256]; + cmsStage* CLUT; + cmsUInt32Number i, nGridPoints; + cmsHPROFILE hLab; -// This function does calculate the necessary conversion operations -// needed from transpassing data from a LUT to a LUT. The conversion -// is modeled as a pointer of function and two coefficients, a and b -// The function is actually called only if not null pointer is provided, -// and the two paramaters are passed in. There are several types of -// conversions, but basically they do a linear scalling and a interchange + // Sanity check + if (nProfiles < 1 || nProfiles > 255) return NULL; + + // Translate black-preserving intents to ICC ones + for (i=0; i < nProfiles; i++) + ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); + // Check for non-cmyk profiles + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData || + cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass)) + return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); + + // Allocate an empty LUT for holding the result + Result = cmsPipelineAlloc(ContextID, 4, 4); + if (Result == NULL) return NULL; -// Main dispatcher - -int cmsChooseCnvrt(int Absolute, - int Phase1, LPcmsCIEXYZ BlackPointIn, - LPcmsCIEXYZ WhitePointIn, - LPcmsCIEXYZ IlluminantIn, - LPMAT3 ChromaticAdaptationMatrixIn, + memset(&bp, 0, sizeof(bp)); - int Phase2, LPcmsCIEXYZ BlackPointOut, - LPcmsCIEXYZ WhitePointOut, - LPcmsCIEXYZ IlluminantOut, - LPMAT3 ChromaticAdaptationMatrixOut, + // We need the input LUT of the last profile, assuming this one is responsible of + // black generation. This LUT will be searched in inverse order. + bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC); + if (bp.LabK2cmyk == NULL) goto Cleanup; - int DoBlackPointCompensation, - double AdaptationState, - _cmsADJFN *fn1, - LPWMAT3 wm, LPWVEC3 wof) -{ - - int rc; - MAT3 m; - VEC3 of; + // Get total area coverage (in 0..1 domain) + bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0; + if (bp.MaxTAC <= 0) goto Cleanup; - MAT3identity(&m); - VEC3init(&of, 0, 0, 0); + // Create a LUT holding normal ICC transform + bp.cmyk2cmyk = DefaultICCintents(ContextID, + nProfiles, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + if (bp.cmyk2cmyk == NULL) goto Cleanup; - switch (Phase1) { - - // Input LUT is giving XYZ relative values. + // Now the tone curve + bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + if (bp.KTone == NULL) goto Cleanup; - case XYZRel: rc = FromXYZRelLUT(Absolute, - BlackPointIn, - WhitePointIn, - IlluminantIn, - ChromaticAdaptationMatrixIn, - Phase2, - BlackPointOut, - WhitePointOut, - IlluminantOut, - ChromaticAdaptationMatrixOut, - DoBlackPointCompensation, - AdaptationState, - fn1, &m, &of); - break; + // To measure the output, Last profile to Lab + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], + CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + if ( bp.hProofOutput == NULL) goto Cleanup; + // Same as anterior, but lab in the 0..1 range + bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], + FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, + FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + if (bp.cmyk2Lab == NULL) goto Cleanup; + cmsCloseProfile(hLab); + + // Error estimation (for debug only) + bp.MaxError = 0; + + // How many gridpoints are we going to use? + nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); - // Input LUT is giving Lab relative values + CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); + if (CLUT == NULL) goto Cleanup; + + if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) + goto Cleanup; + + cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); + +Cleanup: + + if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); + if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); + if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); + + if (bp.KTone) cmsFreeToneCurve(bp.KTone); + if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); + + return Result; +} + +// Link routines ------------------------------------------------------------------------------------------------------ + +// Chain several profiles into a single LUT. It just checks the parameters and then calls the handler +// for the first intent in chain. The handler may be user-defined. Is up to the handler to deal with the +// rest of intents in chain. A maximum of 255 profiles at time are supported, which is pretty reasonable. +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsIntentsList* Intent; + + // Make sure a reasonable number of profiles is provided + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles); + return NULL; + } - case LabRel: rc = FromLabRelLUT(Absolute, - BlackPointIn, - WhitePointIn, - IlluminantIn, - ChromaticAdaptationMatrixIn, - Phase2, - BlackPointOut, - WhitePointOut, - IlluminantOut, - ChromaticAdaptationMatrixOut, - DoBlackPointCompensation, - AdaptationState, - fn1, &m, &of); - break; + for (i=0; i < nProfiles; i++) { + + // Check if black point is really needed or allowed. Note that + // following Adobe's document: + // BPC does not apply to devicelink profiles, nor to abs colorimetric, + // and applies always on V4 perceptual and saturation. + + if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC) + BPC[i] = FALSE; + + if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) { + + // Force BPC for V4 profiles in perceptual and saturation + if (cmsGetEncodedICCversion(hProfiles[i]) >= 0x4000000) + BPC[i] = TRUE; + } + } + // Search for a handler. The first intent in the chain defines the handler. That would + // prevent using multiple custom intents in a multiintent chain, but the behaviour of + // this case would present some issues if the custom intent tries to do things like + // preserve primaries. This solution is not perfect, but works well on most cases. + Intent = SearchIntent(ContextID, TheIntents[0]); + if (Intent == NULL) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); + return NULL; + } + + // Call the handler + return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); +} + +// ------------------------------------------------------------------------------------------------- + +// Get information about available intents. nMax is the maximum space for the supplied "Codes" +// and "Descriptions" the function returns the total number of intents, which may be greater +// than nMax, although the matrices are not populated beyond this level. +cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); + cmsIntentsList* pt; + cmsUInt32Number nIntents; - // Unrecognized combination + for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) + { + if (nIntents < nMax) { + if (Codes != NULL) + Codes[nIntents] = pt ->Intent; + + if (Descriptions != NULL) + Descriptions[nIntents] = pt ->Description; + } - default: cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Phase error"); - return FALSE; + nIntents++; + } - } + for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next) + { + if (nIntents < nMax) { + if (Codes != NULL) + Codes[nIntents] = pt ->Intent; + + if (Descriptions != NULL) + Descriptions[nIntents] = pt ->Description; + } - MAT3toFix(wm, &m); - VEC3toFix(wof, &of); + nIntents++; + } + return nIntents; +} - // Do some optimization -- discard conversion if identity parameters. +cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +{ + return cmsGetSupportedIntentsTHR(NULL, nMax, Codes, Descriptions); +} - if (*fn1 == XYZ2XYZ || *fn1 == Lab2XYZ2Lab) { +// The plug-in registration. User can add new intents or override default routines +cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext id, cmsPluginBase* Data) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(id, IntentPlugin); + cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data; + cmsIntentsList* fl; - if (IdentityParameters(wm, wof)) - *fn1 = NULL; - } + // Do we have to reset the custom intents? + if (Data == NULL) { + + ctx->Intents = NULL; + return TRUE; + } + + fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); + if (fl == NULL) return FALSE; - return rc; + fl ->Intent = Plugin ->Intent; + strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1); + fl ->Description[sizeof(fl ->Description)-1] = 0; + + fl ->Link = Plugin ->Link; + + fl ->Next = ctx ->Intents; + ctx ->Intents = fl; + + return TRUE; } - - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmserr.c --- a/src/share/native/sun/java2d/cmm/lcms/cmserr.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmserr.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,92 +49,644 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - +// +//--------------------------------------------------------------------------------- -#include "lcms.h" +#include "lcms2_internal.h" -// As a rule, only the functions visible from API can signal -// errors. +// This function is here to help applications to prevent mixing lcms versions on header and shared objects. +int CMSEXPORT cmsGetEncodedCMMversion(void) +{ + return LCMS_VERSION; +} + +// I am so tired about incompatibilities on those functions that here are some replacements +// that hopefully would be fully portable. + +// compare two strings ignoring case +int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2) +{ + register const unsigned char *us1 = (const unsigned char *)s1, + *us2 = (const unsigned char *)s2; + + while (toupper(*us1) == toupper(*us2++)) + if (*us1++ == '\0') + return 0; + + return (toupper(*us1) - toupper(*--us2)); +} + +// long int because C99 specifies ftell in such way (7.19.9.2) +long int CMSEXPORT cmsfilelength(FILE* f) +{ + long int p , n; + + p = ftell(f); // register current file position + if (p == -1L) + return -1L; + + if (fseek(f, 0, SEEK_END) != 0) { + return -1L; + } + + n = ftell(f); + fseek(f, p, SEEK_SET); // file position restored + + return n; +} + -void cdecl cmsSignalError(int ErrorCode, const char *ErrorText, ...); +// Memory handling ------------------------------------------------------------------ +// +// This is the interface to low-level memory management routines. By default a simple +// wrapping to malloc/free/realloc is provided, although there is a limit on the max +// amount of memoy that can be reclaimed. This is mostly as a safety feature to prevent +// bogus or evil code to allocate huge blocks that otherwise lcms would never need. + +#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U)) + +// User may override this behaviour by using a memory plug-in, which basically replaces +// the default memory management functions. In this case, no check is performed and it +// is up to the plug-in writter to keep in the safe side. There are only three functions +// required to be implemented: malloc, realloc and free, although the user may want to +// replace the optional mallocZero, calloc and dup as well. + +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// ********************************************************************************* -int LCMSEXPORT cmsErrorAction(int lAbort); -void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn); +// This is the default memory allocation function. It does a very coarse +// check of amount of memory, just to prevent exploits +static +void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size) +{ + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum + + return (void*) malloc(size); + + cmsUNUSED_PARAMETER(ContextID); +} + +// Generic allocate & zero +static +void* _cmsMallocZeroDefaultFn(cmsContext ContextID, cmsUInt32Number size) +{ + void *pt = _cmsMalloc(ContextID, size); + if (pt == NULL) return NULL; + + memset(pt, 0, size); + return pt; +} -// ****************************************************************** +// The default free function. The only check proformed is against NULL pointers +static +void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr) +{ + // free(NULL) is defined a no-op by C99, therefore it is safe to + // avoid the check, but it is here just in case... + + if (Ptr) free(Ptr); + + cmsUNUSED_PARAMETER(ContextID); +} -static int nDoAbort = LCMS_ERROR_ABORT; -static cmsErrorHandlerFunction UserErrorHandler = (cmsErrorHandlerFunction) NULL; +// The default realloc function. Again it checks for exploits. If Ptr is NULL, +// realloc behaves the same way as malloc and allocates a new block of size bytes. +static +void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size) +{ + + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never realloc over 512Mb + + return realloc(Ptr, size); + + cmsUNUSED_PARAMETER(ContextID); +} -int LCMSEXPORT cmsErrorAction(int nAction) +// The default calloc function. Allocates an array of num elements, each one of size bytes +// all memory is initialized to zero. +static +void* _cmsCallocDefaultFn(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) { - int nOld = nDoAbort; - nDoAbort = nAction; + cmsUInt32Number Total = num * size; + + // Preserve calloc behaviour + if (Total == 0) return NULL; + + // Safe check for overflow. + if (num >= UINT_MAX / size) return NULL; + + // Check for overflow + if (Total < num || Total < size) { + return NULL; + } + + if (Total > MAX_MEMORY_FOR_ALLOC) return NULL; // Never alloc over 512Mb - return nOld; + return _cmsMallocZero(ContextID, Total); +} + +// Generic block duplication +static +void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number size) +{ + void* mem; + + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never dup over 512Mb + + mem = _cmsMalloc(ContextID, size); + + if (mem != NULL && Org != NULL) + memmove(mem, Org, size); + + return mem; } -void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn) + +// Pointers to memory manager functions in Context0 +_cmsMemPluginChunkType _cmsMemPluginChunk = { _cmsMallocDefaultFn, _cmsMallocZeroDefaultFn, _cmsFreeDefaultFn, + _cmsReallocDefaultFn, _cmsCallocDefaultFn, _cmsDupDefaultFn + }; + + +// Reset and duplicate memory manager +void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src) { - UserErrorHandler = Fn; + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Duplicate + ctx ->chunks[MemPlugin] = _cmsSubAllocDup(ctx ->MemPool, src ->chunks[MemPlugin], sizeof(_cmsMemPluginChunkType)); + } + else { + + // To reset it, we use the default allocators, which cannot be overridden + ctx ->chunks[MemPlugin] = &ctx ->DefaultMemoryManager; + } +} + +// Auxiliary to fill memory management functions from plugin (or context 0 defaults) +void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr) +{ + if (Plugin == NULL) { + + memcpy(ptr, &_cmsMemPluginChunk, sizeof(_cmsMemPluginChunk)); + } + else { + + ptr ->MallocPtr = Plugin -> MallocPtr; + ptr ->FreePtr = Plugin -> FreePtr; + ptr ->ReallocPtr = Plugin -> ReallocPtr; + + // Make sure we revert to defaults + ptr ->MallocZeroPtr= _cmsMallocZeroDefaultFn; + ptr ->CallocPtr = _cmsCallocDefaultFn; + ptr ->DupPtr = _cmsDupDefaultFn; + + if (Plugin ->MallocZeroPtr != NULL) ptr ->MallocZeroPtr = Plugin -> MallocZeroPtr; + if (Plugin ->CallocPtr != NULL) ptr ->CallocPtr = Plugin -> CallocPtr; + if (Plugin ->DupPtr != NULL) ptr ->DupPtr = Plugin -> DupPtr; + + } } -// Default error handler +// Plug-in replacement entry +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase *Data) +{ + cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data; + _cmsMemPluginChunkType* ptr; + + // NULL forces to reset to defaults. In this special case, the defaults are stored in the context structure. + // Remaining plug-ins does NOT have any copy in the context structure, but this is somehow special as the + // context internal data should be malloce'd by using those functions. + if (Data == NULL) { + + struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID; + + // Return to the default allocators + if (ContextID != NULL) { + ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager; + } + return TRUE; + } + + // Check for required callbacks + if (Plugin -> MallocPtr == NULL || + Plugin -> FreePtr == NULL || + Plugin -> ReallocPtr == NULL) return FALSE; + + // Set replacement functions + ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + if (ptr == NULL) + return FALSE; + + _cmsInstallAllocFunctions(Plugin, ptr); + return TRUE; +} + +// Generic allocate +void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr ->MallocPtr(ContextID, size); +} + +// Generic allocate & zero +void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->MallocZeroPtr(ContextID, size); +} + +// Generic calloc +void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->CallocPtr(ContextID, num, size); +} + +// Generic reallocate +void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->ReallocPtr(ContextID, Ptr, size); +} + +// Generic free memory +void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr) +{ + if (Ptr != NULL) { + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + ptr ->FreePtr(ContextID, Ptr); + } +} + +// Generic block duplication +void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr ->DupPtr(ContextID, Org, size); +} + +// ******************************************************************************************** + +// Sub allocation takes care of many pointers of small size. The memory allocated in +// this way have be freed at once. Next function allocates a single chunk for linked list +// I prefer this method over realloc due to the big inpact on xput realloc may have if +// memory is being swapped to disk. This approach is safer (although that may not be true on all platforms) +static +_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial) +{ + _cmsSubAllocator_chunk* chunk; + + // 20K by default + if (Initial == 0) + Initial = 20*1024; + + // Create the container + chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk)); + if (chunk == NULL) return NULL; + + // Initialize values + chunk ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, Initial); + if (chunk ->Block == NULL) { + + // Something went wrong + _cmsFree(ContextID, chunk); + return NULL; + } + + chunk ->BlockSize = Initial; + chunk ->Used = 0; + chunk ->next = NULL; + + return chunk; +} + +// The suballocated is nothing but a pointer to the first element in the list. We also keep +// the thread ID in this structure. +_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial) +{ + _cmsSubAllocator* sub; + + // Create the container + sub = (_cmsSubAllocator*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator)); + if (sub == NULL) return NULL; + + sub ->ContextID = ContextID; + + sub ->h = _cmsCreateSubAllocChunk(ContextID, Initial); + if (sub ->h == NULL) { + _cmsFree(ContextID, sub); + return NULL; + } + + return sub; +} + + +// Get rid of whole linked list +void _cmsSubAllocDestroy(_cmsSubAllocator* sub) +{ + _cmsSubAllocator_chunk *chunk, *n; + + for (chunk = sub ->h; chunk != NULL; chunk = n) { + + n = chunk->next; + if (chunk->Block != NULL) _cmsFree(sub ->ContextID, chunk->Block); + _cmsFree(sub ->ContextID, chunk); + } + + // Free the header + _cmsFree(sub ->ContextID, sub); +} + + +// Get a pointer to small memory block. +void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size) +{ + cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used; + cmsUInt8Number* ptr; + + size = _cmsALIGNMEM(size); + + // Check for memory. If there is no room, allocate a new chunk of double memory size. + if (size > Free) { + + _cmsSubAllocator_chunk* chunk; + cmsUInt32Number newSize; + + newSize = sub -> h ->BlockSize * 2; + if (newSize < size) newSize = size; + + chunk = _cmsCreateSubAllocChunk(sub -> ContextID, newSize); + if (chunk == NULL) return NULL; + + // Link list + chunk ->next = sub ->h; + sub ->h = chunk; + + } + + ptr = sub -> h ->Block + sub -> h ->Used; + sub -> h -> Used += size; + + return (void*) ptr; +} + +// Duplicate in pool +void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size) +{ + void *NewPtr; + + // Dup of null pointer is also NULL + if (ptr == NULL) + return NULL; + + NewPtr = _cmsSubAlloc(s, size); + + if (ptr != NULL && NewPtr != NULL) { + memcpy(NewPtr, ptr, size); + } + + return NewPtr; +} + -void cmsSignalError(int ErrorCode, const char *ErrorText, ...) -{ - va_list args; +// Error logging ****************************************************************** + +// There is no error handling at all. When a function fails, it returns proper value. +// For example, all create functions does return NULL on failure. Other return FALSE +// It may be interesting, for the developer, to know why the function is failing. +// for that reason, lcms2 does offer a logging function. This function does recive +// a ENGLISH string with some clues on what is going wrong. You can show this +// info to the end user, or just create some sort of log. +// The logging function should NOT terminate the program, as this obviously can leave +// resources. It is the programmer's responsibility to check each function return code +// to make sure it didn't fail. + +// Error messages are limited to MAX_ERROR_MESSAGE_LEN + +#define MAX_ERROR_MESSAGE_LEN 1024 + +// --------------------------------------------------------------------------------------------------------- + +// This is our default log error +static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); + +// Context0 storage, which is global +_cmsLogErrorChunkType _cmsLogErrorChunk = { DefaultLogErrorHandlerFunction }; - if (nDoAbort == LCMS_ERROR_IGNORE) return; +// Allocates and inits error logger container for a given context. If src is NULL, only initializes the value +// to the default. Otherwise, it duplicates the value. The interface is standard across all context clients +void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsLogErrorChunkType LogErrorChunk = { DefaultLogErrorHandlerFunction }; + void* from; + + if (src != NULL) { + from = src ->chunks[Logger]; + } + else { + from = &LogErrorChunk; + } - va_start(args, ErrorText); + ctx ->chunks[Logger] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsLogErrorChunkType)); +} - if (UserErrorHandler != NULL) { +// The default error logger does nothing. +static +void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text) +{ + // fprintf(stderr, "[lcms]: %s\n", Text); + // fflush(stderr); + + cmsUNUSED_PARAMETER(ContextID); + cmsUNUSED_PARAMETER(ErrorCode); + cmsUNUSED_PARAMETER(Text); +} - char Buffer[1024]; +// Change log error, context based +void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn) +{ + _cmsLogErrorChunkType* lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger); + + if (lhg != NULL) { + + if (Fn == NULL) + lhg -> LogErrorHandler = DefaultLogErrorHandlerFunction; + else + lhg -> LogErrorHandler = Fn; + } +} - vsnprintf(Buffer, 1023, ErrorText, args); - va_end(args); +// Change log error, legacy +void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn) +{ + cmsSetLogErrorHandlerTHR(NULL, Fn); +} + +// Log an error +// ErrorText is a text holding an english description of error. +void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...) +{ + va_list args; + char Buffer[MAX_ERROR_MESSAGE_LEN]; + _cmsLogErrorChunkType* lhg; + - if (UserErrorHandler(ErrorCode, Buffer)) { + va_start(args, ErrorText); + vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args); + va_end(args); + + // Check for the context, if specified go there. If not, go for the global + lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger); + if (lhg ->LogErrorHandler) { + lhg ->LogErrorHandler(ContextID, ErrorCode, Buffer); + } +} - return; - } - } +// Utility function to print signatures +void _cmsTagSignature2String(char String[5], cmsTagSignature sig) +{ + cmsUInt32Number be; + + // Convert to big endian + be = _cmsAdjustEndianess32((cmsUInt32Number) sig); -#if defined( __CONSOLE__ ) || defined( NON_WINDOWS ) + // Move chars + memmove(String, &be, 4); + + // Make sure of terminator + String[4] = 0; +} + +//-------------------------------------------------------------------------------------------------- + - fprintf(stderr, "lcms: Error #%d; ", ErrorCode); - vfprintf(stderr, ErrorText, args); - fprintf(stderr, "\n"); - va_end(args); +static +void* defMtxCreate(cmsContext id) +{ + _cmsMutex* ptr_mutex = (_cmsMutex*) _cmsMalloc(id, sizeof(_cmsMutex)); + _cmsInitMutexPrimitive(ptr_mutex); + return (void*) ptr_mutex; +} + +static +void defMtxDestroy(cmsContext id, void* mtx) +{ + _cmsDestroyMutexPrimitive((_cmsMutex *) mtx); + _cmsFree(id, mtx); +} + +static +cmsBool defMtxLock(cmsContext id, void* mtx) +{ + cmsUNUSED_PARAMETER(id); + return _cmsLockPrimitive((_cmsMutex *) mtx) == 0; +} + +static +void defMtxUnlock(cmsContext id, void* mtx) +{ + cmsUNUSED_PARAMETER(id); + _cmsUnlockPrimitive((_cmsMutex *) mtx); +} - if (nDoAbort == LCMS_ERROR_ABORT) exit(1); -#else - { - char Buffer1[1024]; - char Buffer2[256]; + + +// Pointers to memory manager functions in Context0 +_cmsMutexPluginChunkType _cmsMutexPluginChunk = { defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; + +// Allocate and init mutex container. +void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsMutexPluginChunkType MutexChunk = {defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; + void* from; + + if (src != NULL) { + from = src ->chunks[MutexPlugin]; + } + else { + from = &MutexChunk; + } + + ctx ->chunks[MutexPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsMutexPluginChunkType)); +} + +// Register new ways to transform +cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginMutex* Plugin = (cmsPluginMutex*) Data; + _cmsMutexPluginChunkType* ctx = ( _cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (Data == NULL) { - snprintf(Buffer1, 767, "Error #%x; ", ErrorCode); - vsnprintf(Buffer2, 255, ErrorText, args); - strcat(Buffer1, Buffer2); - MessageBox(NULL, Buffer1, "Little cms", - MB_OK|MB_ICONSTOP|MB_TASKMODAL); - va_end(args); + // No lock routines + ctx->CreateMutexPtr = NULL; + ctx->DestroyMutexPtr = NULL; + ctx->LockMutexPtr = NULL; + ctx ->UnlockMutexPtr = NULL; + return TRUE; + } + + // Factory callback is required + if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL || + Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE; + - if (nDoAbort == LCMS_ERROR_ABORT) { + ctx->CreateMutexPtr = Plugin->CreateMutexPtr; + ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr; + ctx ->LockMutexPtr = Plugin ->LockMutexPtr; + ctx ->UnlockMutexPtr = Plugin ->UnlockMutexPtr; + + // All is ok + return TRUE; +} + +// Generic Mutex fns +void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->CreateMutexPtr == NULL) return NULL; -#ifdef __BORLANDC__ - _cexit(); -#endif + return ptr ->CreateMutexPtr(ContextID); +} + +void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->DestroyMutexPtr != NULL) { + + ptr ->DestroyMutexPtr(ContextID, mtx); + } +} - FatalAppExit(0, "lcms is terminating application"); - } - } -#endif +cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->LockMutexPtr == NULL) return TRUE; + + return ptr ->LockMutexPtr(ContextID, mtx); } + +void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->UnlockMutexPtr != NULL) { + + ptr ->UnlockMutexPtr(ContextID, mtx); + } +} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsgamma.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsgamma.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsgamma.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2013 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,448 +49,1068 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +#include "lcms2_internal.h" +// Tone curves are powerful constructs that can contain curves specified in diverse ways. +// The curve is stored in segments, where each segment can be sampled or specified by parameters. +// a 16.bit simplification of the *whole* curve is kept for optimization purposes. For float operation, +// each segment is evaluated separately. Plug-ins may be used to define new parametric schemes, +// each plug-in may define up to MAX_TYPES_IN_LCMS_PLUGIN functions types. For defining a function, +// the plug-in should provide the type id, how many parameters each type has, and a pointer to +// a procedure that evaluates the function. In the case of reverse evaluation, the evaluator will +// be called with the type id as a negative value, and a sampled version of the reversed curve +// will be built. + +// ----------------------------------------------------------------- Implementation +// Maxim number of nodes +#define MAX_NODES_IN_CURVE 4097 +#define MINUS_INF (-1E22F) +#define PLUS_INF (+1E22F) -#include "lcms.h" +// The list of supported parametric curves +typedef struct _cmsParametricCurvesCollection_st { + + cmsUInt32Number nFunctions; // Number of supported functions in this chunk + cmsInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types + cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function + + cmsParametricCurveEvaluator Evaluator; // The evaluator + + struct _cmsParametricCurvesCollection_st* Next; // Next in list -// Gamma handling. +} _cmsParametricCurvesCollection; + +// This is the default (built-in) evaluator +static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R); + +// The built-in list +static _cmsParametricCurvesCollection DefaultCurves = { + 9, // # of curve types + { 1, 2, 3, 4, 5, 6, 7, 8, 108 }, // Parametric curve ID + { 1, 3, 4, 5, 7, 4, 5, 5, 1 }, // Parameters by type + DefaultEvalParametricFn, // Evaluator + NULL // Next in chain +}; -LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries); -void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma); -void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3]); -LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma); -LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE Src); -LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma); -LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[]); -LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma); -LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma, int nPoints); -LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda); +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginCurvesList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsCurvesPluginChunkType newHead = { NULL }; + _cmsParametricCurvesCollection* entry; + _cmsParametricCurvesCollection* Anterior = NULL; + _cmsCurvesPluginChunkType* head = (_cmsCurvesPluginChunkType*) src->chunks[CurvesPlugin]; + + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->ParametricCurves; + entry != NULL; + entry = entry ->Next) { + + _cmsParametricCurvesCollection *newEntry = ( _cmsParametricCurvesCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsParametricCurvesCollection)); + + if (newEntry == NULL) + return; -LCMSBOOL cdecl _cmsSmoothEndpoints(LPWORD Table, int nPoints); + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.ParametricCurves == NULL) + newHead.ParametricCurves = newEntry; + } + + ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsCurvesPluginChunkType)); +} + +// The allocator have to follow the chain +void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Copy all linked list + DupPluginCurvesList(ctx, src); + } + else { + static _cmsCurvesPluginChunkType CurvesPluginChunk = { NULL }; + ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx ->MemPool, &CurvesPluginChunk, sizeof(_cmsCurvesPluginChunkType)); + } +} -// Sampled curves +// The linked list head +_cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL }; -LPSAMPLEDCURVE cdecl cmsAllocSampledCurve(int nItems); -void cdecl cmsFreeSampledCurve(LPSAMPLEDCURVE p); -void cdecl cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max); -void cdecl cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max); -LCMSBOOL cdecl cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double SmoothingLambda); -void cdecl cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints); +// As a way to install new parametric curves +cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); + cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data; + _cmsParametricCurvesCollection* fl; + + if (Data == NULL) { + + ctx -> ParametricCurves = NULL; + return TRUE; + } + + fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection)); + if (fl == NULL) return FALSE; -LPSAMPLEDCURVE cdecl cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints); + // Copy the parameters + fl ->Evaluator = Plugin ->Evaluator; + fl ->nFunctions = Plugin ->nFunctions; -double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t); -double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold); + // Make sure no mem overwrites + if (fl ->nFunctions > MAX_TYPES_IN_LCMS_PLUGIN) + fl ->nFunctions = MAX_TYPES_IN_LCMS_PLUGIN; -// ---------------------------------------------------------------------------------------- - + // Copy the data + memmove(fl->FunctionTypes, Plugin ->FunctionTypes, fl->nFunctions * sizeof(cmsUInt32Number)); + memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number)); -#define MAX_KNOTS 4096 -typedef float vec[MAX_KNOTS+1]; + // Keep linked list + fl ->Next = ctx->ParametricCurves; + ctx->ParametricCurves = fl; + + // All is ok + return TRUE; +} -// Ciclic-redundant-check for assuring table is a true representation of parametric curve - -// The usual polynomial, which is used for AAL5, FDDI, and probably Ethernet -#define QUOTIENT 0x04c11db7 - +// Search in type list, return position or -1 if not found static -unsigned int Crc32(unsigned int result, LPVOID ptr, int len) +int IsInSet(int Type, _cmsParametricCurvesCollection* c) { - int i,j; - BYTE octet; - LPBYTE data = (LPBYTE) ptr; + int i; - for (i=0; i < len; i++) { + for (i=0; i < (int) c ->nFunctions; i++) + if (abs(Type) == c ->FunctionTypes[i]) return i; + + return -1; +} + - octet = *data++; +// Search for the collection which contains a specific type +static +_cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index) +{ + _cmsParametricCurvesCollection* c; + int Position; + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); - for (j=0; j < 8; j++) { + for (c = ctx->ParametricCurves; c != NULL; c = c ->Next) { - if (result & 0x80000000) { + Position = IsInSet(Type, c); - result = (result << 1) ^ QUOTIENT ^ (octet >> 7); - } - else - { - result = (result << 1) ^ (octet >> 7); - } - octet <<= 1; + if (Position != -1) { + if (index != NULL) + *index = Position; + return c; + } + } + // If none found, revert for defaults + for (c = &DefaultCurves; c != NULL; c = c ->Next) { + + Position = IsInSet(Type, c); + + if (Position != -1) { + if (index != NULL) + *index = Position; + return c; } } - return result; + return NULL; } -// Get CRC of gamma table +// Low level allocate, which takes care of memory details. nEntries may be zero, and in this case +// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the +// optimization curve is given. Both features simultaneously is an error +static +cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEntries, + cmsUInt32Number nSegments, const cmsCurveSegment* Segments, + const cmsUInt16Number* Values) +{ + cmsToneCurve* p; + cmsUInt32Number i; + + // We allow huge tables, which are then restricted for smoothing operations + if (nEntries > 65530) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve of more than 65530 entries"); + return NULL; + } -unsigned int _cmsCrc32OfGammaTable(LPGAMMATABLE Table) -{ - unsigned int crc = ~0U; + if (nEntries == 0 && nSegments == 0) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve with zero segments and no table"); + return NULL; + } + + // Allocate all required pointers, etc. + p = (cmsToneCurve*) _cmsMallocZero(ContextID, sizeof(cmsToneCurve)); + if (!p) return NULL; + + // In this case, there are no segments + if (nSegments == 0) { + p ->Segments = NULL; + p ->Evals = NULL; + } + else { + p ->Segments = (cmsCurveSegment*) _cmsCalloc(ContextID, nSegments, sizeof(cmsCurveSegment)); + if (p ->Segments == NULL) goto Error; + + p ->Evals = (cmsParametricCurveEvaluator*) _cmsCalloc(ContextID, nSegments, sizeof(cmsParametricCurveEvaluator)); + if (p ->Evals == NULL) goto Error; + } + + p -> nSegments = nSegments; - crc = Crc32(crc, &Table -> Seed.Type, sizeof(int)); - crc = Crc32(crc, Table ->Seed.Params, sizeof(double)*10); - crc = Crc32(crc, &Table ->nEntries, sizeof(int)); - crc = Crc32(crc, Table ->GammaTable, sizeof(WORD) * Table -> nEntries); + // This 16-bit table contains a limited precision representation of the whole curve and is kept for + // increasing xput on certain operations. + if (nEntries == 0) { + p ->Table16 = NULL; + } + else { + p ->Table16 = (cmsUInt16Number*) _cmsCalloc(ContextID, nEntries, sizeof(cmsUInt16Number)); + if (p ->Table16 == NULL) goto Error; + } + + p -> nEntries = nEntries; + + // Initialize members if requested + if (Values != NULL && (nEntries > 0)) { + + for (i=0; i < nEntries; i++) + p ->Table16[i] = Values[i]; + } + + // Initialize the segments stuff. The evaluator for each segment is located and a pointer to it + // is placed in advance to maximize performance. + if (Segments != NULL && (nSegments > 0)) { + + _cmsParametricCurvesCollection *c; + + p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*)); + if (p ->SegInterp == NULL) goto Error; - return ~crc; + for (i=0; i < nSegments; i++) { + + // Type 0 is a special marker for table-based curves + if (Segments[i].Type == 0) + p ->SegInterp[i] = _cmsComputeInterpParams(ContextID, Segments[i].nGridPoints, 1, 1, NULL, CMS_LERP_FLAGS_FLOAT); + + memmove(&p ->Segments[i], &Segments[i], sizeof(cmsCurveSegment)); + + if (Segments[i].Type == 0 && Segments[i].SampledPoints != NULL) + p ->Segments[i].SampledPoints = (cmsFloat32Number*) _cmsDupMem(ContextID, Segments[i].SampledPoints, sizeof(cmsFloat32Number) * Segments[i].nGridPoints); + else + p ->Segments[i].SampledPoints = NULL; + + c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL); + if (c != NULL) + p ->Evals[i] = c ->Evaluator; + } + } + + p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS); + if (p->InterpParams != NULL) + return p; + +Error: + if (p -> Segments) _cmsFree(ContextID, p ->Segments); + if (p -> Evals) _cmsFree(ContextID, p -> Evals); + if (p ->Table16) _cmsFree(ContextID, p ->Table16); + _cmsFree(ContextID, p); + return NULL; } -LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries) +// Parametric Fn using floating point +static +cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R) { - LPGAMMATABLE p; - size_t size; + cmsFloat64Number e, Val, disc; + + switch (Type) { + + // X = Y ^ Gamma + case 1: + if (R < 0) { + + if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) + Val = R; + else + Val = 0; + } + else + Val = pow(R, Params[0]); + break; + + // Type 1 Reversed: X = Y ^1/gamma + case -1: + if (R < 0) { + + if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) + Val = R; + else + Val = 0; + } + else + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE) + Val = PLUS_INF; + else + Val = pow(R, 1 / Params[0]); + } + break; + + // CIE 122-1966 + // Y = (aX + b)^Gamma | X >= -b/a + // Y = 0 | else + case 2: + { + + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = -Params[2] / Params[1]; + + if (R >= disc) { + + e = Params[1] * R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]); + else + Val = 0; + } + else + Val = 0; + } + } + break; - if (nEntries > 65530 || nEntries <= 0) { - cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't create gammatable of more than 65530 entries"); - return NULL; - } + // Type 2 Reversed + // X = (Y ^1/g - b) / a + case -2: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + if (R < 0) + Val = 0; + else + Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1]; + + if (Val < 0) + Val = 0; + } + } + break; + + + // IEC 61966-3 + // Y = (aX + b)^Gamma | X <= -b/a + // Y = c | else + case 3: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = -Params[2] / Params[1]; + if (disc < 0) + disc = 0; + + if (R >= disc) { + + e = Params[1] * R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]) + Params[3]; + else + Val = 0; + } + else + Val = Params[3]; + } + } + break; + + + // Type 3 reversed + // X=((Y-c)^1/g - b)/a | (Y>=c) + // X=-b/a | (Y= Params[3]) { + + e = R - Params[3]; + + if (e > 0) + Val = (pow(e, 1 / Params[0]) - Params[2]) / Params[1]; + else + Val = 0; + } + else { + Val = -Params[2] / Params[1]; + } + } + } + break; + + + // IEC 61966-2.1 (sRGB) + // Y = (aX + b)^Gamma | X >= d + // Y = cX | X < d + case 4: + if (R >= Params[4]) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]); + else + Val = 0; + } + else + Val = R * Params[3]; + break; - size = sizeof(GAMMATABLE) + (sizeof(WORD) * (nEntries-1)); + // Type 4 reversed + // X=((Y^1/g-b)/a) | Y >= (ad+b)^g + // X=Y/c | Y< (ad+b)^g + case -4: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[3]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + e = Params[1] * Params[4] + Params[2]; + if (e < 0) + disc = 0; + else + disc = pow(e, Params[0]); + + if (R >= disc) { + + Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + else { + Val = R / Params[3]; + } + } + } + break; + + + // Y = (aX + b)^Gamma + e | X >= d + // Y = cX + f | X < d + case 5: + if (R >= Params[4]) { + + e = Params[1]*R + Params[2]; - p = (LPGAMMATABLE) _cmsMalloc(size); - if (!p) return NULL; + if (e > 0) + Val = pow(e, Params[0]) + Params[5]; + else + Val = Params[5]; + } + else + Val = R*Params[3] + Params[6]; + break; + + + // Reversed type 5 + // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e), cd+f + // X=(Y-f)/c | else + case -5: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[3]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = Params[3] * Params[4] + Params[6]; + if (R >= disc) { + + e = R - Params[5]; + if (e < 0) + Val = 0; + else + Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + else { + Val = (R - Params[6]) / Params[3]; + } + } + } + break; + + + // Types 6,7,8 comes from segmented curves as described in ICCSpecRevision_02_11_06_Float.pdf + // Type 6 is basically identical to type 5 without d + + // Y = (a * X + b) ^ Gamma + c + case 6: + e = Params[1]*R + Params[2]; + + if (e < 0) + Val = Params[3]; + else + Val = pow(e, Params[0]) + Params[3]; + break; - ZeroMemory(p, size); + // ((Y - c) ^1/Gamma - b) / a + case -6: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + e = R - Params[3]; + if (e < 0) + Val = 0; + else + Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + } + break; + + + // Y = a * log (b * X^Gamma + c) + d + case 7: + + e = Params[2] * pow(R, Params[0]) + Params[3]; + if (e <= 0) + Val = Params[4]; + else + Val = Params[1]*log10(e) + Params[4]; + break; + + // (Y - d) / a = log(b * X ^Gamma + c) + // pow(10, (Y-d) / a) = b * X ^Gamma + c + // pow((pow(10, (Y-d) / a) - c) / b, 1/g) = X + case -7: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[2]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + Val = pow((pow(10.0, (R - Params[4]) / Params[1]) - Params[3]) / Params[2], 1.0 / Params[0]); + } + } + break; + - p -> Seed.Type = 0; - p -> nEntries = nEntries; + //Y = a * b^(c*X+d) + e + case 8: + Val = (Params[0] * pow(Params[1], Params[2] * R + Params[3]) + Params[4]); + break; + + + // Y = (log((y-e) / a) / log(b) - d ) / c + // a=0, b=1, c=2, d=3, e=4, + case -8: - return p; + disc = R - Params[4]; + if (disc < 0) Val = 0; + else + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[2]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + Val = (log(disc / Params[0]) / log(Params[1]) - Params[3]) / Params[2]; + } + } + break; + + // S-Shaped: (1 - (1-x)^1/g)^1/g + case 108: + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE) + Val = 0; + else + Val = pow(1.0 - pow(1 - R, 1/Params[0]), 1/Params[0]); + break; + + // y = (1 - (1-x)^1/g)^1/g + // y^g = (1 - (1-x)^1/g) + // 1 - y^g = (1-x)^1/g + // (1 - y^g)^g = 1 - x + // 1 - (1 - y^g)^g + case -108: + Val = 1 - pow(1 - pow(R, Params[0]), Params[0]); + break; + + default: + // Unsupported parametric curve. Should never reach here + return 0; + } + + return Val; } -void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma) +// Evaluate a segmented function for a single value. Return -Inf if no valid segment found . +// If fn type is 0, perform an interpolation on the table +static +cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R) { - if (Gamma) _cmsFree(Gamma); -} + int i; + cmsFloat32Number Out32; + cmsFloat64Number Out; + + for (i = (int) g->nSegments - 1; i >= 0; --i) { + // Check for domain + if ((R > g->Segments[i].x0) && (R <= g->Segments[i].x1)) { + // Type == 0 means segment is sampled + if (g->Segments[i].Type == 0) { + + cmsFloat32Number R1 = (cmsFloat32Number)(R - g->Segments[i].x0) / (g->Segments[i].x1 - g->Segments[i].x0); + + // Setup the table (TODO: clean that) + g->SegInterp[i]->Table = g->Segments[i].SampledPoints; -void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3]) -{ - cmsFreeGamma(Gamma[0]); - cmsFreeGamma(Gamma[1]); - cmsFreeGamma(Gamma[2]); - Gamma[0] = Gamma[1] = Gamma[2] = NULL; + g->SegInterp[i]->Interpolation.LerpFloat(&R1, &Out32, g->SegInterp[i]); + Out = (cmsFloat64Number) Out32; + + } + else { + Out = g->Evals[i](g->Segments[i].Type, g->Segments[i].Params, R); + } + + if (isinf(Out)) + return PLUS_INF; + else + { + if (isinf(-Out)) + return MINUS_INF; + } + + return Out; + } + } + + return MINUS_INF; } - - -// Duplicate a gamma table - -LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE In) +// Access to estimated low-res table +cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t) { - LPGAMMATABLE Ptr; - size_t size; + _cmsAssert(t != NULL); + return t ->nEntries; +} - Ptr = cmsAllocGamma(In -> nEntries); - if (Ptr == NULL) return NULL; - - size = sizeof(GAMMATABLE) + (sizeof(WORD) * (In -> nEntries-1)); - - CopyMemory(Ptr, In, size); - return Ptr; +const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + return t ->Table16; } -// Handle gamma using interpolation tables. The resulting curves can become -// very stange, but are pleasent to eye. - -LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma, - LPGAMMATABLE OutGamma) +// Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the +// floating point description empty. +cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsUInt32Number nEntries, const cmsUInt16Number Values[]) { - register int i; - L16PARAMS L16In, L16Out; - LPWORD InPtr, OutPtr; - LPGAMMATABLE p; - - p = cmsAllocGamma(256); - if (!p) return NULL; + return AllocateToneCurveStruct(ContextID, nEntries, 0, NULL, Values); +} - cmsCalcL16Params(InGamma -> nEntries, &L16In); - InPtr = InGamma -> GammaTable; - - cmsCalcL16Params(OutGamma -> nEntries, &L16Out); - OutPtr = OutGamma-> GammaTable; - - for (i=0; i < 256; i++) - { - WORD wValIn, wValOut; - - wValIn = cmsLinearInterpLUT16(RGB_8_TO_16(i), InPtr, &L16In); - wValOut = cmsReverseLinearInterpLUT16(wValIn, OutPtr, &L16Out); - - p -> GammaTable[i] = wValOut; - } - - return p; +static +cmsUInt32Number EntriesByGamma(cmsFloat64Number Gamma) +{ + if (fabs(Gamma - 1.0) < 0.001) return 2; + return 4096; } +// Create a segmented gamma, fill the table +cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, + cmsUInt32Number nSegments, const cmsCurveSegment Segments[]) +{ + cmsUInt32Number i; + cmsFloat64Number R, Val; + cmsToneCurve* g; + cmsUInt32Number nGridPoints = 4096; -// New method, using smoothed parametric curves. This works FAR better. -// We want to get -// -// y = f(g^-1(x)) ; f = ingamma, g = outgamma -// -// And this can be parametrized as -// -// y = f(t) -// x = g(t) + _cmsAssert(Segments != NULL); + + // Optimizatin for identity curves. + if (nSegments == 1 && Segments[0].Type == 1) { + + nGridPoints = EntriesByGamma(Segments[0].Params[0]); + } + + g = AllocateToneCurveStruct(ContextID, nGridPoints, nSegments, Segments, NULL); + if (g == NULL) return NULL; + + // Once we have the floating point version, we can approximate a 16 bit table of 4096 entries + // for performance reasons. This table would normally not be used except on 8/16 bits transforms. + for (i = 0; i < nGridPoints; i++) { + + R = (cmsFloat64Number) i / (nGridPoints-1); + + Val = EvalSegmentedFn(g, R); + + // Round and saturate + g ->Table16[i] = _cmsQuickSaturateWord(Val * 65535.0); + } + + return g; +} + +// Use a segmented curve to store the floating point table +cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]) +{ + cmsCurveSegment Seg[3]; + + // A segmented tone curve should have function segments in the first and last positions + // Initialize segmented curve part up to 0 to constant value = samples[0] + Seg[0].x0 = MINUS_INF; + Seg[0].x1 = 0; + Seg[0].Type = 6; + + Seg[0].Params[0] = 1; + Seg[0].Params[1] = 0; + Seg[0].Params[2] = 0; + Seg[0].Params[3] = values[0]; + Seg[0].Params[4] = 0; + + // From zero to 1 + Seg[1].x0 = 0; + Seg[1].x1 = 1.0; + Seg[1].Type = 0; + + Seg[1].nGridPoints = nEntries; + Seg[1].SampledPoints = (cmsFloat32Number*) values; + + // Final segment is constant = lastsample + Seg[2].x0 = 1.0; + Seg[2].x1 = PLUS_INF; + Seg[2].Type = 6; + + Seg[2].Params[0] = 1; + Seg[2].Params[1] = 0; + Seg[2].Params[2] = 0; + Seg[2].Params[3] = values[nEntries-1]; + Seg[2].Params[4] = 0; -LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma, - LPGAMMATABLE OutGamma, int nPoints) -{ + return cmsBuildSegmentedToneCurve(ContextID, 3, Seg); +} - LPSAMPLEDCURVE x, y, r; - LPGAMMATABLE res; - - x = cmsConvertGammaToSampledCurve(InGamma, nPoints); - y = cmsConvertGammaToSampledCurve(OutGamma, nPoints); - r = cmsJoinSampledCurves(y, x, nPoints); +// Parametric curves +// +// Parameters goes as: Curve, a, b, c, d, e, f +// Type is the ICC type +1 +// if type is negative, then the curve is analyticaly inverted +cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]) +{ + cmsCurveSegment Seg0; + int Pos = 0; + cmsUInt32Number size; + _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos); - // Does clean "hair" - cmsSmoothSampledCurve(r, 0.001); + _cmsAssert(Params != NULL); - cmsClampSampledCurve(r, 0.0, 65535.0); + if (c == NULL) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type); + return NULL; + } + + memset(&Seg0, 0, sizeof(Seg0)); - cmsFreeSampledCurve(x); - cmsFreeSampledCurve(y); + Seg0.x0 = MINUS_INF; + Seg0.x1 = PLUS_INF; + Seg0.Type = Type; - res = cmsConvertSampledCurveToGamma(r, 65535.0); - cmsFreeSampledCurve(r); + size = c->ParameterCount[Pos] * sizeof(cmsFloat64Number); + memmove(Seg0.Params, Params, size); - return res; + return cmsBuildSegmentedToneCurve(ContextID, 1, &Seg0); } -// Reverse a gamma table - -LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma) +// Build a gamma table based on gamma constant +cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma) { - register int i; - L16PARAMS L16In; - LPWORD InPtr; - LPGAMMATABLE p; - - // Try to reverse it analytically whatever possible - if (InGamma -> Seed.Type > 0 && InGamma -> Seed.Type <= 5 && - _cmsCrc32OfGammaTable(InGamma) == InGamma -> Seed.Crc32) { - - return cmsBuildParametricGamma(nResultSamples, -(InGamma -> Seed.Type), InGamma ->Seed.Params); - } + return cmsBuildParametricToneCurve(ContextID, 1, &Gamma); +} - // Nope, reverse the table - p = cmsAllocGamma(nResultSamples); - if (!p) return NULL; +// Free all memory taken by the gamma curve +void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve) +{ + cmsContext ContextID; + + if (Curve == NULL) return; + + ContextID = Curve ->InterpParams->ContextID; + + _cmsFreeInterpParams(Curve ->InterpParams); - cmsCalcL16Params(InGamma -> nEntries, &L16In); - InPtr = InGamma -> GammaTable; + if (Curve -> Table16) + _cmsFree(ContextID, Curve ->Table16); + + if (Curve ->Segments) { + + cmsUInt32Number i; + + for (i=0; i < Curve ->nSegments; i++) { + + if (Curve ->Segments[i].SampledPoints) { + _cmsFree(ContextID, Curve ->Segments[i].SampledPoints); + } - for (i=0; i < nResultSamples; i++) - { - WORD wValIn, wValOut; + if (Curve ->SegInterp[i] != 0) + _cmsFreeInterpParams(Curve->SegInterp[i]); + } + + _cmsFree(ContextID, Curve ->Segments); + _cmsFree(ContextID, Curve ->SegInterp); + } + + if (Curve -> Evals) + _cmsFree(ContextID, Curve -> Evals); - wValIn = _cmsQuantizeVal(i, nResultSamples); - wValOut = cmsReverseLinearInterpLUT16(wValIn, InPtr, &L16In); - p -> GammaTable[i] = wValOut; - } + if (Curve) _cmsFree(ContextID, Curve); +} + +// Utility function, free 3 gamma tables +void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]) +{ + + _cmsAssert(Curve != NULL); + + if (Curve[0] != NULL) cmsFreeToneCurve(Curve[0]); + if (Curve[1] != NULL) cmsFreeToneCurve(Curve[1]); + if (Curve[2] != NULL) cmsFreeToneCurve(Curve[2]); + + Curve[0] = Curve[1] = Curve[2] = NULL; +} - return p; +// Duplicate a gamma table +cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In) +{ + if (In == NULL) return NULL; + + return AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16); +} + +// Joins two curves for X and Y. Curves should be monotonic. +// We want to get +// +// y = Y^-1(X(t)) +// +cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, + const cmsToneCurve* X, + const cmsToneCurve* Y, cmsUInt32Number nResultingPoints) +{ + cmsToneCurve* out = NULL; + cmsToneCurve* Yreversed = NULL; + cmsFloat32Number t, x; + cmsFloat32Number* Res = NULL; + cmsUInt32Number i; + + + _cmsAssert(X != NULL); + _cmsAssert(Y != NULL); + + Yreversed = cmsReverseToneCurveEx(nResultingPoints, Y); + if (Yreversed == NULL) goto Error; + + Res = (cmsFloat32Number*) _cmsCalloc(ContextID, nResultingPoints, sizeof(cmsFloat32Number)); + if (Res == NULL) goto Error; + + //Iterate + for (i=0; i < nResultingPoints; i++) { + + t = (cmsFloat32Number) i / (nResultingPoints-1); + x = cmsEvalToneCurveFloat(X, t); + Res[i] = cmsEvalToneCurveFloat(Yreversed, x); + } + + // Allocate space for output + out = cmsBuildTabulatedToneCurveFloat(ContextID, nResultingPoints, Res); + +Error: + + if (Res != NULL) _cmsFree(ContextID, Res); + if (Yreversed != NULL) cmsFreeToneCurve(Yreversed); + + return out; } -// Parametric curves -// -// Parameters goes as: Gamma, a, b, c, d, e, f -// Type is the ICC type +1 -// if type is negative, then the curve is analyticaly inverted - -LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[]) +// Get the surrounding nodes. This is tricky on non-monotonic tables +static +int GetInterval(cmsFloat64Number In, const cmsUInt16Number LutTable[], const struct _cms_interp_struc* p) { - LPGAMMATABLE Table; - double R, Val, dval, e; - int i; - int ParamsByType[] = { 0, 1, 3, 4, 5, 7 }; + int i; + int y0, y1; + + // A 1 point table is not allowed + if (p -> Domain[0] < 1) return -1; - Table = cmsAllocGamma(nEntries); - if (NULL == Table) return NULL; + // Let's see if ascending or descending. + if (LutTable[0] < LutTable[p ->Domain[0]]) { - Table -> Seed.Type = Type; + // Table is overall ascending + for (i = (int) p->Domain[0] - 1; i >= 0; --i) { - CopyMemory(Table ->Seed.Params, Params, ParamsByType[abs(Type)] * sizeof(double)); - - - for (i=0; i < nEntries; i++) { - - R = (double) i / (nEntries-1); - - switch (Type) { + y0 = LutTable[i]; + y1 = LutTable[i+1]; - // X = Y ^ Gamma - case 1: - Val = pow(R, Params[0]); - break; + if (y0 <= y1) { // Increasing + if (In >= y0 && In <= y1) return i; + } + else + if (y1 < y0) { // Decreasing + if (In >= y1 && In <= y0) return i; + } + } + } + else { + // Table is overall descending + for (i=0; i < (int) p -> Domain[0]; i++) { - // Type 1 Reversed: X = Y ^1/gamma - case -1: - Val = pow(R, 1/Params[0]); - break; + y0 = LutTable[i]; + y1 = LutTable[i+1]; - // CIE 122-1966 - // Y = (aX + b)^Gamma | X >= -b/a - // Y = 0 | else - case 2: - if (R >= -Params[2] / Params[1]) { - - e = Params[1]*R + Params[2]; + if (y0 <= y1) { // Increasing + if (In >= y0 && In <= y1) return i; + } + else + if (y1 < y0) { // Decreasing + if (In >= y1 && In <= y0) return i; + } + } + } - if (e > 0) - Val = pow(e, Params[0]); - else - Val = 0; - } - else - Val = 0; - break; + return -1; +} + +// Reverse a gamma table +cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsUInt32Number nResultSamples, const cmsToneCurve* InCurve) +{ + cmsToneCurve *out; + cmsFloat64Number a = 0, b = 0, y, x1, y1, x2, y2; + int i, j; + int Ascending; + + _cmsAssert(InCurve != NULL); + + // Try to reverse it analytically whatever possible - // Type 2 Reversed - // X = (Y ^1/g - b) / a - case -2: + if (InCurve ->nSegments == 1 && InCurve ->Segments[0].Type > 0 && + /* InCurve -> Segments[0].Type <= 5 */ + GetParametricCurveByType(InCurve ->InterpParams->ContextID, InCurve ->Segments[0].Type, NULL) != NULL) { - Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1]; - if (Val < 0) - Val = 0; - break; - + return cmsBuildParametricToneCurve(InCurve ->InterpParams->ContextID, + -(InCurve -> Segments[0].Type), + InCurve -> Segments[0].Params); + } - // IEC 61966-3 - // Y = (aX + b)^Gamma | X <= -b/a - // Y = c | else - case 3: - if (R >= -Params[2] / Params[1]) { + // Nope, reverse the table. + out = cmsBuildTabulatedToneCurve16(InCurve ->InterpParams->ContextID, nResultSamples, NULL); + if (out == NULL) + return NULL; + + // We want to know if this is an ascending or descending table + Ascending = !cmsIsToneCurveDescending(InCurve); - e = Params[1]*R + Params[2]; - Val = pow(e, Params[0]) + Params[3]; - } - else - Val = Params[3]; - break; + // Iterate across Y axis + for (i=0; i < (int) nResultSamples; i++) { + + y = (cmsFloat64Number) i * 65535.0 / (nResultSamples - 1); + + // Find interval in which y is within. + j = GetInterval(y, InCurve->Table16, InCurve->InterpParams); + if (j >= 0) { - // Type 3 reversed - // X=((Y-c)^1/g - b)/a | (Y>=c) - // X=-b/a | (YTable16[j]; + x2 = InCurve ->Table16[j+1]; - case -3: - if (R >= Params[3]) { - e = R - Params[3]; - Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1]; - if (Val < 0) Val = 0; - } - else { - Val = -Params[2] / Params[1]; - } - break; + y1 = (cmsFloat64Number) (j * 65535.0) / (InCurve ->nEntries - 1); + y2 = (cmsFloat64Number) ((j+1) * 65535.0 ) / (InCurve ->nEntries - 1); - - // IEC 61966-2.1 (sRGB) - // Y = (aX + b)^Gamma | X >= d - // Y = cX | X < d - case 4: - if (R >= Params[4]) { + // If collapsed, then use any + if (x1 == x2) { - e = Params[1]*R + Params[2]; - if (e > 0) - Val = pow(e, Params[0]); - else - Val = 0; - } - else - Val = R * Params[3]; - break; + out ->Table16[i] = _cmsQuickSaturateWord(Ascending ? y2 : y1); + continue; + + } else { - // Type 4 reversed - // X=((Y^1/g-b)/a) | Y >= (ad+b)^g - // X=Y/c | Y< (ad+b)^g - - case -4: - if (R >= pow(Params[1] * Params[4] + Params[2], Params[0])) { + // Interpolate + a = (y2 - y1) / (x2 - x1); + b = y2 - a * x2; + } + } - Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1]; - } - else { - Val = R / Params[3]; - } - break; - + out ->Table16[i] = _cmsQuickSaturateWord(a* y + b); + } - // Y = (aX + b)^Gamma + e | X <= d - // Y = cX + f | else - case 5: - if (R >= Params[4]) { - - e = Params[1]*R + Params[2]; - Val = pow(e, Params[0]) + Params[5]; - } - else - Val = R*Params[3] + Params[6]; - break; - - - // Reversed type 5 - // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e) - // X=(Y-f)/c | else - case -5: - - if (R >= pow(Params[1] * Params[4], Params[0]) + Params[5]) { - - Val = pow(R - Params[5], 1/Params[0]) - Params[2] / Params[1]; - } - else { - Val = (R - Params[6]) / Params[3]; - } - break; - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported parametric curve type=%d", abs(Type)-1); - cmsFreeGamma(Table); - return NULL; - } - - - // Saturate - - dval = Val * 65535.0 + .5; - if (dval > 65535.) dval = 65535.0; - if (dval < 0) dval = 0; - - Table->GammaTable[i] = (WORD) floor(dval); - } - - Table -> Seed.Crc32 = _cmsCrc32OfGammaTable(Table); - - return Table; + return out; } -// Build a gamma table based on gamma constant - -LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma) +// Reverse a gamma table +cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma) { - return cmsBuildParametricGamma(nEntries, 1, &Gamma); + _cmsAssert(InGamma != NULL); + + return cmsReverseToneCurveEx(4096, InGamma); } - - // From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite // differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press. // @@ -499,485 +1120,343 @@ // Input: smoothing parameter (lambda), length (m). // Output: smoothed vector (z): vector from 1 to m. - static -void smooth2(vec w, vec y, vec z, float lambda, int m) +cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[], + cmsFloat32Number z[], cmsFloat32Number lambda, int m) { - int i, i1, i2; - vec c, d, e; - d[1] = w[1] + lambda; - c[1] = -2 * lambda / d[1]; - e[1] = lambda /d[1]; - z[1] = w[1] * y[1]; - d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1]; - c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2]; - e[2] = lambda / d[2]; - z[2] = w[2] * y[2] - c[1] * z[1]; - for (i = 3; i < m - 1; i++) { - i1 = i - 1; i2 = i - 2; - d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; - c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i]; - e[i] = lambda / d[i]; - z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2]; - } - i1 = m - 2; i2 = m - 3; - d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; - c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1]; - z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2]; - i1 = m - 1; i2 = m - 2; - d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; - z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m]; - z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m]; - for (i = m - 2; 1<= i; i--) - z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2]; -} + int i, i1, i2; + cmsFloat32Number *c, *d, *e; + cmsBool st; - -// Smooths a curve sampled at regular intervals + c = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + d = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + e = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); -LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda) - -{ - vec w, y, z; - int i, nItems, Zeros, Poles; + if (c != NULL && d != NULL && e != NULL) { - if (cmsIsLinear(Tab->GammaTable, Tab->nEntries)) return FALSE; // Nothing to do - - nItems = Tab -> nEntries; - - if (nItems > MAX_KNOTS) { - cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothGamma: too many points."); - return FALSE; - } + d[1] = w[1] + lambda; + c[1] = -2 * lambda / d[1]; + e[1] = lambda /d[1]; + z[1] = w[1] * y[1]; + d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1]; + c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2]; + e[2] = lambda / d[2]; + z[2] = w[2] * y[2] - c[1] * z[1]; - ZeroMemory(w, nItems * sizeof(float)); - ZeroMemory(y, nItems * sizeof(float)); - ZeroMemory(z, nItems * sizeof(float)); - - for (i=0; i < nItems; i++) - { - y[i+1] = (float) Tab -> GammaTable[i]; - w[i+1] = 1.0; + for (i = 3; i < m - 1; i++) { + i1 = i - 1; i2 = i - 2; + d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i]; + e[i] = lambda / d[i]; + z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2]; } - smooth2(w, y, z, (float) lambda, nItems); + i1 = m - 2; i2 = m - 3; + + d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1]; + z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2]; + i1 = m - 1; i2 = m - 2; + + d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m]; + z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m]; + + for (i = m - 2; 1<= i; i--) + z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2]; + + st = TRUE; + } + else st = FALSE; + + if (c != NULL) _cmsFree(ContextID, c); + if (d != NULL) _cmsFree(ContextID, d); + if (e != NULL) _cmsFree(ContextID, e); + + return st; +} + +// Smooths a curve sampled at regular intervals. +cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda) +{ + cmsBool SuccessStatus = TRUE; + cmsFloat32Number *w, *y, *z; + cmsUInt32Number i, nItems, Zeros, Poles; + + if (Tab != NULL && Tab->InterpParams != NULL) + { + cmsContext ContextID = Tab->InterpParams->ContextID; + + if (!cmsIsToneCurveLinear(Tab)) // Only non-linear curves need smoothing + { + nItems = Tab->nEntries; + if (nItems < MAX_NODES_IN_CURVE) + { + // Allocate one more item than needed + w = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + y = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + z = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + + if (w != NULL && y != NULL && z != NULL) // Ensure no memory allocation failure + { + memset(w, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + memset(y, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + memset(z, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + + for (i = 0; i < nItems; i++) + { + y[i + 1] = (cmsFloat32Number)Tab->Table16[i]; + w[i + 1] = 1.0; + } + + if (smooth2(ContextID, w, y, z, (cmsFloat32Number)lambda, (int)nItems)) + { + // Do some reality - checking... - // Do some reality - checking... - Zeros = Poles = 0; - for (i=nItems; i > 1; --i) { + Zeros = Poles = 0; + for (i = nItems; i > 1; --i) + { + if (z[i] == 0.) Zeros++; + if (z[i] >= 65535.) Poles++; + if (z[i] < z[i - 1]) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic."); + SuccessStatus = FALSE; + break; + } + } + + if (SuccessStatus && Zeros > (nItems / 3)) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros."); + SuccessStatus = FALSE; + } + + if (SuccessStatus && Poles > (nItems / 3)) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles."); + SuccessStatus = FALSE; + } - if (z[i] == 0.) Zeros++; - if (z[i] >= 65535.) Poles++; - if (z[i] < z[i-1]) return FALSE; // Non-Monotonic + if (SuccessStatus) // Seems ok + { + for (i = 0; i < nItems; i++) + { + // Clamp to cmsUInt16Number + Tab->Table16[i] = _cmsQuickSaturateWord(z[i + 1]); + } + } + } + else // Could not smooth + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Function smooth2 failed."); + SuccessStatus = FALSE; + } + } + else // One or more buffers could not be allocated + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Could not allocate memory."); + SuccessStatus = FALSE; + } + + if (z != NULL) + _cmsFree(ContextID, z); + + if (y != NULL) + _cmsFree(ContextID, y); + + if (w != NULL) + _cmsFree(ContextID, w); + } + else // too many items in the table + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Too many points."); + SuccessStatus = FALSE; + } + } + } + else // Tab parameter or Tab->InterpParams is NULL + { + // Can't signal an error here since the ContextID is not known at this point + SuccessStatus = FALSE; } - if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros - if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles - - // Seems ok - - for (i=0; i < nItems; i++) { + return SuccessStatus; +} - // Clamp to WORD - - float v = z[i+1]; +// Is a table linear? Do not use parametric since we cannot guarantee some weird parameters resulting +// in a linear table. This way assures it is linear in 12 bits, which should be enought in most cases. +cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve) +{ + int i; + int diff; - if (v < 0) v = 0; - if (v > 65535.) v = 65535.; + _cmsAssert(Curve != NULL); + + for (i=0; i < (int) Curve ->nEntries; i++) { - Tab -> GammaTable[i] = (WORD) floor(v + .5); - } + diff = abs((int) Curve->Table16[i] - (int) _cmsQuantizeVal(i, Curve ->nEntries)); + if (diff > 0x0f) + return FALSE; + } return TRUE; } +// Same, but for monotonicity +cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t) +{ + cmsUInt32Number n; + int i, last; + cmsBool lDescending; -// Check if curve is exponential, return gamma if so. + _cmsAssert(t != NULL); + + // Degenerated curves are monotonic? Ok, let's pass them + n = t ->nEntries; + if (n < 2) return TRUE; + + // Curve direction + lDescending = cmsIsToneCurveDescending(t); + + if (lDescending) { + + last = t ->Table16[0]; + + for (i = 1; i < (int) n; i++) { + + if (t ->Table16[i] - last > 2) // We allow some ripple + return FALSE; + else + last = t ->Table16[i]; + + } + } + else { + + last = t ->Table16[n-1]; + + for (i = (int) n - 2; i >= 0; --i) { + + if (t ->Table16[i] - last > 2) + return FALSE; + else + last = t ->Table16[i]; + + } + } + + return TRUE; +} + +// Same, but for descending tables +cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + return t ->Table16[0] > t ->Table16[t ->nEntries-1]; +} + + +// Another info fn: is out gamma table multisegment? +cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + return t -> nSegments > 1; +} -double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold) +cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + if (t -> nSegments != 1) return 0; + return t ->Segments[0].Type; +} + +// We need accuracy this time +cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v) +{ + _cmsAssert(Curve != NULL); + + // Check for 16 bits table. If so, this is a limited-precision tone curve + if (Curve ->nSegments == 0) { + + cmsUInt16Number In, Out; + + In = (cmsUInt16Number) _cmsQuickSaturateWord(v * 65535.0); + Out = cmsEvalToneCurve16(Curve, In); + + return (cmsFloat32Number) (Out / 65535.0); + } + + return (cmsFloat32Number) EvalSegmentedFn(Curve, v); +} + +// We need xput over here +cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v) { - double gamma, sum, sum2; - double n, x, y, Std; - int i; + cmsUInt16Number out; + + _cmsAssert(Curve != NULL); + + Curve ->InterpParams ->Interpolation.Lerp16(&v, &out, Curve ->InterpParams); + return out; +} + + +// Least squares fitting. +// A mathematical procedure for finding the best-fitting curve to a given set of points by +// minimizing the sum of the squares of the offsets ("the residuals") of the points from the curve. +// The sum of the squares of the offsets is used instead of the offset absolute values because +// this allows the residuals to be treated as a continuous differentiable quantity. +// +// y = f(x) = x ^ g +// +// R = (yi - (xi^g)) +// R2 = (yi - (xi^g))2 +// SUM R2 = SUM (yi - (xi^g))2 +// +// dR2/dg = -2 SUM x^g log(x)(y - x^g) +// solving for dR2/dg = 0 +// +// g = 1/n * SUM(log(y) / log(x)) + +cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision) +{ + cmsFloat64Number gamma, sum, sum2; + cmsFloat64Number n, x, y, Std; + cmsUInt32Number i; + + _cmsAssert(t != NULL); sum = sum2 = n = 0; - // Does exclude endpoints - for (i=1; i < nEntries - 1; i++) { + // Excluding endpoints + for (i=1; i < (MAX_NODES_IN_CURVE-1); i++) { - x = (double) i / (nEntries - 1); - y = (double) GammaTable[i] / 65535.; + x = (cmsFloat64Number) i / (MAX_NODES_IN_CURVE-1); + y = (cmsFloat64Number) cmsEvalToneCurveFloat(t, (cmsFloat32Number) x); - // Avoid 7% on lower part to prevent - // artifacts due to linear ramps + // Avoid 7% on lower part to prevent + // artifacts due to linear ramps - if (y > 0. && y < 1. && x > 0.07) { + if (y > 0. && y < 1. && x > 0.07) { gamma = log(y) / log(x); sum += gamma; sum2 += gamma * gamma; n++; - } - + } } // Take a look on SD to see if gamma isn't exponential at all Std = sqrt((n * sum2 - sum * sum) / (n*(n-1))); - - if (Std > Thereshold) + if (Std > Precision) return -1.0; return (sum / n); // The mean } - - -double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t) -{ - return cmsEstimateGammaEx(t->GammaTable, t->nEntries, 0.7); -} - - -// -----------------------------------------------------------------Sampled curves - -// Allocate a empty curve - -LPSAMPLEDCURVE cmsAllocSampledCurve(int nItems) -{ - LPSAMPLEDCURVE pOut; - - pOut = (LPSAMPLEDCURVE) _cmsMalloc(sizeof(SAMPLEDCURVE)); - if (pOut == NULL) - return NULL; - - if((pOut->Values = (double *) _cmsMalloc(nItems * sizeof(double))) == NULL) - { - _cmsFree(pOut); - return NULL; - } - - pOut->nItems = nItems; - ZeroMemory(pOut->Values, nItems * sizeof(double)); - - return pOut; -} - - -void cmsFreeSampledCurve(LPSAMPLEDCURVE p) -{ - _cmsFree((LPVOID) p -> Values); - _cmsFree((LPVOID) p); -} - - - -// Does duplicate a sampled curve - -LPSAMPLEDCURVE cmsDupSampledCurve(LPSAMPLEDCURVE p) -{ - LPSAMPLEDCURVE out; - - out = cmsAllocSampledCurve(p -> nItems); - if (!out) return NULL; - - CopyMemory(out ->Values, p ->Values, p->nItems * sizeof(double)); - - return out; -} - - -// Take min, max of curve - -void cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max) -{ - int i; - - *Min = 65536.; - *Max = 0.; - - for (i=0; i < p -> nItems; i++) { - - double v = p -> Values[i]; - - if (v < *Min) - *Min = v; - - if (v > *Max) - *Max = v; - } - - if (*Min < 0) *Min = 0; - if (*Max > 65535.0) *Max = 65535.0; -} - -// Clamps to Min, Max - -void cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max) -{ - - int i; - - for (i=0; i < p -> nItems; i++) { - - double v = p -> Values[i]; - - if (v < Min) - v = Min; - - if (v > Max) - v = Max; - - p -> Values[i] = v; - - } - -} - - - -// Smooths a curve sampled at regular intervals - -LCMSBOOL cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double lambda) -{ - vec w, y, z; - int i, nItems; - - nItems = Tab -> nItems; - - if (nItems > MAX_KNOTS) { - cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothSampledCurve: too many points."); - return FALSE; - } - - ZeroMemory(w, nItems * sizeof(float)); - ZeroMemory(y, nItems * sizeof(float)); - ZeroMemory(z, nItems * sizeof(float)); - - for (i=0; i < nItems; i++) - { - float value = (float) Tab -> Values[i]; - - y[i+1] = value; - w[i+1] = (float) ((value < 0.0) ? 0 : 1); - } - - - smooth2(w, y, z, (float) lambda, nItems); - - for (i=0; i < nItems; i++) { - - Tab -> Values[i] = z[i+1];; - } - - return TRUE; - -} - - -// Scale a value v, within domain Min .. Max -// to a domain 0..(nPoints-1) - -static -double ScaleVal(double v, double Min, double Max, int nPoints) -{ - - double a, b; - - if (v <= Min) return 0; - if (v >= Max) return (nPoints-1); - - a = (double) (nPoints - 1) / (Max - Min); - b = a * Min; - - return (a * v) - b; - -} - - -// Does rescale a sampled curve to fit in a 0..(nPoints-1) domain - -void cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints) -{ - - int i; - - for (i=0; i < p -> nItems; i++) { - - double v = p -> Values[i]; - - p -> Values[i] = ScaleVal(v, Min, Max, nPoints); - } - -} - - -// Joins two sampled curves for X and Y. Curves should be sorted. - -LPSAMPLEDCURVE cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints) -{ - int i, j; - LPSAMPLEDCURVE out; - double MinX, MinY, MaxX, MaxY; - double x, y, x1, y1, x2, y2, a, b; - - out = cmsAllocSampledCurve(nResultingPoints); - if (out == NULL) - return NULL; - - if (X -> nItems != Y -> nItems) { - - cmsSignalError(LCMS_ERRC_ABORTED, "cmsJoinSampledCurves: invalid curve."); - cmsFreeSampledCurve(out); - return NULL; - } - - // Get endpoints of sampled curves - cmsEndpointsOfSampledCurve(X, &MinX, &MaxX); - cmsEndpointsOfSampledCurve(Y, &MinY, &MaxY); - - - // Set our points - out ->Values[0] = MinY; - for (i=1; i < nResultingPoints; i++) { - - // Scale t to x domain - x = (i * (MaxX - MinX) / (nResultingPoints-1)) + MinX; - - // Find interval in which j is within (always up, - // since fn should be monotonic at all) - - j = 1; - while ((j < X ->nItems - 1) && X ->Values[j] < x) - j++; - - // Now x is within X[j-1], X[j] - x1 = X ->Values[j-1]; x2 = X ->Values[j]; - y1 = Y ->Values[j-1]; y2 = Y ->Values[j]; - - // Interpolate the value - a = (y1 - y2) / (x1 - x2); - b = y1 - a * x1; - y = a* x + b; - - out ->Values[i] = y; - } - - - cmsClampSampledCurve(out, MinY, MaxY); - return out; -} - - - -// Convert between curve types - -LPGAMMATABLE cmsConvertSampledCurveToGamma(LPSAMPLEDCURVE Sampled, double Max) -{ - LPGAMMATABLE Gamma; - int i, nPoints; - - - nPoints = Sampled ->nItems; - - Gamma = cmsAllocGamma(nPoints); - for (i=0; i < nPoints; i++) { - - Gamma->GammaTable[i] = (WORD) floor(ScaleVal(Sampled ->Values[i], 0, Max, 65536) + .5); - } - - return Gamma; - -} - -// Inverse of anterior - -LPSAMPLEDCURVE cmsConvertGammaToSampledCurve(LPGAMMATABLE Gamma, int nPoints) -{ - LPSAMPLEDCURVE Sampled; - L16PARAMS L16; - int i; - WORD wQuant, wValIn; - - if (nPoints > 4096) { - - cmsSignalError(LCMS_ERRC_ABORTED, "cmsConvertGammaToSampledCurve: too many points (max=4096)"); - return NULL; - } - - cmsCalcL16Params(Gamma -> nEntries, &L16); - - Sampled = cmsAllocSampledCurve(nPoints); - for (i=0; i < nPoints; i++) { - wQuant = _cmsQuantizeVal(i, nPoints); - wValIn = cmsLinearInterpLUT16(wQuant, Gamma ->GammaTable, &L16); - Sampled ->Values[i] = (float) wValIn; - } - - return Sampled; -} - - - - -// Smooth endpoints (used in Black/White compensation) - -LCMSBOOL _cmsSmoothEndpoints(LPWORD Table, int nEntries) -{ - vec w, y, z; - int i, Zeros, Poles; - - - - if (cmsIsLinear(Table, nEntries)) return FALSE; // Nothing to do - - - if (nEntries > MAX_KNOTS) { - cmsSignalError(LCMS_ERRC_ABORTED, "_cmsSmoothEndpoints: too many points."); - return FALSE; - } - - ZeroMemory(w, nEntries * sizeof(float)); - ZeroMemory(y, nEntries * sizeof(float)); - ZeroMemory(z, nEntries * sizeof(float)); - - for (i=0; i < nEntries; i++) - { - y[i+1] = (float) Table[i]; - w[i+1] = 1.0; - } - - w[1] = 65535.0; - w[nEntries] = 65535.0; - - smooth2(w, y, z, (float) nEntries, nEntries); - - // Do some reality - checking... - Zeros = Poles = 0; - for (i=nEntries; i > 1; --i) { - - if (z[i] == 0.) Zeros++; - if (z[i] >= 65535.) Poles++; - if (z[i] < z[i-1]) return FALSE; // Non-Monotonic - } - - if (Zeros > (nEntries / 3)) return FALSE; // Degenerated, mostly zeros - if (Poles > (nEntries / 3)) return FALSE; // Degenerated, mostly poles - - // Seems ok - - for (i=0; i < nEntries; i++) { - - // Clamp to WORD - - float v = z[i+1]; - - if (v < 0) v = 0; - if (v > 65535.) v = 65535.; - - Table[i] = (WORD) floor(v + .5); - } - - return TRUE; -} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsgmt.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsgmt.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsgmt.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,871 +49,382 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "lcms.h" - -/* -Gamut check by default is a catching of 0xFFFF/0xFFFF/0xFFFF PCS values, used -internally by lcms to hold invalid values. Matrix LUT's, operates in a way that -unencodeable values are marked as this combination, if PCS is XYZ, this is a very -high value since encoding is a 1.15 fixed point, something like 1.9997, 1.9997, 1.9997 -not a very common color after all. Lab PCS is not to be a problem, since L>100 are truely -undefined. There is a posibility than ICC comitee defines L>100 as a valid means -to use highlights, then it will be lost. - -(1.10 - Actually ICC did it, so this should be checked for full ICC 4.0 support) - -*/ - - -LCMSBOOL _cmsEndPointsBySpace(icColorSpaceSignature Space, WORD **White, WORD **Black, - int *nOutputs) -{ - // Only most common spaces - - static WORD RGBblack[4] = { 0, 0, 0 }; - static WORD RGBwhite[4] = { 0xffff, 0xffff, 0xffff }; - static WORD CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink - static WORD CMYKwhite[4] = { 0, 0, 0, 0 }; - static WORD LABblack[4] = { 0, 0x8000, 0x8000 }; - static WORD LABwhite[4] = { 0xFF00, 0x8000, 0x8000 }; - static WORD CMYblack[4] = { 0xffff, 0xffff, 0xffff }; - static WORD CMYwhite[4] = { 0, 0, 0 }; - static WORD Grayblack[4] = { 0 }; - static WORD GrayWhite[4] = { 0xffff }; - - switch (Space) { - - case icSigGrayData: if (White) *White = GrayWhite; - if (Black) *Black = Grayblack; - if (nOutputs) *nOutputs = 1; - return TRUE; - - case icSigRgbData: if (White) *White = RGBwhite; - if (Black) *Black = RGBblack; - if (nOutputs) *nOutputs = 3; - return TRUE; - - case icSigLabData: if (White) *White = LABwhite; - if (Black) *Black = LABblack; - if (nOutputs) *nOutputs = 3; - return TRUE; - - case icSigCmykData: if (White) *White = CMYKwhite; - if (Black) *Black = CMYKblack; - if (nOutputs) *nOutputs = 4; - return TRUE; - - case icSigCmyData: if (White) *White = CMYwhite; - if (Black) *Black = CMYblack; - if (nOutputs) *nOutputs = 3; - return TRUE; - - default:; - } +// +//--------------------------------------------------------------------------------- +// - return FALSE; -} - - -WORD *_cmsWhiteBySpace(icColorSpaceSignature Space) -{ - WORD *White= NULL, *Black = NULL; - int Dummy; - static WORD Default[MAXCHANNELS]; - - if (_cmsEndPointsBySpace(Space, &White, &Black, &Dummy)) - return White; - - return Default; - -} - - - - -WORD Clamp_L(Fixed32 in) -{ - if (in == 0xFFFF) return 0xFFFFU; // Marker - - if (in > 0xFF00) return 0xFF00U; // L* = 100.0 - return (WORD) in; -} - - -#define ENCODE_AB(x) (WORD) (((x) + 128.0) * 256.0 + 0.5) - -WORD Clamp_ab(Fixed32 in) -{ - if (in == 0xFFFF) return 0xFFFFU; // Marker - - if (in < 0) return ENCODE_AB(-128.0); // Max negative number - if (in > 0xFFFF) return ENCODE_AB(+127.9961); // Max positive number - return (WORD) in; -} - - - -// Returns dE on two Lab values - -double LCMSEXPORT cmsDeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2) -{ - double dL, da, db; - - if (Lab1 -> L < 0 || - Lab2 -> L < 0) return 65536.; - - if (Lab1 -> a < -200 || Lab1 -> a > 200) return 65536.; - if (Lab1 -> b < -200 || Lab1 -> b > 200) return 65536.; - - if (Lab2 -> a < -200 || Lab2 -> a > 200) return 65536.; - if (Lab2 -> b < -200 || Lab2 -> b > 200) return 65536.; - - if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0; - - dL = fabs(Lab1 -> L - Lab2 -> L); - da = fabs(Lab1 -> a - Lab2 -> a); - db = fabs(Lab1 -> b - Lab2 -> b); - - return pow(dL*dL + da * da + db * db, 0.5); - -} +#include "lcms2_internal.h" -// Square -static -double Sqr(double v) -{ - return v * v; -} - -// Return the CIE94 Delta E -double LCMSEXPORT cmsCIE94DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2) +// Auxiliary: append a Lab identity after the given sequence of profiles +// and return the transform. Lab profile is closed, rest of profiles are kept open. +cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) { - cmsCIELCh LCh1, LCh2; - double dE, dL, dC, dh, dhsq; - double c12, sc, sh; - - if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0; - - dL = fabs(Lab1 ->L - Lab2 ->L); - - cmsLab2LCh(&LCh1, Lab1); - cmsLab2LCh(&LCh2, Lab2); - - dC = fabs(LCh1.C - LCh2.C); - dE = cmsDeltaE(Lab1, Lab2); - - dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC); - if (dhsq < 0) - dh = 0; - else - dh = pow(dhsq, 0.5); - - c12 = sqrt(LCh1.C * LCh2.C); - - sc = 1.0 + (0.048 * c12); - sh = 1.0 + (0.014 * c12); - - return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh)); -} - - -// Auxiliary + cmsHTRANSFORM xform; + cmsHPROFILE hLab; + cmsHPROFILE ProfileList[256]; + cmsBool BPCList[256]; + cmsFloat64Number AdaptationList[256]; + cmsUInt32Number IntentList[256]; + cmsUInt32Number i; -static -double ComputeLBFD(LPcmsCIELab Lab) -{ - double yt; - - if (Lab->L > 7.996969) - yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100; - else - yt = 100 * (Lab->L / 903.3); - - return (54.6 * (LOGE * (log(yt + 1.5))) - 9.6); -} - - - -// bfd - gets BFD(1:1) difference between Lab1, Lab2 -double LCMSEXPORT cmsBFDdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2) -{ - double lbfd1,lbfd2,AveC,Aveh,dE,deltaL, - deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd; - cmsCIELCh LCh1, LCh2; + // This is a rather big number and there is no need of dynamic memory + // since we are adding a profile, 254 + 1 = 255 and this is the limit + if (nProfiles > 254) return NULL; - - if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0; - - lbfd1 = ComputeLBFD(Lab1); - lbfd2 = ComputeLBFD(Lab2); - deltaL = lbfd2 - lbfd1; - - cmsLab2LCh(&LCh1, Lab1); - cmsLab2LCh(&LCh2, Lab2); - - deltaC = LCh2.C - LCh1.C; - AveC = (LCh1.C+LCh2.C)/2; - Aveh = (LCh1.h+LCh2.h)/2; - - dE = cmsDeltaE(Lab1, Lab2); - - if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC))) - deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC)); - else - deltah =0; - + // The output space + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return NULL; - dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521; - g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000)); - t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))- - 0.040*cos((2*Aveh-136)/(180/M_PI))+ - 0.070*cos((3*Aveh-31)/(180/M_PI))+ - 0.049*cos((4*Aveh+114)/(180/M_PI))- - 0.015*cos((5*Aveh-103)/(180/M_PI))); - - dh = dc*(g*t+1-g); - rh = -0.260*cos((Aveh-308)/(180/M_PI))- - 0.379*cos((2*Aveh-160)/(180/M_PI))- - 0.636*cos((3*Aveh+254)/(180/M_PI))+ - 0.226*cos((4*Aveh+140)/(180/M_PI))- - 0.194*cos((5*Aveh+280)/(180/M_PI)); - - rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000)); - rt = rh*rc; - - bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh))); + // Create a copy of parameters + for (i=0; i < nProfiles; i++) { - return bfd; -} - - -// cmc - CMC(1:1) difference between Lab1, Lab2 -double LCMSEXPORT cmsCMCdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2) -{ - double dE,dL,dC,dh,sl,sc,sh,t,f,cmc; - cmsCIELCh LCh1, LCh2; - - if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0; - - cmsLab2LCh(&LCh1, Lab1); - cmsLab2LCh(&LCh2, Lab2); - - - dL = Lab2->L-Lab1->L; - dC = LCh2.C-LCh1.C; - - dE = cmsDeltaE(Lab1, Lab2); - if (Sqr(dE)>(Sqr(dL)+Sqr(dC))) - dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC)); - else - dh =0; + ProfileList[i] = hProfiles[i]; + BPCList[i] = BPC[i]; + AdaptationList[i] = AdaptationStates[i]; + IntentList[i] = Intents[i]; + } - if ((LCh1.h > 164) && (LCh1.h<345)) - t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI)))); - else - t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI)))); - - sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638; - sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L); - - if (Lab1->L<16) - sl = 0.511; - - f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900)); - sh = sc*(t*f+1-f); - cmc = sqrt(Sqr(dL/sl)+Sqr(dC/sc)+Sqr(dh/sh)); - - return cmc; -} - - + // Place Lab identity at chain's end. + ProfileList[nProfiles] = hLab; + BPCList[nProfiles] = 0; + AdaptationList[nProfiles] = 1.0; + IntentList[nProfiles] = INTENT_RELATIVE_COLORIMETRIC; -static -double atan2deg(double b, double a) -{ - double h; - - if (a == 0 && b == 0) - h = 0; - else - h = atan2(a, b); - - h *= (180. / M_PI); + // Create the transform + xform = cmsCreateExtendedTransform(ContextID, nProfiles + 1, ProfileList, + BPCList, + IntentList, + AdaptationList, + NULL, 0, + InputFormat, + OutputFormat, + dwFlags); - while (h > 360.) - h -= 360.; - - while ( h < 0) - h += 360.; - - return h; + cmsCloseProfile(hLab); -} - - -static -double RADIANES(double deg) -{ - return (deg * M_PI) / 180.; + return xform; } -// dE2000 The weightings KL, KC and KH can be modified to reflect the relative -// importance of lightness, chroma and hue in different industrial applications - -double LCMSEXPORT cmsCIE2000DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2, - double Kl, double Kc, double Kh) +// Compute K -> L* relationship. Flags may include black point compensation. In this case, +// the relationship is assumed from the profile with BPC to a black point zero. +static +cmsToneCurve* ComputeKToLstar(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) { - double L1 = Lab1->L; - double a1 = Lab1->a; - double b1 = Lab1->b; - double C = sqrt( Sqr(a1) + Sqr(b1) ); - - double Ls = Lab2 ->L; - double as = Lab2 ->a; - double bs = Lab2 ->b; - double Cs = sqrt( Sqr(as) + Sqr(bs) ); + cmsToneCurve* out = NULL; + cmsUInt32Number i; + cmsHTRANSFORM xform; + cmsCIELab Lab; + cmsFloat32Number cmyk[4]; + cmsFloat32Number* SampledPoints; - double G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) )); - - double a_p = (1 + G ) * a1; - double b_p = b1; - double C_p = sqrt( Sqr(a_p) + Sqr(b_p)); - double h_p = atan2deg(a_p, b_p); - - - double a_ps = (1 + G) * as; - double b_ps = bs; - double C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps)); - double h_ps = atan2deg(a_ps, b_ps); - - double meanC_p =(C_p + C_ps) / 2; - - double hps_plus_hp = h_ps + h_p; - double hps_minus_hp = h_ps - h_p; + xform = _cmsChain2Lab(ContextID, nProfiles, TYPE_CMYK_FLT, TYPE_Lab_DBL, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (xform == NULL) return NULL; - double meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 : - (hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 : - (hps_plus_hp - 360)/2; + SampledPoints = (cmsFloat32Number*) _cmsCalloc(ContextID, nPoints, sizeof(cmsFloat32Number)); + if (SampledPoints == NULL) goto Error; + + for (i=0; i < nPoints; i++) { - double delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) : - (hps_minus_hp) > 180 ? (hps_minus_hp - 360) : - (hps_minus_hp); - double delta_L = (Ls - L1); - double delta_C = (C_ps - C_p ); - - - double delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANES(delta_h) / 2); - - double T = 1 - 0.17 * cos(RADIANES(meanh_p-30)) - + 0.24 * cos(RADIANES(2*meanh_p)) - + 0.32 * cos(RADIANES(3*meanh_p + 6)) - - 0.2 * cos(RADIANES(4*meanh_p - 63)); + cmyk[0] = 0; + cmyk[1] = 0; + cmyk[2] = 0; + cmyk[3] = (cmsFloat32Number) ((i * 100.0) / (nPoints-1)); - double Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) ); + cmsDoTransform(xform, cmyk, &Lab, 1); + SampledPoints[i]= (cmsFloat32Number) (1.0 - Lab.L / 100.0); // Negate K for easier operation + } - double Sc = 1 + 0.045 * (C_p + C_ps)/2; - double Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T; - - double delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25))); - - double Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0))); + out = cmsBuildTabulatedToneCurveFloat(ContextID, nPoints, SampledPoints); - double Rt = -sin(2 * RADIANES(delta_ro)) * Rc; +Error: - double deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) + - Sqr(delta_C/(Sc * Kc)) + - Sqr(delta_H/(Sh * Kh)) + - Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh))); + cmsDeleteTransform(xform); + if (SampledPoints) _cmsFree(ContextID, SampledPoints); - return deltaE00; + return out; } - -// Carefully, clamp on CIELab space. - -void LCMSEXPORT cmsClampLab(LPcmsCIELab Lab, double amax, double amin, - double bmax, double bmin) +// Compute Black tone curve on a CMYK -> CMYK transform. This is done by +// using the proof direction on both profiles to find K->L* relationship +// then joining both curves. dwFlags may include black point compensation. +cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) { - - // Whole Luma surface to zero - - if (Lab -> L < 0) { - - Lab-> L = Lab->a = Lab-> b = 0.0; - return; - } + cmsToneCurve *in, *out, *KTone; - // Clamp white, DISCARD HIGHLIGHTS. This is done - // in such way because icc spec doesn't allow the - // use of L>100 as a highlight means. - - if (Lab->L > 100) - Lab -> L = 100; - - // Check out gamut prism, on a, b faces - - if (Lab -> a < amin || Lab->a > amax|| - Lab -> b < bmin || Lab->b > bmax) { - - cmsCIELCh LCh; - double h, slope; - - // Falls outside a, b limits. Transports to LCh space, - // and then do the clipping + // Make sure CMYK -> CMYK + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + cmsGetColorSpace(hProfiles[nProfiles-1])!= cmsSigCmykData) return NULL; - if (Lab -> a == 0.0) { // Is hue exactly 90? - - // atan will not work, so clamp here - Lab -> b = Lab->b < 0 ? bmin : bmax; - return; - } - - cmsLab2LCh(&LCh, Lab); - - slope = Lab -> b / Lab -> a; - h = LCh.h; - - // There are 4 zones - - if ((h >= 0. && h < 45.) || - (h >= 315 && h <= 360.)) { + // Make sure last is an output profile + if (cmsGetDeviceClass(hProfiles[nProfiles - 1]) != cmsSigOutputClass) return NULL; - // clip by amax - Lab -> a = amax; - Lab -> b = amax * slope; - } - else - if (h >= 45. && h < 135) - { - // clip by bmax - Lab -> b = bmax; - Lab -> a = bmax / slope; - } - else - if (h >= 135 && h < 225) { - // clip by amin - Lab -> a = amin; - Lab -> b = amin * slope; + // Create individual curves. BPC works also as each K to L* is + // computed as a BPC to zero black point in case of L* + in = ComputeKToLstar(ContextID, nPoints, nProfiles - 1, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (in == NULL) return NULL; - } - else - if (h >= 225 && h < 315) { - // clip by bmin - Lab -> b = bmin; - Lab -> a = bmin / slope; - } - else - cmsSignalError(LCMS_ERRC_ABORTED, "Invalid angle"); - - } -} - -// Several utilities ------------------------------------------------------- - -// Translate from our colorspace to ICC representation - -icColorSpaceSignature LCMSEXPORT _cmsICCcolorSpace(int OurNotation) -{ - switch (OurNotation) { - - case 1: - case PT_GRAY: return icSigGrayData; + out = ComputeKToLstar(ContextID, nPoints, 1, + Intents + (nProfiles - 1), + &hProfiles [nProfiles - 1], + BPC + (nProfiles - 1), + AdaptationStates + (nProfiles - 1), + dwFlags); + if (out == NULL) { + cmsFreeToneCurve(in); + return NULL; + } - case 2: - case PT_RGB: return icSigRgbData; + // Build the relationship. This effectively limits the maximum accuracy to 16 bits, but + // since this is used on black-preserving LUTs, we are not losing accuracy in any case + KTone = cmsJoinToneCurve(ContextID, in, out, nPoints); + + // Get rid of components + cmsFreeToneCurve(in); cmsFreeToneCurve(out); - case PT_CMY: return icSigCmyData; - case PT_CMYK: return icSigCmykData; - case PT_YCbCr:return icSigYCbCrData; - case PT_YUV: return icSigLuvData; - case PT_XYZ: return icSigXYZData; - case PT_Lab: return icSigLabData; - case PT_YUVK: return icSigLuvKData; - case PT_HSV: return icSigHsvData; - case PT_HLS: return icSigHlsData; - case PT_Yxy: return icSigYxyData; - case PT_HiFi: return icSigHexachromeData; - case PT_HiFi7: return icSigHeptachromeData; - case PT_HiFi8: return icSigOctachromeData; + // Something went wrong... + if (KTone == NULL) return NULL; - case PT_HiFi9: return icSigMCH9Data; - case PT_HiFi10: return icSigMCHAData; - case PT_HiFi11: return icSigMCHBData; - case PT_HiFi12: return icSigMCHCData; - case PT_HiFi13: return icSigMCHDData; - case PT_HiFi14: return icSigMCHEData; - case PT_HiFi15: return icSigMCHFData; + // Make sure it is monotonic + if (!cmsIsToneCurveMonotonic(KTone)) { + cmsFreeToneCurve(KTone); + return NULL; + } - default: return icMaxEnumData; - } + return KTone; } -int LCMSEXPORT _cmsLCMScolorSpace(icColorSpaceSignature ProfileSpace) -{ - switch (ProfileSpace) { - - case icSigGrayData: return PT_GRAY; - case icSigRgbData: return PT_RGB; - case icSigCmyData: return PT_CMY; - case icSigCmykData: return PT_CMYK; - case icSigYCbCrData:return PT_YCbCr; - case icSigLuvData: return PT_YUV; - case icSigXYZData: return PT_XYZ; - case icSigLabData: return PT_Lab; - case icSigLuvKData: return PT_YUVK; - case icSigHsvData: return PT_HSV; - case icSigHlsData: return PT_HLS; - case icSigYxyData: return PT_Yxy; - - case icSig6colorData: - case icSigHexachromeData: return PT_HiFi; - - case icSigHeptachromeData: - case icSig7colorData: return PT_HiFi7; - - case icSigOctachromeData: - case icSig8colorData: return PT_HiFi8; - - case icSigMCH9Data: - case icSig9colorData: return PT_HiFi9; - - case icSigMCHAData: - case icSig10colorData: return PT_HiFi10; - - case icSigMCHBData: - case icSig11colorData: return PT_HiFi11; - - case icSigMCHCData: - case icSig12colorData: return PT_HiFi12; - - case icSigMCHDData: - case icSig13colorData: return PT_HiFi13; - - case icSigMCHEData: - case icSig14colorData: return PT_HiFi14; - - case icSigMCHFData: - case icSig15colorData: return PT_HiFi15; - - default: return icMaxEnumData; - } -} - - -int LCMSEXPORT _cmsChannelsOf(icColorSpaceSignature ColorSpace) -{ - - switch (ColorSpace) { - - case icSigGrayData: return 1; - - case icSig2colorData: return 2; - - case icSigXYZData: - case icSigLabData: - case icSigLuvData: - case icSigYCbCrData: - case icSigYxyData: - case icSigRgbData: - case icSigHsvData: - case icSigHlsData: - case icSigCmyData: - case icSig3colorData: return 3; - - case icSigLuvKData: - case icSigCmykData: - case icSig4colorData: return 4; - - case icSigMCH5Data: - case icSig5colorData: return 5; - - case icSigHexachromeData: - case icSig6colorData: return 6; - - case icSigHeptachromeData: - case icSig7colorData: return 7; - - case icSigOctachromeData: - case icSig8colorData: return 8; - - case icSigMCH9Data: - case icSig9colorData: return 9; - - case icSigMCHAData: - case icSig10colorData: return 10; - - case icSigMCHBData: - case icSig11colorData: return 11; - - case icSigMCHCData: - case icSig12colorData: return 12; - - case icSigMCHDData: - case icSig13colorData: return 13; - - case icSigMCHEData: - case icSig14colorData: return 14; - - case icSigMCHFData: - case icSig15colorData: return 15; - - default: return 3; - } - -} - - -// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable -// number of gridpoints that would make exact match. However, a -// prelinearization of 258 entries, would map 0xFF00 on entry 257. -// This is almost what we need, unfortunately, the rest of entries -// should be scaled by (255*257/256) and this is not exact. -// -// An intermediate solution would be to use 257 entries. This does not -// map 0xFF00 exactly on a node, but so close that the dE induced is -// negligible. AND the rest of curve is exact. - -static -void CreateLabPrelinearization(LPGAMMATABLE LabTable[]) -{ - int i; - - LabTable[0] = cmsAllocGamma(257); - LabTable[1] = cmsBuildGamma(257, 1.0); - LabTable[2] = cmsBuildGamma(257, 1.0); - - // L* uses 257 entries. Entry 256 holds 0xFFFF, so, the effective range - // is 0..0xFF00. Last entry (257) is also collapsed to 0xFFFF - - // From 0 to 0xFF00 - for (i=0; i < 256; i++) - LabTable[0]->GammaTable[i] = RGB_8_TO_16(i); - - // Repeat last for 0xFFFF - LabTable[0] ->GammaTable[256] = 0xFFFF; -} - +// Gamut LUT Creation ----------------------------------------------------------------------------------------- // Used by gamut & softproofing typedef struct { - cmsHTRANSFORM hInput; // From whatever input color space. NULL for Lab + cmsHTRANSFORM hInput; // From whatever input color space. 16 bits to DBL cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back - double Thereshold; // The thereshold after which is considered out of gamut + cmsFloat64Number Thereshold; // The thereshold after which is considered out of gamut - } GAMUTCHAIN,FAR* LPGAMUTCHAIN; + } GAMUTCHAIN; // This sampler does compute gamut boundaries by comparing original // values with a transform going back and forth. Values above ERR_THERESHOLD // of maximum are considered out of gamut. - #define ERR_THERESHOLD 5 static -int GamutSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) { - LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo; - WORD Proof[MAXCHANNELS], Check[MAXCHANNELS]; - WORD Proof2[MAXCHANNELS], Check2[MAXCHANNELS]; + GAMUTCHAIN* t = (GAMUTCHAIN* ) Cargo; cmsCIELab LabIn1, LabOut1; cmsCIELab LabIn2, LabOut2; - double dE1, dE2, ErrorRatio; + cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS]; + cmsFloat64Number dE1, dE2, ErrorRatio; // Assume in-gamut by default. - dE1 = 0.; - dE2 = 0; ErrorRatio = 1.0; - - // Any input space? I can use In[] no matter channels - // because is just one pixel - - if (t -> hInput != NULL) cmsDoTransform(t -> hInput, In, In, 1); + // Convert input to Lab + cmsDoTransform(t -> hInput, In, &LabIn1, 1); // converts from PCS to colorant. This always // does return in-gamut values, - cmsDoTransform(t -> hForward, In, Proof, 1); + cmsDoTransform(t -> hForward, &LabIn1, Proof, 1); // Now, do the inverse, from colorant to PCS. - cmsDoTransform(t -> hReverse, Proof, Check, 1); + cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1); + memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab)); // Try again, but this time taking Check as input - cmsDoTransform(t -> hForward, Check, Proof2, 1); - cmsDoTransform(t -> hReverse, Proof2, Check2, 1); + cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1); + cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1); + // Take difference of direct value + dE1 = cmsDeltaE(&LabIn1, &LabOut1); + + // Take difference of converted value + dE2 = cmsDeltaE(&LabIn2, &LabOut2); - // Does the transform returns out-of-gamut? - if (Check[0] == 0xFFFF && - Check[1] == 0xFFFF && - Check[2] == 0xFFFF) - - Out[0] = 0xFF00; // Out of gamut! + // if dE1 is small and dE2 is small, value is likely to be in gamut + if (dE1 < t->Thereshold && dE2 < t->Thereshold) + Out[0] = 0; else { - // Transport encoded values - cmsLabEncoded2Float(&LabIn1, In); - cmsLabEncoded2Float(&LabOut1, Check); - - // Take difference of direct value - dE1 = cmsDeltaE(&LabIn1, &LabOut1); - - cmsLabEncoded2Float(&LabIn2, Check); - cmsLabEncoded2Float(&LabOut2, Check2); - - // Take difference of converted value - dE2 = cmsDeltaE(&LabIn2, &LabOut2); - - - // if dE1 is small and dE2 is small, value is likely to be in gamut - if (dE1 < t->Thereshold && dE2 < t->Thereshold) + // if dE1 is small and dE2 is big, undefined. Assume in gamut + if (dE1 < t->Thereshold && dE2 > t->Thereshold) Out[0] = 0; else - // if dE1 is small and dE2 is big, undefined. Assume in gamut - if (dE1 < t->Thereshold && dE2 > t->Thereshold) - Out[0] = 0; - else - // dE1 is big and dE2 is small, clearly out of gamut - if (dE1 > t->Thereshold && dE2 < t->Thereshold) - Out[0] = (WORD) _cmsQuickFloor((dE1 - t->Thereshold) + .5); - else { + // dE1 is big and dE2 is small, clearly out of gamut + if (dE1 > t->Thereshold && dE2 < t->Thereshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5); + else { - // dE1 is big and dE2 is also big, could be due to perceptual mapping - // so take error ratio - if (dE2 == 0.0) - ErrorRatio = dE1; - else - ErrorRatio = dE1 / dE2; + // dE1 is big and dE2 is also big, could be due to perceptual mapping + // so take error ratio + if (dE2 == 0.0) + ErrorRatio = dE1; + else + ErrorRatio = dE1 / dE2; - if (ErrorRatio > t->Thereshold) - Out[0] = (WORD) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); - else - Out[0] = 0; - } + if (ErrorRatio > t->Thereshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); + else + Out[0] = 0; + } + } - } return TRUE; } - -// Does compute a gamut LUT going back and forth across -// pcs -> relativ. colorimetric intent -> pcs -// the dE obtained is then annotated on the LUT. -// values truely out of gamut, are clipped to dE = 0xFFFE -// and values changed are supposed to be handled by -// any gamut remapping, so, are out of gamut as well. +// Does compute a gamut LUT going back and forth across pcs -> relativ. colorimetric intent -> pcs +// the dE obtained is then annotated on the LUT. Values truly out of gamut are clipped to dE = 0xFFFE +// and values changed are supposed to be handled by any gamut remapping, so, are out of gamut as well. // -// **WARNING: This algorithm does assume that gamut -// remapping algorithms does NOT move in-gamut colors, -// of course, many perceptual and saturation intents does -// not work in such way, but relativ. ones should. +// **WARNING: This algorithm does assume that gamut remapping algorithms does NOT move in-gamut colors, +// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should. -static -LPLUT ComputeGamutWithInput(cmsHPROFILE hInput, cmsHPROFILE hProfile, int Intent) +cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number nGamutPCSposition, + cmsHPROFILE hGamut) { cmsHPROFILE hLab; - LPLUT Gamut; - DWORD dwFormat; + cmsPipeline* Gamut; + cmsStage* CLUT; + cmsUInt32Number dwFormat; GAMUTCHAIN Chain; - int nErrState, nChannels, nGridpoints; - LPGAMMATABLE Trans[3]; - icColorSpaceSignature ColorSpace; + cmsUInt32Number nChannels, nGridpoints; + cmsColorSpaceSignature ColorSpace; + cmsUInt32Number i; + cmsHPROFILE ProfileList[256]; + cmsBool BPCList[256]; + cmsFloat64Number AdaptationList[256]; + cmsUInt32Number IntentList[256]; + + memset(&Chain, 0, sizeof(GAMUTCHAIN)); - ZeroMemory(&Chain, sizeof(GAMUTCHAIN)); + if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition); + return NULL; + } - hLab = cmsCreateLabProfile(NULL); + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return NULL; - // Safeguard against early abortion - nErrState = cmsErrorAction(LCMS_ERROR_IGNORE); // The figure of merit. On matrix-shaper profiles, should be almost zero as // the conversion is pretty exact. On LUT based profiles, different resolutions // of input and output CLUT may result in differences. - if (!cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) && - !cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_OUTPUT)) + if (cmsIsMatrixShaper(hGamut)) { Chain.Thereshold = 1.0; - else + } + else { Chain.Thereshold = ERR_THERESHOLD; - - ColorSpace = cmsGetColorSpace(hProfile); - - // If input profile specified, create a transform from such profile to Lab - if (hInput != NULL) { - - nChannels = _cmsChannelsOf(ColorSpace); - nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); - dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); - - Chain.hInput = cmsCreateTransform(hInput, dwFormat, - hLab, TYPE_Lab_16, - Intent, - cmsFLAGS_NOTPRECALC); - } - else { - // Input transform=NULL (Lab) Used to compute the gamut tag - // This table will take 53 points to give some accurancy, - // 53 * 53 * 53 * 2 = 291K - - nChannels = 3; // For Lab - nGridpoints = 53; - Chain.hInput = NULL; - dwFormat = (CHANNELS_SH(_cmsChannelsOf(ColorSpace))|BYTES_SH(2)); } - // Does create the forward step - Chain.hForward = cmsCreateTransform(hLab, TYPE_Lab_16, - hProfile, dwFormat, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOTPRECALC); + // Create a copy of parameters + for (i=0; i < nGamutPCSposition; i++) { + ProfileList[i] = hProfiles[i]; + BPCList[i] = BPC[i]; + AdaptationList[i] = AdaptationStates[i]; + IntentList[i] = Intents[i]; + } + + // Fill Lab identity + ProfileList[nGamutPCSposition] = hLab; + BPCList[nGamutPCSposition] = 0; + AdaptationList[nGamutPCSposition] = 1.0; + IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; + + + ColorSpace = cmsGetColorSpace(hGamut); + + nChannels = cmsChannelsOf(ColorSpace); + nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); + dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + + // 16 bits to Lab double + Chain.hInput = cmsCreateExtendedTransform(ContextID, + nGamutPCSposition + 1, + ProfileList, + BPCList, + IntentList, + AdaptationList, + NULL, 0, + dwFormat, TYPE_Lab_DBL, + cmsFLAGS_NOCACHE); + + + // Does create the forward step. Lab double to device + dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + Chain.hForward = cmsCreateTransformTHR(ContextID, + hLab, TYPE_Lab_DBL, + hGamut, dwFormat, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE); // Does create the backwards step - Chain.hReverse = cmsCreateTransform(hProfile, dwFormat, - hLab, TYPE_Lab_16, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOTPRECALC); - - // Restores error handler previous state - cmsErrorAction(nErrState); + Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat, + hLab, TYPE_Lab_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE); // All ok? - if (Chain.hForward && Chain.hReverse) { + if (Chain.hInput && Chain.hForward && Chain.hReverse) { - // Go on, try to compute gamut LUT from PCS. - // This consist on a single channel containing - // dE when doing a transform back and forth on - // the colorimetric intent. + // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing + // dE when doing a transform back and forth on the colorimetric intent. - Gamut = cmsAllocLUT(); - Gamut = cmsAlloc3DGrid(Gamut, nGridpoints, nChannels, 1); + Gamut = cmsPipelineAlloc(ContextID, 3, 1); + if (Gamut != NULL) { - // If no input, then this is a gamut tag operated by Lab, - // so include pertinent prelinearization - if (hInput == NULL) { - - CreateLabPrelinearization(Trans); - cmsAllocLinearTable(Gamut, Trans, 1); - cmsFreeGammaTriple(Trans); - } - - - cmsSample3DGrid(Gamut, GamutSampler, (LPVOID) &Chain, Gamut ->wFlags); + CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL); + if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) { + cmsPipelineFree(Gamut); + Gamut = NULL; + } + else { + cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0); + } + } } else Gamut = NULL; // Didn't work... @@ -921,352 +433,187 @@ if (Chain.hInput) cmsDeleteTransform(Chain.hInput); if (Chain.hForward) cmsDeleteTransform(Chain.hForward); if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse); - - cmsCloseProfile(hLab); + if (hLab) cmsCloseProfile(hLab); // And return computed hull return Gamut; } - -// Wrapper +// Total Area Coverage estimation ---------------------------------------------------------------- -LPLUT _cmsComputeGamutLUT(cmsHPROFILE hProfile, int Intent) -{ - return ComputeGamutWithInput(NULL, hProfile, Intent); -} +typedef struct { + cmsUInt32Number nOutputChans; + cmsHTRANSFORM hRoundTrip; + cmsFloat32Number MaxTAC; + cmsFloat32Number MaxInput[cmsMAXCHANNELS]; + +} cmsTACestimator; -// This routine does compute the gamut check CLUT. This CLUT goes from whatever -// input space to the 0 or != 0 gamut check. +// This callback just accounts the maximum ink dropped in the given node. It does not populate any +// memory, as the destination table is NULL. Its only purpose it to know the global maximum. +static +int EstimateTAC(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo) +{ + cmsTACestimator* bp = (cmsTACestimator*) Cargo; + cmsFloat32Number RoundTrip[cmsMAXCHANNELS]; + cmsUInt32Number i; + cmsFloat32Number Sum; + + + // Evaluate the xform + cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); -LPLUT _cmsPrecalculateGamutCheck(cmsHTRANSFORM h) -{ - _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h; + // All all amounts of ink + for (Sum=0, i=0; i < bp ->nOutputChans; i++) + Sum += RoundTrip[i]; + + // If above maximum, keep track of input values + if (Sum > bp ->MaxTAC) { + + bp ->MaxTAC = Sum; - return ComputeGamutWithInput(p->InputProfile, p ->PreviewProfile, p->Intent); + for (i=0; i < bp ->nOutputChans; i++) { + bp ->MaxInput[i] = In[i]; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(Out); } -// SoftProofing. Convert from Lab to device, then back to Lab, -// any gamut remapping is applied - -static -int SoftProofSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +// Detect Total area coverage of the profile +cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile) { - LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo; - WORD Colorant[MAXCHANNELS]; + cmsTACestimator bp; + cmsUInt32Number dwFormatter; + cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS]; + cmsHPROFILE hLab; + cmsContext ContextID = cmsGetProfileContextID(hProfile); - // From pcs to colorant - cmsDoTransform(t -> hForward, In, Colorant, 1); + // TAC only works on output profiles + if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) { + return 0; + } + + // Create a fake formatter for result + dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE); - // Now, do the inverse, from colorant to pcs. - cmsDoTransform(t -> hReverse, Colorant, Out, 1); + bp.nOutputChans = T_CHANNELS(dwFormatter); + bp.MaxTAC = 0; // Initial TAC is 0 - return TRUE; -} - -// Does return Softproofing LUT on desired intent + // for safety + if (bp.nOutputChans >= cmsMAXCHANNELS) return 0; -LPLUT _cmsComputeSoftProofLUT(cmsHPROFILE hProfile, int nIntent) -{ - cmsHPROFILE hLab; - LPLUT SoftProof; - DWORD dwFormat; - GAMUTCHAIN Chain; - int nErrState; - LPGAMMATABLE Trans[3]; + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return 0; + // Setup a roundtrip on perceptual intent in output profile for TAC estimation + bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16, + hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); + + cmsCloseProfile(hLab); + if (bp.hRoundTrip == NULL) return 0; + + // For L* we only need black and white. For C* we need many points + GridPoints[0] = 6; + GridPoints[1] = 74; + GridPoints[2] = 74; - // LUTs are never abs. colorimetric, is the transform who - // is responsible of generating white point displacement - if (nIntent == INTENT_ABSOLUTE_COLORIMETRIC) - nIntent = INTENT_RELATIVE_COLORIMETRIC; - - ZeroMemory(&Chain, sizeof(GAMUTCHAIN)); - - hLab = cmsCreateLabProfile(NULL); - - // ONLY 4 channels - dwFormat = (CHANNELS_SH(4)|BYTES_SH(2)); - - // Safeguard against early abortion - nErrState = cmsErrorAction(LCMS_ERROR_IGNORE); - - // Does create the first step - Chain.hForward = cmsCreateTransform(hLab, TYPE_Lab_16, - hProfile, dwFormat, - nIntent, - cmsFLAGS_NOTPRECALC); - - // Does create the last step - Chain.hReverse = cmsCreateTransform(hProfile, dwFormat, - hLab, TYPE_Lab_16, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOTPRECALC); + if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) { + bp.MaxTAC = 0; + } - // Restores error handler previous state - cmsErrorAction(nErrState); - - // All ok? - if (Chain.hForward && Chain.hReverse) { - - // This is Lab -> Lab, so 33 point should hold anything - SoftProof = cmsAllocLUT(); - SoftProof = cmsAlloc3DGrid(SoftProof, 33, 3, 3); + cmsDeleteTransform(bp.hRoundTrip); - CreateLabPrelinearization(Trans); - cmsAllocLinearTable(SoftProof, Trans, 1); - cmsFreeGammaTriple(Trans); - - cmsSample3DGrid(SoftProof, SoftProofSampler, (LPVOID) &Chain, SoftProof->wFlags); - } - else - SoftProof = NULL; // Didn't work... - - // Free all needed stuff. - if (Chain.hForward) cmsDeleteTransform(Chain.hForward); - if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse); - - cmsCloseProfile(hLab); - - return SoftProof; + // Results in % + return bp.MaxTAC; } -static -int MostlyLinear(WORD Table[], int nEntries) +// Carefully, clamp on CIELab space. + +cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, + double amax, double amin, + double bmax, double bmin) { - register int i; - int diff; + + // Whole Luma surface to zero - for (i=5; i < nEntries; i++) { + if (Lab -> L < 0) { + + Lab-> L = Lab->a = Lab-> b = 0.0; + return FALSE; + } - diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries)); - if (diff > 0x0300) - return 0; - } + // Clamp white, DISCARD HIGHLIGHTS. This is done + // in such way because icc spec doesn't allow the + // use of L>100 as a highlight means. + + if (Lab->L > 100) + Lab -> L = 100; + + // Check out gamut prism, on a, b faces - return 1; -} + if (Lab -> a < amin || Lab->a > amax|| + Lab -> b < bmin || Lab->b > bmax) { + + cmsCIELCh LCh; + double h, slope; + + // Falls outside a, b limits. Transports to LCh space, + // and then do the clipping -static -void SlopeLimiting(WORD Table[], int nEntries) -{ - int At = (int) floor((double) nEntries * 0.02 + 0.5); // Cutoff at 2% - double Val, Slope; - int i; + if (Lab -> a == 0.0) { // Is hue exactly 90? + + // atan will not work, so clamp here + Lab -> b = Lab->b < 0 ? bmin : bmax; + return TRUE; + } - Val = Table[At]; - Slope = Val / At; + cmsLab2LCh(&LCh, Lab); + + slope = Lab -> b / Lab -> a; + h = LCh.h; - for (i=0; i < At; i++) - Table[i] = (WORD) floor(i * Slope + 0.5); + // There are 4 zones -} - + if ((h >= 0. && h < 45.) || + (h >= 315 && h <= 360.)) { -// Check for monotonicity. - -static -LCMSBOOL IsMonotonic(LPGAMMATABLE t) -{ - int n = t -> nEntries; - int i, last; + // clip by amax + Lab -> a = amax; + Lab -> b = amax * slope; + } + else + if (h >= 45. && h < 135.) + { + // clip by bmax + Lab -> b = bmax; + Lab -> a = bmax / slope; + } + else + if (h >= 135. && h < 225.) { + // clip by amin + Lab -> a = amin; + Lab -> b = amin * slope; - last = t ->GammaTable[n-1]; - - for (i = n-2; i >= 0; --i) { - - if (t ->GammaTable[i] > last) - - return FALSE; - else - last = t ->GammaTable[i]; + } + else + if (h >= 225. && h < 315.) { + // clip by bmin + Lab -> b = bmin; + Lab -> a = bmin / slope; + } + else { + cmsSignalError(0, cmsERROR_RANGE, "Invalid angle"); + return FALSE; + } } return TRUE; } - -// Check for endpoints - -static -LCMSBOOL HasProperEndpoints(LPGAMMATABLE t) -{ - if (t ->GammaTable[0] != 0) return FALSE; - if (t ->GammaTable[t ->nEntries-1] != 0xFFFF) return FALSE; - - return TRUE; -} - - - -#define PRELINEARIZATION_POINTS 4096 - -// Fixes the gamma balancing of transform. Thanks to Mike Chaney -// for pointing this subtle bug. - -void _cmsComputePrelinearizationTablesFromXFORM(cmsHTRANSFORM h[], int nTransforms, LPLUT Grid) -{ - LPGAMMATABLE Trans[MAXCHANNELS]; - unsigned int t, i, v; - int j; - WORD In[MAXCHANNELS], Out[MAXCHANNELS]; - LCMSBOOL lIsSuitable; - _LPcmsTRANSFORM InputXForm = (_LPcmsTRANSFORM) h[0]; - _LPcmsTRANSFORM OutputXForm = (_LPcmsTRANSFORM) h[nTransforms-1]; - - - // First space is *Lab, use our specialized curves for v2 Lab - - if (InputXForm ->EntryColorSpace == icSigLabData && - OutputXForm->ExitColorSpace != icSigLabData) { - - CreateLabPrelinearization(Trans); - cmsAllocLinearTable(Grid, Trans, 1); - cmsFreeGammaTriple(Trans); - return; - } - - - // Do nothing on all but Gray/RGB to Gray/RGB transforms - - if (((InputXForm ->EntryColorSpace != icSigRgbData) && (InputXForm ->EntryColorSpace != icSigGrayData)) || - ((OutputXForm->ExitColorSpace != icSigRgbData) && (OutputXForm->ExitColorSpace != icSigGrayData))) return; - - - for (t = 0; t < Grid -> InputChan; t++) - Trans[t] = cmsAllocGamma(PRELINEARIZATION_POINTS); - - for (i=0; i < PRELINEARIZATION_POINTS; i++) { - - v = _cmsQuantizeVal(i, PRELINEARIZATION_POINTS); - - for (t=0; t < Grid -> InputChan; t++) - In[t] = (WORD) v; - - cmsDoTransform(h[0], In, Out, 1); - for (j=1; j < nTransforms; j++) - cmsDoTransform(h[j], Out, Out, 1); - - for (t=0; t < Grid -> InputChan; t++) - Trans[t] ->GammaTable[i] = Out[t]; - - } - - - // Check transfer curves - lIsSuitable = TRUE; - for (t=0; (lIsSuitable && (t < Grid->InputChan)); t++) { - - - // Exclude if already linear - if (MostlyLinear(Trans[t]->GammaTable, PRELINEARIZATION_POINTS)) - lIsSuitable = FALSE; - - // Exclude if non-monotonic - if (!IsMonotonic(Trans[t])) - lIsSuitable = FALSE; - - // Exclude if weird endpoints - if (!HasProperEndpoints(Trans[t])) - lIsSuitable = FALSE; - - /* - // Exclude if transfer function is not smooth enough - // to be modelled as a gamma function, or the gamma is reversed - - if (cmsEstimateGamma(Trans[t]) < 1.0) - lIsSuitable = FALSE; - */ - - } - - if (lIsSuitable) { - - for (t = 0; t < Grid ->InputChan; t++) - SlopeLimiting(Trans[t]->GammaTable, Trans[t]->nEntries); - } - - if (lIsSuitable) cmsAllocLinearTable(Grid, Trans, 1); - - - for (t = 0; t < Grid ->InputChan; t++) - cmsFreeGamma(Trans[t]); - - -} - - -// Compute K -> L* relationship. Flags may include black point compensation. In this case, -// the relationship is assumed from the profile with BPC to a black point zero. -static -LPGAMMATABLE ComputeKToLstar(cmsHPROFILE hProfile, int nPoints, int Intent, DWORD dwFlags) -{ - LPGAMMATABLE out; - int i; - WORD cmyk[4], wLab[3]; - cmsHPROFILE hLab = cmsCreateLabProfile(NULL); - cmsHTRANSFORM xform = cmsCreateTransform(hProfile, TYPE_CMYK_16, - hLab, TYPE_Lab_16, - Intent, (dwFlags|cmsFLAGS_NOTPRECALC)); - - - out = cmsAllocGamma(nPoints); - for (i=0; i < nPoints; i++) { - - cmyk[0] = 0; - cmyk[1] = 0; - cmyk[2] = 0; - cmyk[3] = _cmsQuantizeVal(i, nPoints); - - cmsDoTransform(xform, cmyk, wLab, 1); - out->GammaTable[i] = (WORD) (0xFFFF - wLab[0]); - } - - cmsDeleteTransform(xform); - cmsCloseProfile(hLab); - - return out; -} - - - -// Compute Black tone curve on a CMYK -> CMYK transform. This is done by -// using the proof direction on both profiles to find K->L* relationship -// then joining both curves. dwFlags may include black point compensation. - -LPGAMMATABLE _cmsBuildKToneCurve(cmsHTRANSFORM hCMYK2CMYK, int nPoints) -{ - LPGAMMATABLE in, out; - LPGAMMATABLE KTone; - _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK; - - - // Make sure CMYK -> CMYK - if (p -> EntryColorSpace != icSigCmykData || - p -> ExitColorSpace != icSigCmykData) return NULL; - - // Create individual curves. BPC works also as each K to L* is - // computed as a BPC to zero black point in case of L* - in = ComputeKToLstar(p ->InputProfile, nPoints, p->Intent, p -> dwOriginalFlags); - out = ComputeKToLstar(p ->OutputProfile, nPoints, p->Intent, p -> dwOriginalFlags); - - // Build the relationship - KTone = cmsJoinGamma(in, out); - - cmsFreeGamma(in); cmsFreeGamma(out); - - // Make sure it is monotonic - - if (!IsMonotonic(KTone)) { - - cmsFreeGamma(KTone); - return NULL; - } - - - return KTone; -} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmshalf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/cmshalf.c Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,564 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +// +#include "lcms2_internal.h" + +#ifndef CMS_NO_HALF_SUPPORT + +// This code is inspired in the paper "Fast Half Float Conversions" +// by Jeroen van der Zijp + +static cmsUInt32Number Mantissa[2048] = { + +0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000, +0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000, +0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000, +0x35900000, 0x35980000, 0x35a00000, 0x35a80000, 0x35b00000, 0x35b80000, +0x35c00000, 0x35c80000, 0x35d00000, 0x35d80000, 0x35e00000, 0x35e80000, +0x35f00000, 0x35f80000, 0x36000000, 0x36040000, 0x36080000, 0x360c0000, +0x36100000, 0x36140000, 0x36180000, 0x361c0000, 0x36200000, 0x36240000, +0x36280000, 0x362c0000, 0x36300000, 0x36340000, 0x36380000, 0x363c0000, +0x36400000, 0x36440000, 0x36480000, 0x364c0000, 0x36500000, 0x36540000, +0x36580000, 0x365c0000, 0x36600000, 0x36640000, 0x36680000, 0x366c0000, +0x36700000, 0x36740000, 0x36780000, 0x367c0000, 0x36800000, 0x36820000, +0x36840000, 0x36860000, 0x36880000, 0x368a0000, 0x368c0000, 0x368e0000, +0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369a0000, +0x369c0000, 0x369e0000, 0x36a00000, 0x36a20000, 0x36a40000, 0x36a60000, +0x36a80000, 0x36aa0000, 0x36ac0000, 0x36ae0000, 0x36b00000, 0x36b20000, +0x36b40000, 0x36b60000, 0x36b80000, 0x36ba0000, 0x36bc0000, 0x36be0000, +0x36c00000, 0x36c20000, 0x36c40000, 0x36c60000, 0x36c80000, 0x36ca0000, +0x36cc0000, 0x36ce0000, 0x36d00000, 0x36d20000, 0x36d40000, 0x36d60000, +0x36d80000, 0x36da0000, 0x36dc0000, 0x36de0000, 0x36e00000, 0x36e20000, +0x36e40000, 0x36e60000, 0x36e80000, 0x36ea0000, 0x36ec0000, 0x36ee0000, +0x36f00000, 0x36f20000, 0x36f40000, 0x36f60000, 0x36f80000, 0x36fa0000, +0x36fc0000, 0x36fe0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000, +0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000, +0x370a0000, 0x370b0000, 0x370c0000, 0x370d0000, 0x370e0000, 0x370f0000, +0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000, +0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371a0000, 0x371b0000, +0x371c0000, 0x371d0000, 0x371e0000, 0x371f0000, 0x37200000, 0x37210000, +0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000, +0x37280000, 0x37290000, 0x372a0000, 0x372b0000, 0x372c0000, 0x372d0000, +0x372e0000, 0x372f0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000, +0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000, +0x373a0000, 0x373b0000, 0x373c0000, 0x373d0000, 0x373e0000, 0x373f0000, +0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000, +0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374a0000, 0x374b0000, +0x374c0000, 0x374d0000, 0x374e0000, 0x374f0000, 0x37500000, 0x37510000, +0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000, +0x37580000, 0x37590000, 0x375a0000, 0x375b0000, 0x375c0000, 0x375d0000, +0x375e0000, 0x375f0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000, +0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000, +0x376a0000, 0x376b0000, 0x376c0000, 0x376d0000, 0x376e0000, 0x376f0000, +0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000, +0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377a0000, 0x377b0000, +0x377c0000, 0x377d0000, 0x377e0000, 0x377f0000, 0x37800000, 0x37808000, +0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000, +0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000, +0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000, +0x378a0000, 0x378a8000, 0x378b0000, 0x378b8000, 0x378c0000, 0x378c8000, +0x378d0000, 0x378d8000, 0x378e0000, 0x378e8000, 0x378f0000, 0x378f8000, +0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000, +0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000, +0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000, +0x37990000, 0x37998000, 0x379a0000, 0x379a8000, 0x379b0000, 0x379b8000, +0x379c0000, 0x379c8000, 0x379d0000, 0x379d8000, 0x379e0000, 0x379e8000, +0x379f0000, 0x379f8000, 0x37a00000, 0x37a08000, 0x37a10000, 0x37a18000, +0x37a20000, 0x37a28000, 0x37a30000, 0x37a38000, 0x37a40000, 0x37a48000, +0x37a50000, 0x37a58000, 0x37a60000, 0x37a68000, 0x37a70000, 0x37a78000, +0x37a80000, 0x37a88000, 0x37a90000, 0x37a98000, 0x37aa0000, 0x37aa8000, +0x37ab0000, 0x37ab8000, 0x37ac0000, 0x37ac8000, 0x37ad0000, 0x37ad8000, +0x37ae0000, 0x37ae8000, 0x37af0000, 0x37af8000, 0x37b00000, 0x37b08000, +0x37b10000, 0x37b18000, 0x37b20000, 0x37b28000, 0x37b30000, 0x37b38000, +0x37b40000, 0x37b48000, 0x37b50000, 0x37b58000, 0x37b60000, 0x37b68000, +0x37b70000, 0x37b78000, 0x37b80000, 0x37b88000, 0x37b90000, 0x37b98000, +0x37ba0000, 0x37ba8000, 0x37bb0000, 0x37bb8000, 0x37bc0000, 0x37bc8000, +0x37bd0000, 0x37bd8000, 0x37be0000, 0x37be8000, 0x37bf0000, 0x37bf8000, +0x37c00000, 0x37c08000, 0x37c10000, 0x37c18000, 0x37c20000, 0x37c28000, +0x37c30000, 0x37c38000, 0x37c40000, 0x37c48000, 0x37c50000, 0x37c58000, +0x37c60000, 0x37c68000, 0x37c70000, 0x37c78000, 0x37c80000, 0x37c88000, +0x37c90000, 0x37c98000, 0x37ca0000, 0x37ca8000, 0x37cb0000, 0x37cb8000, +0x37cc0000, 0x37cc8000, 0x37cd0000, 0x37cd8000, 0x37ce0000, 0x37ce8000, +0x37cf0000, 0x37cf8000, 0x37d00000, 0x37d08000, 0x37d10000, 0x37d18000, +0x37d20000, 0x37d28000, 0x37d30000, 0x37d38000, 0x37d40000, 0x37d48000, +0x37d50000, 0x37d58000, 0x37d60000, 0x37d68000, 0x37d70000, 0x37d78000, +0x37d80000, 0x37d88000, 0x37d90000, 0x37d98000, 0x37da0000, 0x37da8000, +0x37db0000, 0x37db8000, 0x37dc0000, 0x37dc8000, 0x37dd0000, 0x37dd8000, +0x37de0000, 0x37de8000, 0x37df0000, 0x37df8000, 0x37e00000, 0x37e08000, +0x37e10000, 0x37e18000, 0x37e20000, 0x37e28000, 0x37e30000, 0x37e38000, +0x37e40000, 0x37e48000, 0x37e50000, 0x37e58000, 0x37e60000, 0x37e68000, +0x37e70000, 0x37e78000, 0x37e80000, 0x37e88000, 0x37e90000, 0x37e98000, +0x37ea0000, 0x37ea8000, 0x37eb0000, 0x37eb8000, 0x37ec0000, 0x37ec8000, +0x37ed0000, 0x37ed8000, 0x37ee0000, 0x37ee8000, 0x37ef0000, 0x37ef8000, +0x37f00000, 0x37f08000, 0x37f10000, 0x37f18000, 0x37f20000, 0x37f28000, +0x37f30000, 0x37f38000, 0x37f40000, 0x37f48000, 0x37f50000, 0x37f58000, +0x37f60000, 0x37f68000, 0x37f70000, 0x37f78000, 0x37f80000, 0x37f88000, +0x37f90000, 0x37f98000, 0x37fa0000, 0x37fa8000, 0x37fb0000, 0x37fb8000, +0x37fc0000, 0x37fc8000, 0x37fd0000, 0x37fd8000, 0x37fe0000, 0x37fe8000, +0x37ff0000, 0x37ff8000, 0x38000000, 0x38004000, 0x38008000, 0x3800c000, +0x38010000, 0x38014000, 0x38018000, 0x3801c000, 0x38020000, 0x38024000, +0x38028000, 0x3802c000, 0x38030000, 0x38034000, 0x38038000, 0x3803c000, +0x38040000, 0x38044000, 0x38048000, 0x3804c000, 0x38050000, 0x38054000, +0x38058000, 0x3805c000, 0x38060000, 0x38064000, 0x38068000, 0x3806c000, +0x38070000, 0x38074000, 0x38078000, 0x3807c000, 0x38080000, 0x38084000, +0x38088000, 0x3808c000, 0x38090000, 0x38094000, 0x38098000, 0x3809c000, +0x380a0000, 0x380a4000, 0x380a8000, 0x380ac000, 0x380b0000, 0x380b4000, +0x380b8000, 0x380bc000, 0x380c0000, 0x380c4000, 0x380c8000, 0x380cc000, +0x380d0000, 0x380d4000, 0x380d8000, 0x380dc000, 0x380e0000, 0x380e4000, +0x380e8000, 0x380ec000, 0x380f0000, 0x380f4000, 0x380f8000, 0x380fc000, +0x38100000, 0x38104000, 0x38108000, 0x3810c000, 0x38110000, 0x38114000, +0x38118000, 0x3811c000, 0x38120000, 0x38124000, 0x38128000, 0x3812c000, +0x38130000, 0x38134000, 0x38138000, 0x3813c000, 0x38140000, 0x38144000, +0x38148000, 0x3814c000, 0x38150000, 0x38154000, 0x38158000, 0x3815c000, +0x38160000, 0x38164000, 0x38168000, 0x3816c000, 0x38170000, 0x38174000, +0x38178000, 0x3817c000, 0x38180000, 0x38184000, 0x38188000, 0x3818c000, +0x38190000, 0x38194000, 0x38198000, 0x3819c000, 0x381a0000, 0x381a4000, +0x381a8000, 0x381ac000, 0x381b0000, 0x381b4000, 0x381b8000, 0x381bc000, +0x381c0000, 0x381c4000, 0x381c8000, 0x381cc000, 0x381d0000, 0x381d4000, +0x381d8000, 0x381dc000, 0x381e0000, 0x381e4000, 0x381e8000, 0x381ec000, +0x381f0000, 0x381f4000, 0x381f8000, 0x381fc000, 0x38200000, 0x38204000, +0x38208000, 0x3820c000, 0x38210000, 0x38214000, 0x38218000, 0x3821c000, +0x38220000, 0x38224000, 0x38228000, 0x3822c000, 0x38230000, 0x38234000, +0x38238000, 0x3823c000, 0x38240000, 0x38244000, 0x38248000, 0x3824c000, +0x38250000, 0x38254000, 0x38258000, 0x3825c000, 0x38260000, 0x38264000, +0x38268000, 0x3826c000, 0x38270000, 0x38274000, 0x38278000, 0x3827c000, +0x38280000, 0x38284000, 0x38288000, 0x3828c000, 0x38290000, 0x38294000, +0x38298000, 0x3829c000, 0x382a0000, 0x382a4000, 0x382a8000, 0x382ac000, +0x382b0000, 0x382b4000, 0x382b8000, 0x382bc000, 0x382c0000, 0x382c4000, +0x382c8000, 0x382cc000, 0x382d0000, 0x382d4000, 0x382d8000, 0x382dc000, +0x382e0000, 0x382e4000, 0x382e8000, 0x382ec000, 0x382f0000, 0x382f4000, +0x382f8000, 0x382fc000, 0x38300000, 0x38304000, 0x38308000, 0x3830c000, +0x38310000, 0x38314000, 0x38318000, 0x3831c000, 0x38320000, 0x38324000, +0x38328000, 0x3832c000, 0x38330000, 0x38334000, 0x38338000, 0x3833c000, +0x38340000, 0x38344000, 0x38348000, 0x3834c000, 0x38350000, 0x38354000, +0x38358000, 0x3835c000, 0x38360000, 0x38364000, 0x38368000, 0x3836c000, +0x38370000, 0x38374000, 0x38378000, 0x3837c000, 0x38380000, 0x38384000, +0x38388000, 0x3838c000, 0x38390000, 0x38394000, 0x38398000, 0x3839c000, +0x383a0000, 0x383a4000, 0x383a8000, 0x383ac000, 0x383b0000, 0x383b4000, +0x383b8000, 0x383bc000, 0x383c0000, 0x383c4000, 0x383c8000, 0x383cc000, +0x383d0000, 0x383d4000, 0x383d8000, 0x383dc000, 0x383e0000, 0x383e4000, +0x383e8000, 0x383ec000, 0x383f0000, 0x383f4000, 0x383f8000, 0x383fc000, +0x38400000, 0x38404000, 0x38408000, 0x3840c000, 0x38410000, 0x38414000, +0x38418000, 0x3841c000, 0x38420000, 0x38424000, 0x38428000, 0x3842c000, +0x38430000, 0x38434000, 0x38438000, 0x3843c000, 0x38440000, 0x38444000, +0x38448000, 0x3844c000, 0x38450000, 0x38454000, 0x38458000, 0x3845c000, +0x38460000, 0x38464000, 0x38468000, 0x3846c000, 0x38470000, 0x38474000, +0x38478000, 0x3847c000, 0x38480000, 0x38484000, 0x38488000, 0x3848c000, +0x38490000, 0x38494000, 0x38498000, 0x3849c000, 0x384a0000, 0x384a4000, +0x384a8000, 0x384ac000, 0x384b0000, 0x384b4000, 0x384b8000, 0x384bc000, +0x384c0000, 0x384c4000, 0x384c8000, 0x384cc000, 0x384d0000, 0x384d4000, +0x384d8000, 0x384dc000, 0x384e0000, 0x384e4000, 0x384e8000, 0x384ec000, +0x384f0000, 0x384f4000, 0x384f8000, 0x384fc000, 0x38500000, 0x38504000, +0x38508000, 0x3850c000, 0x38510000, 0x38514000, 0x38518000, 0x3851c000, +0x38520000, 0x38524000, 0x38528000, 0x3852c000, 0x38530000, 0x38534000, +0x38538000, 0x3853c000, 0x38540000, 0x38544000, 0x38548000, 0x3854c000, +0x38550000, 0x38554000, 0x38558000, 0x3855c000, 0x38560000, 0x38564000, +0x38568000, 0x3856c000, 0x38570000, 0x38574000, 0x38578000, 0x3857c000, +0x38580000, 0x38584000, 0x38588000, 0x3858c000, 0x38590000, 0x38594000, +0x38598000, 0x3859c000, 0x385a0000, 0x385a4000, 0x385a8000, 0x385ac000, +0x385b0000, 0x385b4000, 0x385b8000, 0x385bc000, 0x385c0000, 0x385c4000, +0x385c8000, 0x385cc000, 0x385d0000, 0x385d4000, 0x385d8000, 0x385dc000, +0x385e0000, 0x385e4000, 0x385e8000, 0x385ec000, 0x385f0000, 0x385f4000, +0x385f8000, 0x385fc000, 0x38600000, 0x38604000, 0x38608000, 0x3860c000, +0x38610000, 0x38614000, 0x38618000, 0x3861c000, 0x38620000, 0x38624000, +0x38628000, 0x3862c000, 0x38630000, 0x38634000, 0x38638000, 0x3863c000, +0x38640000, 0x38644000, 0x38648000, 0x3864c000, 0x38650000, 0x38654000, +0x38658000, 0x3865c000, 0x38660000, 0x38664000, 0x38668000, 0x3866c000, +0x38670000, 0x38674000, 0x38678000, 0x3867c000, 0x38680000, 0x38684000, +0x38688000, 0x3868c000, 0x38690000, 0x38694000, 0x38698000, 0x3869c000, +0x386a0000, 0x386a4000, 0x386a8000, 0x386ac000, 0x386b0000, 0x386b4000, +0x386b8000, 0x386bc000, 0x386c0000, 0x386c4000, 0x386c8000, 0x386cc000, +0x386d0000, 0x386d4000, 0x386d8000, 0x386dc000, 0x386e0000, 0x386e4000, +0x386e8000, 0x386ec000, 0x386f0000, 0x386f4000, 0x386f8000, 0x386fc000, +0x38700000, 0x38704000, 0x38708000, 0x3870c000, 0x38710000, 0x38714000, +0x38718000, 0x3871c000, 0x38720000, 0x38724000, 0x38728000, 0x3872c000, +0x38730000, 0x38734000, 0x38738000, 0x3873c000, 0x38740000, 0x38744000, +0x38748000, 0x3874c000, 0x38750000, 0x38754000, 0x38758000, 0x3875c000, +0x38760000, 0x38764000, 0x38768000, 0x3876c000, 0x38770000, 0x38774000, +0x38778000, 0x3877c000, 0x38780000, 0x38784000, 0x38788000, 0x3878c000, +0x38790000, 0x38794000, 0x38798000, 0x3879c000, 0x387a0000, 0x387a4000, +0x387a8000, 0x387ac000, 0x387b0000, 0x387b4000, 0x387b8000, 0x387bc000, +0x387c0000, 0x387c4000, 0x387c8000, 0x387cc000, 0x387d0000, 0x387d4000, +0x387d8000, 0x387dc000, 0x387e0000, 0x387e4000, 0x387e8000, 0x387ec000, +0x387f0000, 0x387f4000, 0x387f8000, 0x387fc000, 0x38000000, 0x38002000, +0x38004000, 0x38006000, 0x38008000, 0x3800a000, 0x3800c000, 0x3800e000, +0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801a000, +0x3801c000, 0x3801e000, 0x38020000, 0x38022000, 0x38024000, 0x38026000, +0x38028000, 0x3802a000, 0x3802c000, 0x3802e000, 0x38030000, 0x38032000, +0x38034000, 0x38036000, 0x38038000, 0x3803a000, 0x3803c000, 0x3803e000, +0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804a000, +0x3804c000, 0x3804e000, 0x38050000, 0x38052000, 0x38054000, 0x38056000, +0x38058000, 0x3805a000, 0x3805c000, 0x3805e000, 0x38060000, 0x38062000, +0x38064000, 0x38066000, 0x38068000, 0x3806a000, 0x3806c000, 0x3806e000, +0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807a000, +0x3807c000, 0x3807e000, 0x38080000, 0x38082000, 0x38084000, 0x38086000, +0x38088000, 0x3808a000, 0x3808c000, 0x3808e000, 0x38090000, 0x38092000, +0x38094000, 0x38096000, 0x38098000, 0x3809a000, 0x3809c000, 0x3809e000, +0x380a0000, 0x380a2000, 0x380a4000, 0x380a6000, 0x380a8000, 0x380aa000, +0x380ac000, 0x380ae000, 0x380b0000, 0x380b2000, 0x380b4000, 0x380b6000, +0x380b8000, 0x380ba000, 0x380bc000, 0x380be000, 0x380c0000, 0x380c2000, +0x380c4000, 0x380c6000, 0x380c8000, 0x380ca000, 0x380cc000, 0x380ce000, +0x380d0000, 0x380d2000, 0x380d4000, 0x380d6000, 0x380d8000, 0x380da000, +0x380dc000, 0x380de000, 0x380e0000, 0x380e2000, 0x380e4000, 0x380e6000, +0x380e8000, 0x380ea000, 0x380ec000, 0x380ee000, 0x380f0000, 0x380f2000, +0x380f4000, 0x380f6000, 0x380f8000, 0x380fa000, 0x380fc000, 0x380fe000, +0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810a000, +0x3810c000, 0x3810e000, 0x38110000, 0x38112000, 0x38114000, 0x38116000, +0x38118000, 0x3811a000, 0x3811c000, 0x3811e000, 0x38120000, 0x38122000, +0x38124000, 0x38126000, 0x38128000, 0x3812a000, 0x3812c000, 0x3812e000, +0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813a000, +0x3813c000, 0x3813e000, 0x38140000, 0x38142000, 0x38144000, 0x38146000, +0x38148000, 0x3814a000, 0x3814c000, 0x3814e000, 0x38150000, 0x38152000, +0x38154000, 0x38156000, 0x38158000, 0x3815a000, 0x3815c000, 0x3815e000, +0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816a000, +0x3816c000, 0x3816e000, 0x38170000, 0x38172000, 0x38174000, 0x38176000, +0x38178000, 0x3817a000, 0x3817c000, 0x3817e000, 0x38180000, 0x38182000, +0x38184000, 0x38186000, 0x38188000, 0x3818a000, 0x3818c000, 0x3818e000, +0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819a000, +0x3819c000, 0x3819e000, 0x381a0000, 0x381a2000, 0x381a4000, 0x381a6000, +0x381a8000, 0x381aa000, 0x381ac000, 0x381ae000, 0x381b0000, 0x381b2000, +0x381b4000, 0x381b6000, 0x381b8000, 0x381ba000, 0x381bc000, 0x381be000, +0x381c0000, 0x381c2000, 0x381c4000, 0x381c6000, 0x381c8000, 0x381ca000, +0x381cc000, 0x381ce000, 0x381d0000, 0x381d2000, 0x381d4000, 0x381d6000, +0x381d8000, 0x381da000, 0x381dc000, 0x381de000, 0x381e0000, 0x381e2000, +0x381e4000, 0x381e6000, 0x381e8000, 0x381ea000, 0x381ec000, 0x381ee000, +0x381f0000, 0x381f2000, 0x381f4000, 0x381f6000, 0x381f8000, 0x381fa000, +0x381fc000, 0x381fe000, 0x38200000, 0x38202000, 0x38204000, 0x38206000, +0x38208000, 0x3820a000, 0x3820c000, 0x3820e000, 0x38210000, 0x38212000, +0x38214000, 0x38216000, 0x38218000, 0x3821a000, 0x3821c000, 0x3821e000, +0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822a000, +0x3822c000, 0x3822e000, 0x38230000, 0x38232000, 0x38234000, 0x38236000, +0x38238000, 0x3823a000, 0x3823c000, 0x3823e000, 0x38240000, 0x38242000, +0x38244000, 0x38246000, 0x38248000, 0x3824a000, 0x3824c000, 0x3824e000, +0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825a000, +0x3825c000, 0x3825e000, 0x38260000, 0x38262000, 0x38264000, 0x38266000, +0x38268000, 0x3826a000, 0x3826c000, 0x3826e000, 0x38270000, 0x38272000, +0x38274000, 0x38276000, 0x38278000, 0x3827a000, 0x3827c000, 0x3827e000, +0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828a000, +0x3828c000, 0x3828e000, 0x38290000, 0x38292000, 0x38294000, 0x38296000, +0x38298000, 0x3829a000, 0x3829c000, 0x3829e000, 0x382a0000, 0x382a2000, +0x382a4000, 0x382a6000, 0x382a8000, 0x382aa000, 0x382ac000, 0x382ae000, +0x382b0000, 0x382b2000, 0x382b4000, 0x382b6000, 0x382b8000, 0x382ba000, +0x382bc000, 0x382be000, 0x382c0000, 0x382c2000, 0x382c4000, 0x382c6000, +0x382c8000, 0x382ca000, 0x382cc000, 0x382ce000, 0x382d0000, 0x382d2000, +0x382d4000, 0x382d6000, 0x382d8000, 0x382da000, 0x382dc000, 0x382de000, +0x382e0000, 0x382e2000, 0x382e4000, 0x382e6000, 0x382e8000, 0x382ea000, +0x382ec000, 0x382ee000, 0x382f0000, 0x382f2000, 0x382f4000, 0x382f6000, +0x382f8000, 0x382fa000, 0x382fc000, 0x382fe000, 0x38300000, 0x38302000, +0x38304000, 0x38306000, 0x38308000, 0x3830a000, 0x3830c000, 0x3830e000, +0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831a000, +0x3831c000, 0x3831e000, 0x38320000, 0x38322000, 0x38324000, 0x38326000, +0x38328000, 0x3832a000, 0x3832c000, 0x3832e000, 0x38330000, 0x38332000, +0x38334000, 0x38336000, 0x38338000, 0x3833a000, 0x3833c000, 0x3833e000, +0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834a000, +0x3834c000, 0x3834e000, 0x38350000, 0x38352000, 0x38354000, 0x38356000, +0x38358000, 0x3835a000, 0x3835c000, 0x3835e000, 0x38360000, 0x38362000, +0x38364000, 0x38366000, 0x38368000, 0x3836a000, 0x3836c000, 0x3836e000, +0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837a000, +0x3837c000, 0x3837e000, 0x38380000, 0x38382000, 0x38384000, 0x38386000, +0x38388000, 0x3838a000, 0x3838c000, 0x3838e000, 0x38390000, 0x38392000, +0x38394000, 0x38396000, 0x38398000, 0x3839a000, 0x3839c000, 0x3839e000, +0x383a0000, 0x383a2000, 0x383a4000, 0x383a6000, 0x383a8000, 0x383aa000, +0x383ac000, 0x383ae000, 0x383b0000, 0x383b2000, 0x383b4000, 0x383b6000, +0x383b8000, 0x383ba000, 0x383bc000, 0x383be000, 0x383c0000, 0x383c2000, +0x383c4000, 0x383c6000, 0x383c8000, 0x383ca000, 0x383cc000, 0x383ce000, +0x383d0000, 0x383d2000, 0x383d4000, 0x383d6000, 0x383d8000, 0x383da000, +0x383dc000, 0x383de000, 0x383e0000, 0x383e2000, 0x383e4000, 0x383e6000, +0x383e8000, 0x383ea000, 0x383ec000, 0x383ee000, 0x383f0000, 0x383f2000, +0x383f4000, 0x383f6000, 0x383f8000, 0x383fa000, 0x383fc000, 0x383fe000, +0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840a000, +0x3840c000, 0x3840e000, 0x38410000, 0x38412000, 0x38414000, 0x38416000, +0x38418000, 0x3841a000, 0x3841c000, 0x3841e000, 0x38420000, 0x38422000, +0x38424000, 0x38426000, 0x38428000, 0x3842a000, 0x3842c000, 0x3842e000, +0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843a000, +0x3843c000, 0x3843e000, 0x38440000, 0x38442000, 0x38444000, 0x38446000, +0x38448000, 0x3844a000, 0x3844c000, 0x3844e000, 0x38450000, 0x38452000, +0x38454000, 0x38456000, 0x38458000, 0x3845a000, 0x3845c000, 0x3845e000, +0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846a000, +0x3846c000, 0x3846e000, 0x38470000, 0x38472000, 0x38474000, 0x38476000, +0x38478000, 0x3847a000, 0x3847c000, 0x3847e000, 0x38480000, 0x38482000, +0x38484000, 0x38486000, 0x38488000, 0x3848a000, 0x3848c000, 0x3848e000, +0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849a000, +0x3849c000, 0x3849e000, 0x384a0000, 0x384a2000, 0x384a4000, 0x384a6000, +0x384a8000, 0x384aa000, 0x384ac000, 0x384ae000, 0x384b0000, 0x384b2000, +0x384b4000, 0x384b6000, 0x384b8000, 0x384ba000, 0x384bc000, 0x384be000, +0x384c0000, 0x384c2000, 0x384c4000, 0x384c6000, 0x384c8000, 0x384ca000, +0x384cc000, 0x384ce000, 0x384d0000, 0x384d2000, 0x384d4000, 0x384d6000, +0x384d8000, 0x384da000, 0x384dc000, 0x384de000, 0x384e0000, 0x384e2000, +0x384e4000, 0x384e6000, 0x384e8000, 0x384ea000, 0x384ec000, 0x384ee000, +0x384f0000, 0x384f2000, 0x384f4000, 0x384f6000, 0x384f8000, 0x384fa000, +0x384fc000, 0x384fe000, 0x38500000, 0x38502000, 0x38504000, 0x38506000, +0x38508000, 0x3850a000, 0x3850c000, 0x3850e000, 0x38510000, 0x38512000, +0x38514000, 0x38516000, 0x38518000, 0x3851a000, 0x3851c000, 0x3851e000, +0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852a000, +0x3852c000, 0x3852e000, 0x38530000, 0x38532000, 0x38534000, 0x38536000, +0x38538000, 0x3853a000, 0x3853c000, 0x3853e000, 0x38540000, 0x38542000, +0x38544000, 0x38546000, 0x38548000, 0x3854a000, 0x3854c000, 0x3854e000, +0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855a000, +0x3855c000, 0x3855e000, 0x38560000, 0x38562000, 0x38564000, 0x38566000, +0x38568000, 0x3856a000, 0x3856c000, 0x3856e000, 0x38570000, 0x38572000, +0x38574000, 0x38576000, 0x38578000, 0x3857a000, 0x3857c000, 0x3857e000, +0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858a000, +0x3858c000, 0x3858e000, 0x38590000, 0x38592000, 0x38594000, 0x38596000, +0x38598000, 0x3859a000, 0x3859c000, 0x3859e000, 0x385a0000, 0x385a2000, +0x385a4000, 0x385a6000, 0x385a8000, 0x385aa000, 0x385ac000, 0x385ae000, +0x385b0000, 0x385b2000, 0x385b4000, 0x385b6000, 0x385b8000, 0x385ba000, +0x385bc000, 0x385be000, 0x385c0000, 0x385c2000, 0x385c4000, 0x385c6000, +0x385c8000, 0x385ca000, 0x385cc000, 0x385ce000, 0x385d0000, 0x385d2000, +0x385d4000, 0x385d6000, 0x385d8000, 0x385da000, 0x385dc000, 0x385de000, +0x385e0000, 0x385e2000, 0x385e4000, 0x385e6000, 0x385e8000, 0x385ea000, +0x385ec000, 0x385ee000, 0x385f0000, 0x385f2000, 0x385f4000, 0x385f6000, +0x385f8000, 0x385fa000, 0x385fc000, 0x385fe000, 0x38600000, 0x38602000, +0x38604000, 0x38606000, 0x38608000, 0x3860a000, 0x3860c000, 0x3860e000, +0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861a000, +0x3861c000, 0x3861e000, 0x38620000, 0x38622000, 0x38624000, 0x38626000, +0x38628000, 0x3862a000, 0x3862c000, 0x3862e000, 0x38630000, 0x38632000, +0x38634000, 0x38636000, 0x38638000, 0x3863a000, 0x3863c000, 0x3863e000, +0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864a000, +0x3864c000, 0x3864e000, 0x38650000, 0x38652000, 0x38654000, 0x38656000, +0x38658000, 0x3865a000, 0x3865c000, 0x3865e000, 0x38660000, 0x38662000, +0x38664000, 0x38666000, 0x38668000, 0x3866a000, 0x3866c000, 0x3866e000, +0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867a000, +0x3867c000, 0x3867e000, 0x38680000, 0x38682000, 0x38684000, 0x38686000, +0x38688000, 0x3868a000, 0x3868c000, 0x3868e000, 0x38690000, 0x38692000, +0x38694000, 0x38696000, 0x38698000, 0x3869a000, 0x3869c000, 0x3869e000, +0x386a0000, 0x386a2000, 0x386a4000, 0x386a6000, 0x386a8000, 0x386aa000, +0x386ac000, 0x386ae000, 0x386b0000, 0x386b2000, 0x386b4000, 0x386b6000, +0x386b8000, 0x386ba000, 0x386bc000, 0x386be000, 0x386c0000, 0x386c2000, +0x386c4000, 0x386c6000, 0x386c8000, 0x386ca000, 0x386cc000, 0x386ce000, +0x386d0000, 0x386d2000, 0x386d4000, 0x386d6000, 0x386d8000, 0x386da000, +0x386dc000, 0x386de000, 0x386e0000, 0x386e2000, 0x386e4000, 0x386e6000, +0x386e8000, 0x386ea000, 0x386ec000, 0x386ee000, 0x386f0000, 0x386f2000, +0x386f4000, 0x386f6000, 0x386f8000, 0x386fa000, 0x386fc000, 0x386fe000, +0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870a000, +0x3870c000, 0x3870e000, 0x38710000, 0x38712000, 0x38714000, 0x38716000, +0x38718000, 0x3871a000, 0x3871c000, 0x3871e000, 0x38720000, 0x38722000, +0x38724000, 0x38726000, 0x38728000, 0x3872a000, 0x3872c000, 0x3872e000, +0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873a000, +0x3873c000, 0x3873e000, 0x38740000, 0x38742000, 0x38744000, 0x38746000, +0x38748000, 0x3874a000, 0x3874c000, 0x3874e000, 0x38750000, 0x38752000, +0x38754000, 0x38756000, 0x38758000, 0x3875a000, 0x3875c000, 0x3875e000, +0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876a000, +0x3876c000, 0x3876e000, 0x38770000, 0x38772000, 0x38774000, 0x38776000, +0x38778000, 0x3877a000, 0x3877c000, 0x3877e000, 0x38780000, 0x38782000, +0x38784000, 0x38786000, 0x38788000, 0x3878a000, 0x3878c000, 0x3878e000, +0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879a000, +0x3879c000, 0x3879e000, 0x387a0000, 0x387a2000, 0x387a4000, 0x387a6000, +0x387a8000, 0x387aa000, 0x387ac000, 0x387ae000, 0x387b0000, 0x387b2000, +0x387b4000, 0x387b6000, 0x387b8000, 0x387ba000, 0x387bc000, 0x387be000, +0x387c0000, 0x387c2000, 0x387c4000, 0x387c6000, 0x387c8000, 0x387ca000, +0x387cc000, 0x387ce000, 0x387d0000, 0x387d2000, 0x387d4000, 0x387d6000, +0x387d8000, 0x387da000, 0x387dc000, 0x387de000, 0x387e0000, 0x387e2000, +0x387e4000, 0x387e6000, 0x387e8000, 0x387ea000, 0x387ec000, 0x387ee000, +0x387f0000, 0x387f2000, 0x387f4000, 0x387f6000, 0x387f8000, 0x387fa000, +0x387fc000, 0x387fe000 +}; + +static cmsUInt16Number Offset[64] = { +0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0000, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400 +}; + +static cmsUInt32Number Exponent[64] = { +0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000, +0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000, +0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000, +0x09000000, 0x09800000, 0x0a000000, 0x0a800000, 0x0b000000, 0x0b800000, +0x0c000000, 0x0c800000, 0x0d000000, 0x0d800000, 0x0e000000, 0x0e800000, +0x0f000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000, +0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000, +0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000, +0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8a000000, 0x8a800000, +0x8b000000, 0x8b800000, 0x8c000000, 0x8c800000, 0x8d000000, 0x8d800000, +0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000 +}; + +static cmsUInt16Number Base[512] = { +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, +0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, +0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, +0x4800, 0x4c00, 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, +0x7000, 0x7400, 0x7800, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, +0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, +0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, +0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, +0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00 +}; + +static cmsUInt8Number Shift[512] = { +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, +0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, +0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x0d +}; + +cmsFloat32Number CMSEXPORT _cmsHalf2Float(cmsUInt16Number h) +{ + union { + cmsFloat32Number flt; + cmsUInt32Number num; + } out; + + int n = h >> 10; + + out.num = Mantissa[ (h & 0x3ff) + Offset[ n ] ] + Exponent[ n ]; + return out.flt; +} + +cmsUInt16Number CMSEXPORT _cmsFloat2Half(cmsFloat32Number flt) +{ + union { + cmsFloat32Number flt; + cmsUInt32Number num; + } in; + + cmsUInt32Number n, j; + + in.flt = flt; + n = in.num; + j = (n >> 23) & 0x1ff; + + return (cmsUInt16Number) ((cmsUInt32Number) Base[ j ] + (( n & 0x007fffff) >> Shift[ j ])); +} + +#endif diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsintrp.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsintrp.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsintrp.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,667 +49,422 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// -// Interpolation +#include "lcms2_internal.h" -#include "lcms.h" +// This module incorporates several interpolation routines, for 1 to 8 channels on input and +// up to 65535 channels on output. The user may change those by using the interpolation plug-in -void cmsCalcL16Params(int nSamples, LPL16PARAMS p) -{ - p -> nSamples = nSamples; - p -> Domain = (WORD) (nSamples - 1); - p -> nInputs = p -> nOutputs = 1; +// Some people may want to compile as C++ with all warnings on, in this case make compiler silent +#ifdef _MSC_VER +# if (_MSC_VER >= 1400) +# pragma warning( disable : 4365 ) +# endif +#endif -} - - - -// Eval gray LUT having only one input channel +// Interpolation routines by default +static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); -static -void Eval1Input(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16) +// This is the default factory +_cmsInterpPluginChunkType _cmsInterpPluginChunk = { NULL }; + +// The interpolation plug-in memory chunk allocator/dup +void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src) { - Fixed32 fk; - Fixed32 k0, k1, rk, K0, K1; - int OutChan; + void* from; - fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain); - k0 = FIXED_TO_INT(fk); - rk = (WORD) FIXED_REST_TO_INT(fk); + _cmsAssert(ctx != NULL); - k1 = k0 + (StageABC[0] != 0xFFFFU ? 1 : 0); - - K0 = p16 -> opta1 * k0; - K1 = p16 -> opta1 * k1; + if (src != NULL) { + from = src ->chunks[InterpPlugin]; + } + else { + static _cmsInterpPluginChunkType InterpPluginChunk = { NULL }; - for (OutChan=0; OutChan < p16->nOutputs; OutChan++) { + from = &InterpPluginChunk; + } - StageLMN[OutChan] = (WORD) FixedLERP(rk, LutTable[K0+OutChan], - LutTable[K1+OutChan]); - } + _cmsAssert(from != NULL); + ctx ->chunks[InterpPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsInterpPluginChunkType)); } - -// For more that 3 inputs (i.e., CMYK) -// evaluate two 3-dimensional interpolations and then linearly interpolate between them. -static -void Eval4Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16) +// Main plug-in entry +cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Data) { - Fixed32 fk; - Fixed32 k0, rk; - int K0, K1; - LPWORD T; - int i; - WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS]; + cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data; + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); - - fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); + if (Data == NULL) { - K0 = p16 -> opta4 * k0; - K1 = p16 -> opta4 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0)); - - p16 -> nInputs = 3; - - T = LutTable + K0; - - cmsTetrahedralInterp16(StageABC + 1, Tmp1, T, p16); - + ptr ->Interpolators = NULL; + return TRUE; + } - T = LutTable + K1; - - cmsTetrahedralInterp16(StageABC + 1, Tmp2, T, p16); - - - p16 -> nInputs = 4; - for (i=0; i < p16 -> nOutputs; i++) - { - StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]); - - } - + // Set replacement functions + ptr ->Interpolators = Plugin ->InterpolatorsFactory; + return TRUE; } -static -void Eval5Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16) +// Set the interpolation method +cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p) { - Fixed32 fk; - Fixed32 k0, rk; - int K0, K1; - LPWORD T; - int i; - WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS]; + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); + + p ->Interpolation.Lerp16 = NULL; - - fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); - - K0 = p16 -> opta5 * k0; - K1 = p16 -> opta5 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0)); + // Invoke factory, possibly in the Plug-in + if (ptr ->Interpolators != NULL) + p ->Interpolation = ptr->Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags); - p16 -> nInputs = 4; - - T = LutTable + K0; - - Eval4Inputs(StageABC + 1, Tmp1, T, p16); - - T = LutTable + K1; + // If unsupported by the plug-in, go for the LittleCMS default. + // If happens only if an extern plug-in is being used + if (p ->Interpolation.Lerp16 == NULL) + p ->Interpolation = DefaultInterpolatorsFactory(p ->nInputs, p ->nOutputs, p ->dwFlags); - Eval4Inputs(StageABC + 1, Tmp2, T, p16); + // Check for valid interpolator (we just check one member of the union) + if (p ->Interpolation.Lerp16 == NULL) { + return FALSE; + } - p16 -> nInputs = 5; - for (i=0; i < p16 -> nOutputs; i++) - { - StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]); - - } - + return TRUE; } -static -void Eval6Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16) +// This function precalculates as many parameters as possible to speed up the interpolation. +cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, + const cmsUInt32Number nSamples[], + cmsUInt32Number InputChan, cmsUInt32Number OutputChan, + const void *Table, + cmsUInt32Number dwFlags) { - Fixed32 fk; - Fixed32 k0, rk; - int K0, K1; - LPWORD T; - int i; - WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS]; - + cmsInterpParams* p; + cmsUInt32Number i; - fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); - - K0 = p16 -> opta6 * k0; - K1 = p16 -> opta6 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0)); - - p16 -> nInputs = 5; - - T = LutTable + K0; + // Check for maximum inputs + if (InputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", InputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } - Eval5Inputs(StageABC + 1, Tmp1, T, p16); - - T = LutTable + K1; - - Eval5Inputs(StageABC + 1, Tmp2, T, p16); + // Creates an empty object + p = (cmsInterpParams*) _cmsMallocZero(ContextID, sizeof(cmsInterpParams)); + if (p == NULL) return NULL; - p16 -> nInputs = 6; - for (i=0; i < p16 -> nOutputs; i++) - { - StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]); - } + // Keep original parameters + p -> dwFlags = dwFlags; + p -> nInputs = InputChan; + p -> nOutputs = OutputChan; + p ->Table = Table; + p ->ContextID = ContextID; -} + // Fill samples per input direction and domain (which is number of nodes minus one) + for (i=0; i < InputChan; i++) { -static -void Eval7Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16) -{ - Fixed32 fk; - Fixed32 k0, rk; - int K0, K1; - LPWORD T; - int i; - WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS]; + p -> nSamples[i] = nSamples[i]; + p -> Domain[i] = nSamples[i] - 1; + } + + // Compute factors to apply to each component to index the grid array + p -> opta[0] = p -> nOutputs; + for (i=1; i < InputChan; i++) + p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i]; - fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); - - K0 = p16 -> opta7 * k0; - K1 = p16 -> opta7 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0)); - - p16 -> nInputs = 6; - - T = LutTable + K0; + if (!_cmsSetInterpolationRoutine(ContextID, p)) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan); + _cmsFree(ContextID, p); + return NULL; + } - Eval6Inputs(StageABC + 1, Tmp1, T, p16); - - T = LutTable + K1; - - Eval6Inputs(StageABC + 1, Tmp2, T, p16); - - p16 -> nInputs = 7; - for (i=0; i < p16 -> nOutputs; i++) - { - StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]); - } - + // All seems ok + return p; } -static -void Eval8Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16) -{ - Fixed32 fk; - Fixed32 k0, rk; - int K0, K1; - LPWORD T; - int i; - WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS]; - - - fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); - K0 = p16 -> opta8 * k0; - K1 = p16 -> opta8 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0)); - - p16 -> nInputs = 7; - - T = LutTable + K0; - - Eval7Inputs(StageABC + 1, Tmp1, T, p16); +// This one is a wrapper on the anterior, but assuming all directions have same number of nodes +cmsInterpParams* CMSEXPORT _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, + cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags) +{ + int i; + cmsUInt32Number Samples[MAX_INPUT_DIMENSIONS]; - T = LutTable + K1; - - Eval7Inputs(StageABC + 1, Tmp2, T, p16); + // Fill the auxiliary array + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Samples[i] = nSamples; - p16 -> nInputs = 8; - for (i=0; i < p16 -> nOutputs; i++) - { - StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]); - } - + // Call the extended function + return _cmsComputeInterpParamsEx(ContextID, Samples, InputChan, OutputChan, Table, dwFlags); } -// Fills optimization parameters - -void cmsCalcCLUT16ParamsEx(int nSamples, int InputChan, int OutputChan, - LCMSBOOL lUseTetrahedral, LPL16PARAMS p) +// Free all associated memory +void CMSEXPORT _cmsFreeInterpParams(cmsInterpParams* p) { - int clutPoints; - - cmsCalcL16Params(nSamples, p); - - p -> nInputs = InputChan; - p -> nOutputs = OutputChan; - - clutPoints = p -> Domain + 1; - - p -> opta1 = p -> nOutputs; // Z - p -> opta2 = p -> opta1 * clutPoints; // Y - p -> opta3 = p -> opta2 * clutPoints; // X - p -> opta4 = p -> opta3 * clutPoints; // Used only in 4 inputs LUT - p -> opta5 = p -> opta4 * clutPoints; // Used only in 5 inputs LUT - p -> opta6 = p -> opta5 * clutPoints; // Used only on 6 inputs LUT - p -> opta7 = p -> opta6 * clutPoints; // Used only on 7 inputs LUT - p -> opta8 = p -> opta7 * clutPoints; // Used only on 8 inputs LUT - - - switch (InputChan) { + if (p != NULL) _cmsFree(p ->ContextID, p); +} - case 1: // Gray LUT - - p ->Interp3D = Eval1Input; - break; - - case 3: // RGB et al - if (lUseTetrahedral) { - p ->Interp3D = cmsTetrahedralInterp16; - } - else - p ->Interp3D = cmsTrilinearInterp16; - break; - - case 4: // CMYK LUT - p ->Interp3D = Eval4Inputs; - break; - - case 5: // 5 Inks - p ->Interp3D = Eval5Inputs; - break; - - case 6: // 6 Inks - p -> Interp3D = Eval6Inputs; - break; - - case 7: // 7 inks - p ->Interp3D = Eval7Inputs; - break; - - case 8: // 8 inks - p ->Interp3D = Eval8Inputs; - break; - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported restoration (%d channels)", InputChan); - } - +// Inline fixed point interpolation +cmsINLINE cmsUInt16Number LinearInterp(cmsS15Fixed16Number a, cmsS15Fixed16Number l, cmsS15Fixed16Number h) +{ + cmsUInt32Number dif = (cmsUInt32Number) (h - l) * a + 0x8000; + dif = (dif >> 16) + l; + return (cmsUInt16Number) (dif); } -void cmsCalcCLUT16Params(int nSamples, int InputChan, int OutputChan, LPL16PARAMS p) +// Linear interpolation (Fixed-point optimized) +static +void LinLerp1D(register const cmsUInt16Number Value[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p) { - cmsCalcCLUT16ParamsEx(nSamples, InputChan, OutputChan, FALSE, p); -} + cmsUInt16Number y1, y0; + int cell0, rest; + int val3; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + + // if last value... + if (Value[0] == 0xffff) { - + Output[0] = LutTable[p -> Domain[0]]; + return; + } -#ifdef USE_FLOAT + val3 = p -> Domain[0] * Value[0]; + val3 = _cmsToFixedDomain(val3); // To fixed 15.16 + + cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits + rest = FIXED_REST_TO_INT(val3); // Rest is 16 LSB bits + + y0 = LutTable[cell0]; + y1 = LutTable[cell0+1]; -// Floating-point version + Output[0] = LinearInterp(rest, y0, y1); +} -WORD cmsLinearInterpLUT16(WORD Value, WORD LutTable[], LPL16PARAMS p) +// To prevent out of bounds indexing +cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) { - double y1, y0; - double y; - double val2, rest; + return ((v < 1.0e-9f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v); +} + +// Floating-point version of 1D interpolation +static +void LinLerp1Dfloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number y1, y0; + cmsFloat32Number val2, rest; int cell0, cell1; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + + val2 = fclamp(Value[0]); // if last value... + if (val2 == 1.0) { + Output[0] = LutTable[p -> Domain[0]]; + return; + } - if (Value == 0xffff) return LutTable[p -> Domain]; - - val2 = p -> Domain * ((double) Value / 65535.0); + val2 *= p -> Domain[0]; cell0 = (int) floor(val2); cell1 = (int) ceil(val2); // Rest is 16 LSB bits - rest = val2 - cell0; y0 = LutTable[cell0] ; y1 = LutTable[cell1] ; - y = y0 + (y1 - y0) * rest; - - - return (WORD) floor(y+.5); -} - -#endif - - -// -// Linear interpolation (Fixed-point optimized, but C source) -// - - -#ifdef USE_C - -WORD cmsLinearInterpLUT16(WORD Value1, WORD LutTable[], LPL16PARAMS p) -{ - WORD y1, y0; - WORD y; - int dif, a1; - int cell0, rest; - int val3, Value; - - // if last value... - - - Value = Value1; - if (Value == 0xffff) return LutTable[p -> Domain]; - - val3 = p -> Domain * Value; - val3 = ToFixedDomain(val3); // To fixed 15.16 - - cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits - rest = FIXED_REST_TO_INT(val3); // Rest is 16 LSB bits - - y0 = LutTable[cell0] ; - y1 = LutTable[cell0+1] ; - - dif = (int) y1 - y0; // dif is in domain -ffff ... ffff - - if (dif >= 0) - { - a1 = ToFixedDomain(dif * rest); - a1 += 0x8000; - } - else - { - a1 = ToFixedDomain((- dif) * rest); - a1 -= 0x8000; - a1 = -a1; - } - - y = (WORD) (y0 + FIXED_TO_INT(a1)); - - return y; -} - -#endif - -// Linear interpolation (asm by hand optimized) - -#ifdef USE_ASSEMBLER - -#ifdef _MSC_VER -#pragma warning(disable : 4033) -#pragma warning(disable : 4035) -#endif - -WORD cmsLinearInterpLUT16(WORD Value, WORD LutTable[], LPL16PARAMS p) -{ - int xDomain = p -> Domain; - - - if (Value == 0xffff) return LutTable[p -> Domain]; - else - ASM { - xor eax, eax - mov ax, word ptr ss:Value - mov edx, ss:xDomain - mul edx // val3 = p -> Domain * Value; - shld edx, eax, 16 // Convert it to fixed 15.16 - shl eax, 16 // * 65536 / 65535 - mov ebx, 0x0000ffff - div ebx - mov ecx, eax - sar ecx, 16 // ecx = cell0 - mov edx, eax // rest = (val2 & 0xFFFFU) - and edx, 0x0000ffff // edx = rest - mov ebx, ss:LutTable - lea eax, dword ptr [ebx+2*ecx] // Ptr to LUT - xor ebx, ebx - mov bx, word ptr [eax] // EBX = y0 - movzx eax, word ptr [eax+2] // EAX = y1 - sub eax, ebx // EAX = y1-y0 - js IsNegative - mul edx // EAX = EAX * rest - shld edx, eax, 16 // Pass it to fixed - sal eax, 16 // * 65536 / 65535 - mov ecx, 0x0000ffff - div ecx - add eax, 0x8000 // Rounding - sar eax, 16 - add eax, ebx // Done! - } - - RET((WORD) _EAX); - - IsNegative: - - ASM { - neg eax - mul edx // EAX = EAX * rest - shld edx, eax, 16 // Pass it to fixed - sal eax, 16 // * 65536 / 65535 - mov ecx, 0x0000ffff - div ecx - sub eax, 0x8000 - neg eax - sar eax, 16 - add eax, ebx // Done! - } - - RET((WORD) _EAX); -} - -#ifdef _MSC_VER -#pragma warning(default : 4033) -#pragma warning(default : 4035) -#endif - -#endif - -Fixed32 cmsLinearInterpFixed(WORD Value1, WORD LutTable[], LPL16PARAMS p) -{ - Fixed32 y1, y0; - int cell0; - int val3, Value; - - // if last value... - - - Value = Value1; - if (Value == 0xffffU) return LutTable[p -> Domain]; - - val3 = p -> Domain * Value; - val3 = ToFixedDomain(val3); // To fixed 15.16 - - cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits - - y0 = LutTable[cell0] ; - y1 = LutTable[cell0+1] ; - - - return y0 + FixedMul((y1 - y0), (val3 & 0xFFFFL)); + Output[0] = y0 + (y1 - y0) * rest; } -// Reverse Lineal interpolation (16 bits) -// Im using a sort of binary search here, this is not a time-critical function -WORD cmsReverseLinearInterpLUT16(WORD Value, WORD LutTable[], LPL16PARAMS p) +// Eval gray LUT having only one input channel +static +void Eval1Input(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) { - register int l = 1; - register int r = 0x10000; - register int x = 0, res; // 'int' Give spacing for negative values - int NumZeroes, NumPoles; - int cell0, cell1; - double val2; - double y0, y1, x0, x1; - double a, b, f; - - // July/27 2001 - Expanded to handle degenerated curves with an arbitrary - // number of elements containing 0 at the begining of the table (Zeroes) - // and another arbitrary number of poles (FFFFh) at the end. - // First the zero and pole extents are computed, then value is compared. - - NumZeroes = 0; - while (LutTable[NumZeroes] == 0 && NumZeroes < p -> Domain) - NumZeroes++; - - // There are no zeros at the beginning and we are trying to find a zero, so - // return anything. It seems zero would be the less destructive choice - - if (NumZeroes == 0 && Value == 0) - return 0; - - NumPoles = 0; - while (LutTable[p -> Domain - NumPoles] == 0xFFFF && NumPoles < p -> Domain) - NumPoles++; - - // Does the curve belong to this case? - if (NumZeroes > 1 || NumPoles > 1) - { - int a, b; - - // Identify if value fall downto 0 or FFFF zone - if (Value == 0) return 0; - // if (Value == 0xFFFF) return 0xFFFF; - - // else restrict to valid zone - - a = ((NumZeroes-1) * 0xFFFF) / p->Domain; - b = ((p -> Domain - NumPoles) * 0xFFFF) / p ->Domain; - - l = a - 1; - r = b + 1; - } + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, k1, rk, K0, K1; + int v; + cmsUInt32Number OutChan; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; - - // Seems not a degenerated case... apply binary search - - while (r > l) { - - x = (l + r) / 2; - - res = (int) cmsLinearInterpLUT16((WORD) (x - 1), LutTable, p); - - if (res == Value) { + v = Input[0] * p16 -> Domain[0]; + fk = _cmsToFixedDomain(v); - // Found exact match. - - return (WORD) (x - 1); - } + k0 = FIXED_TO_INT(fk); + rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk); - if (res > Value) r = x - 1; - else l = x + 1; - } - - // Not found, should we interpolate? - - - // Get surrounding nodes + k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0); - val2 = p -> Domain * ((double) (x - 1) / 65535.0); - - cell0 = (int) floor(val2); - cell1 = (int) ceil(val2); - - if (cell0 == cell1) return (WORD) x; - - y0 = LutTable[cell0] ; - x0 = (65535.0 * cell0) / p ->Domain; - - y1 = LutTable[cell1] ; - x1 = (65535.0 * cell1) / p ->Domain; + K0 = p16 -> opta[0] * k0; + K1 = p16 -> opta[0] * k1; - a = (y1 - y0) / (x1 - x0); - b = y0 - a * x0; - - if (fabs(a) < 0.01) return (WORD) x; + for (OutChan=0; OutChan < p16->nOutputs; OutChan++) { - f = ((Value - b) / a); - - if (f < 0.0) return (WORD) 0; - if (f >= 65535.0) return (WORD) 0xFFFF; - - return (WORD) floor(f + 0.5); - + Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]); + } } +// Eval gray LUT having only one input channel +static +void Eval1InputFloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number y1, y0; + cmsFloat32Number val2, rest; + int cell0, cell1; + cmsUInt32Number OutChan; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; -// Trilinear interpolation (16 bits) - float version + val2 = fclamp(Value[0]); + + // if last value... + if (val2 == 1.0) { + Output[0] = LutTable[p -> Domain[0]]; + return; + } + + val2 *= p -> Domain[0]; -#ifdef USE_FLOAT -void cmsTrilinearInterp16(WORD Input[], WORD Output[], - WORD LutTable[], LPL16PARAMS p) + cell0 = (int) floor(val2); + cell1 = (int) ceil(val2); + + // Rest is 16 LSB bits + rest = val2 - cell0; + + cell0 *= p -> opta[0]; + cell1 *= p -> opta[0]; + + for (OutChan=0; OutChan < p->nOutputs; OutChan++) { + + y0 = LutTable[cell0 + OutChan] ; + y1 = LutTable[cell1 + OutChan] ; + + Output[OutChan] = y0 + (y1 - y0) * rest; + } +} + +// Bilinear interpolation (16 bits) - cmsFloat32Number version +static +void BilinearInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) { -# define LERP(a,l,h) (double) ((l)+(((h)-(l))*(a))) -# define DENS(X, Y, Z) (double) (LutTable[TotalOut*((Z)+clutPoints*((Y)+clutPoints*(X)))+OutChan]) +# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) +# define DENS(i,j) (LutTable[(i)+(j)+OutChan]) + + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + cmsFloat32Number px, py; + int x0, y0, + X0, Y0, X1, Y1; + int TotalOut, OutChan; + cmsFloat32Number fx, fy, + d00, d01, d10, d11, + dx0, dx1, + dxy; + + TotalOut = p -> nOutputs; + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + + x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; + y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; + X0 = p -> opta[1] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[1]); + + Y0 = p -> opta[0] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d00 = DENS(X0, Y0); + d01 = DENS(X0, Y1); + d10 = DENS(X1, Y0); + d11 = DENS(X1, Y1); + + dx0 = LERP(fx, d00, d10); + dx1 = LERP(fx, d01, d11); + + dxy = LERP(fy, dx0, dx1); + + Output[OutChan] = dxy; + } - double px, py, pz; - int x0, y0, z0, - x1, y1, z1; - int clutPoints, TotalOut, OutChan; - double fx, fy, fz, - d000, d001, d010, d011, - d100, d101, d110, d111, - dx00, dx01, dx10, dx11, - dxy0, dxy1, dxyz; +# undef LERP +# undef DENS +} + +// Bilinear interpolation (16 bits) - optimized version +static +void BilinearInterp16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p) +{ +#define DENS(i,j) (LutTable[(i)+(j)+OutChan]) +#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) - clutPoints = p -> Domain + 1; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + int OutChan, TotalOut; + cmsS15Fixed16Number fx, fy; + register int rx, ry; + int x0, y0; + register int X0, X1, Y0, Y1; + int d00, d01, d10, d11, + dx0, dx1, + dxy; + TotalOut = p -> nOutputs; - px = ((double) Input[0] * (p->Domain)) / 65535.0; - py = ((double) Input[1] * (p->Domain)) / 65535.0; - pz = ((double) Input[2] * (p->Domain)) / 65535.0; + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + x0 = FIXED_TO_INT(fx); + rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain - x0 = (int) _cmsQuickFloor(px); fx = px - (double) x0; - y0 = (int) _cmsQuickFloor(py); fy = py - (double) y0; - z0 = (int) _cmsQuickFloor(pz); fz = pz - (double) z0; - x1 = x0 + (Input[0] != 0xFFFFU ? 1 : 0); - y1 = y0 + (Input[1] != 0xFFFFU ? 1 : 0); - z1 = z0 + (Input[2] != 0xFFFFU ? 1 : 0); + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + y0 = FIXED_TO_INT(fy); + ry = FIXED_REST_TO_INT(fy); - for (OutChan = 0; OutChan < TotalOut; OutChan++) - { + X0 = p -> opta[1] * x0; + X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[1]); - d000 = DENS(x0, y0, z0); - d001 = DENS(x0, y0, z1); - d010 = DENS(x0, y1, z0); - d011 = DENS(x0, y1, z1); + Y0 = p -> opta[0] * y0; + Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[0]); - d100 = DENS(x1, y0, z0); - d101 = DENS(x1, y0, z1); - d110 = DENS(x1, y1, z0); - d111 = DENS(x1, y1, z1); + for (OutChan = 0; OutChan < TotalOut; OutChan++) { - - dx00 = LERP(fx, d000, d100); - dx01 = LERP(fx, d001, d101); - dx10 = LERP(fx, d010, d110); - dx11 = LERP(fx, d011, d111); + d00 = DENS(X0, Y0); + d01 = DENS(X0, Y1); + d10 = DENS(X1, Y0); + d11 = DENS(X1, Y1); - dxy0 = LERP(fy, dx00, dx10); - dxy1 = LERP(fy, dx01, dx11); + dx0 = LERP(rx, d00, d10); + dx1 = LERP(rx, d01, d11); - dxyz = LERP(fz, dxy0, dxy1); + dxy = LERP(ry, dx0, dx1); - Output[OutChan] = (WORD) floor(dxyz + .5); + Output[OutChan] = (cmsUInt16Number) dxy; } @@ -717,23 +473,91 @@ } -#endif +// Trilinear interpolation (16 bits) - cmsFloat32Number version +static +void TrilinearInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) + +{ +# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) +# define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) + + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + cmsFloat32Number px, py, pz; + int x0, y0, z0, + X0, Y0, Z0, X1, Y1, Z1; + int TotalOut, OutChan; + cmsFloat32Number fx, fy, fz, + d000, d001, d010, d011, + d100, d101, d110, d111, + dx00, dx01, dx10, dx11, + dxy0, dxy1, dxyz; + + TotalOut = p -> nOutputs; + + // We need some clipping here + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + pz = fclamp(Input[2]) * p->Domain[2]; + + x0 = (int) floor(px); fx = px - (cmsFloat32Number) x0; // We need full floor funcionality here + y0 = (int) floor(py); fy = py - (cmsFloat32Number) y0; + z0 = (int) floor(pz); fz = pz - (cmsFloat32Number) z0; + + X0 = p -> opta[2] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d000 = DENS(X0, Y0, Z0); + d001 = DENS(X0, Y0, Z1); + d010 = DENS(X0, Y1, Z0); + d011 = DENS(X0, Y1, Z1); + + d100 = DENS(X1, Y0, Z0); + d101 = DENS(X1, Y0, Z1); + d110 = DENS(X1, Y1, Z0); + d111 = DENS(X1, Y1, Z1); -#ifndef USE_FLOAT + dx00 = LERP(fx, d000, d100); + dx01 = LERP(fx, d001, d101); + dx10 = LERP(fx, d010, d110); + dx11 = LERP(fx, d011, d111); + + dxy0 = LERP(fy, dx00, dx10); + dxy1 = LERP(fy, dx01, dx11); + + dxyz = LERP(fz, dxy0, dxy1); + + Output[OutChan] = dxyz; + } + + +# undef LERP +# undef DENS +} // Trilinear interpolation (16 bits) - optimized version - -void cmsTrilinearInterp16(WORD Input[], WORD Output[], - WORD LutTable[], LPL16PARAMS p) +static +void TrilinearInterp16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p) { #define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) -#define LERP(a,l,h) (WORD) (l+ ROUND_FIXED_TO_INT(((h-l)*a))) - +#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; int OutChan, TotalOut; - Fixed32 fx, fy, fz; + cmsS15Fixed16Number fx, fy, fz; register int rx, ry, rz; int x0, y0, z0; register int X0, X1, Y0, Y1, Z0, Z1; @@ -742,37 +566,32 @@ dx00, dx01, dx10, dx11, dxy0, dxy1, dxyz; - TotalOut = p -> nOutputs; - fx = ToFixedDomain((int) Input[0] * p -> Domain); + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); x0 = FIXED_TO_INT(fx); rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain - fy = ToFixedDomain((int) Input[1] * p -> Domain); + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); y0 = FIXED_TO_INT(fy); ry = FIXED_REST_TO_INT(fy); - fz = ToFixedDomain((int) Input[2] * p -> Domain); + fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); z0 = FIXED_TO_INT(fz); rz = FIXED_REST_TO_INT(fz); - - X0 = p -> opta3 * x0; - X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta3); - - Y0 = p -> opta2 * y0; - Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta2); + X0 = p -> opta[2] * x0; + X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[2]); - Z0 = p -> opta1 * z0; - Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta1); - + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[1]); + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta[0]); - for (OutChan = 0; OutChan < TotalOut; OutChan++) - { + for (OutChan = 0; OutChan < TotalOut; OutChan++) { d000 = DENS(X0, Y0, Z0); d001 = DENS(X0, Y0, Z1); @@ -795,7 +614,7 @@ dxyz = LERP(rz, dxy0, dxy1); - Output[OutChan] = (WORD) dxyz; + Output[OutChan] = (cmsUInt16Number) dxyz; } @@ -803,332 +622,922 @@ # undef DENS } -#endif - - -#ifdef USE_FLOAT - -#define DENS(X, Y, Z) (double) (LutTable[TotalOut*((Z)+clutPoints*((Y)+clutPoints*(X)))+OutChan]) - // Tetrahedral interpolation, using Sakamoto algorithm. - -void cmsTetrahedralInterp16(WORD Input[], - WORD Output[], - WORD LutTable[], - LPL16PARAMS p) +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static +void TetrahedralInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) { - double px, py, pz; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number px, py, pz; int x0, y0, z0, - x1, y1, z1; - double fx, fy, fz; - double c1=0, c2=0, c3=0; - int clutPoints, OutChan, TotalOut; + X0, Y0, Z0, X1, Y1, Z1; + cmsFloat32Number rx, ry, rz; + cmsFloat32Number c0, c1=0, c2=0, c3=0; + int OutChan, TotalOut; - - clutPoints = p -> Domain + 1; TotalOut = p -> nOutputs; + // We need some clipping here + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + pz = fclamp(Input[2]) * p->Domain[2]; - px = ((double) Input[0] * p->Domain) / 65535.0; - py = ((double) Input[1] * p->Domain) / 65535.0; - pz = ((double) Input[2] * p->Domain) / 65535.0; - - x0 = (int) _cmsQuickFloor(px); fx = (px - (double) x0); - y0 = (int) _cmsQuickFloor(py); fy = (py - (double) y0); - z0 = (int) _cmsQuickFloor(pz); fz = (pz - (double) z0); + x0 = (int) floor(px); rx = (px - (cmsFloat32Number) x0); // We need full floor functionality here + y0 = (int) floor(py); ry = (py - (cmsFloat32Number) y0); + z0 = (int) floor(pz); rz = (pz - (cmsFloat32Number) z0); - x1 = x0 + (Input[0] != 0xFFFFU ? 1 : 0); - y1 = y0 + (Input[1] != 0xFFFFU ? 1 : 0); - z1 = z0 + (Input[2] != 0xFFFFU ? 1 : 0); + X0 = p -> opta[2] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]); + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]); - for (OutChan=0; OutChan < TotalOut; OutChan++) - { + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan=0; OutChan < TotalOut; OutChan++) { // These are the 6 Tetrahedral - if (fx >= fy && fy >= fz) - { - c1 = DENS(x1, y0, z0) - DENS(x0, y0, z0); - c2 = DENS(x1, y1, z0) - DENS(x1, y0, z0); - c3 = DENS(x1, y1, z1) - DENS(x1, y1, z0); - } - else - if (fx >= fz && fz >= fy) - { - c1 = DENS(x1, y0, z0) - DENS(x0, y0, z0); - c2 = DENS(x1, y1, z1) - DENS(x1, y0, z1); - c3 = DENS(x1, y0, z1) - DENS(x1, y0, z0); - } - else - if (fz >= fx && fx >= fy) - { - c1 = DENS(x1, y0, z1) - DENS(x0, y0, z1); - c2 = DENS(x1, y1, z1) - DENS(x1, y0, z1); - c3 = DENS(x0, y0, z1) - DENS(x0, y0, z0); - } - else - if (fy >= fx && fx >= fz) - { - c1 = DENS(x1, y1, z0) - DENS(x0, y1, z0); - c2 = DENS(x0, y1, z0) - DENS(x0, y0, z0); - c3 = DENS(x1, y1, z1) - DENS(x1, y1, z0); + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; - } - else - if (fy >= fz && fz >= fx) - { - c1 = DENS(x1, y1, z1) - DENS(x0, y1, z1); - c2 = DENS(x0, y1, z0) - DENS(x0, y0, z0); - c3 = DENS(x0, y1, z1) - DENS(x0, y1, z0); - } - else - if (fz >= fy && fy >= fx) - { - c1 = DENS(x1, y1, z1) - DENS(x0, y1, z1); - c2 = DENS(x0, y1, z1) - DENS(x0, y0, z1); - c3 = DENS(x0, y0, z1) - DENS(x0, y0, z0); - } - else - { - c1 = c2 = c3 = 0; - // assert(FALSE); - } + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); - Output[OutChan] = (WORD) floor((double) DENS(x0,y0,z0) + c1 * fx + c2 * fy + c3 * fz + .5); + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Output[OutChan] = c0 + c1 * rx + c2 * ry + c3 * rz; } } #undef DENS -#else - -#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) - - -void cmsTetrahedralInterp16(WORD Input[], - WORD Output[], - WORD LutTable1[], - LPL16PARAMS p) -{ - - Fixed32 fx, fy, fz; - Fixed32 rx, ry, rz; - int x0, y0, z0; - Fixed32 c0, c1, c2, c3, Rest; - int OutChan; - Fixed32 X0, X1, Y0, Y1, Z0, Z1; - int TotalOut = p -> nOutputs; - register LPWORD LutTable = LutTable1; - fx = ToFixedDomain((int) Input[0] * p -> Domain); - fy = ToFixedDomain((int) Input[1] * p -> Domain); - fz = ToFixedDomain((int) Input[2] * p -> Domain); +static +void TetrahedralInterp16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table; + cmsS15Fixed16Number fx, fy, fz; + cmsS15Fixed16Number rx, ry, rz; + int x0, y0, z0; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + cmsUInt32Number TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); + + x0 = FIXED_TO_INT(fx); + y0 = FIXED_TO_INT(fy); + z0 = FIXED_TO_INT(fz); + + rx = FIXED_REST_TO_INT(fx); + ry = FIXED_REST_TO_INT(fy); + rz = FIXED_REST_TO_INT(fz); + + X0 = p -> opta[2] * x0; + X1 = (Input[0] == 0xFFFFU ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = (Input[1] == 0xFFFFU ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = (Input[2] == 0xFFFFU ? 0 : p->opta[0]); + + LutTable = &LutTable[X0+Y0+Z0]; + + // Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)) + // which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16 + // This can be replaced by: t = Rest+0x8001, x = (t + (t>>16))>>16 + // at the cost of being off by one at 7fff and 17ffe. + if (rx >= ry) { + if (ry >= rz) { + Y1 += X1; + Z1 += Y1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c3 -= c2; + c2 -= c1; + c1 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else if (rz >= rx) { + X1 += Z1; + Y1 += X1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c2 -= c1; + c1 -= c3; + c3 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else { + Z1 += X1; + Y1 += Z1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c2 -= c3; + c3 -= c1; + c1 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } + } else { + if (rx >= rz) { + X1 += Y1; + Z1 += X1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c3 -= c1; + c1 -= c2; + c2 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else if (ry >= rz) { + Z1 += Y1; + X1 += Z1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c1 -= c3; + c3 -= c2; + c2 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else { + Y1 += Z1; + X1 += Y1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c1 -= c2; + c2 -= c3; + c3 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } + } +} + + +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static +void Eval4Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + cmsS15Fixed16Number fx, fy, fz; + cmsS15Fixed16Number rx, ry, rz; + int x0, y0, z0; + cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + cmsUInt32Number i; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + cmsUInt32Number OutChan; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + + + fk = _cmsToFixedDomain((int) Input[0] * p16 -> Domain[0]); + fx = _cmsToFixedDomain((int) Input[1] * p16 -> Domain[1]); + fy = _cmsToFixedDomain((int) Input[2] * p16 -> Domain[2]); + fz = _cmsToFixedDomain((int) Input[3] * p16 -> Domain[3]); + + k0 = FIXED_TO_INT(fk); x0 = FIXED_TO_INT(fx); y0 = FIXED_TO_INT(fy); z0 = FIXED_TO_INT(fz); + rk = FIXED_REST_TO_INT(fk); rx = FIXED_REST_TO_INT(fx); ry = FIXED_REST_TO_INT(fy); rz = FIXED_REST_TO_INT(fz); - X0 = p -> opta3 * x0; - X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta3); + K0 = p16 -> opta[3] * k0; + K1 = K0 + (Input[0] == 0xFFFFU ? 0 : p16->opta[3]); + + X0 = p16 -> opta[2] * x0; + X1 = X0 + (Input[1] == 0xFFFFU ? 0 : p16->opta[2]); + + Y0 = p16 -> opta[1] * y0; + Y1 = Y0 + (Input[2] == 0xFFFFU ? 0 : p16->opta[1]); + + Z0 = p16 -> opta[0] * z0; + Z1 = Z0 + (Input[3] == 0xFFFFU ? 0 : p16->opta[0]); + + LutTable = (cmsUInt16Number*) p16 -> Table; + LutTable += K0; + + for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; - Y0 = p -> opta2 * y0; - Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta2); + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz; + + Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))); + } + + + LutTable = (cmsUInt16Number*) p16 -> Table; + LutTable += K1; + + for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { - Z0 = p -> opta1 * z0; - Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta1); + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz; + + Tmp2[OutChan] = (cmsUInt16Number) (c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))); + } - // These are the 6 Tetrahedral - for (OutChan=0; OutChan < TotalOut; OutChan++) { + for (i=0; i < p16 -> nOutputs; i++) { + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } +} +#undef DENS - c0 = DENS(X0, Y0, Z0); + +// For more that 3 inputs (i.e., CMYK) +// evaluate two 3-dimensional interpolations and then linearly interpolate between them. + - if (rx >= ry && ry >= rz) { - - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); +static +void Eval4InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; - } - else - if (rx >= rz && rz >= ry) { + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + K0 = p -> opta[3] * k0; + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[3]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 3*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; - } - else - if (rz >= rx && rx >= ry) { + TetrahedralInterpFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + TetrahedralInterpFloat(Input + 1, Tmp2, &p1); - c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; + for (i=0; i < p -> nOutputs; i++) + { + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + Output[i] = y0 + (y1 - y0) * rest; } - else - if (ry >= rx && rx >= rz) { +} + + +static +void Eval5Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], - c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + const cmsUInt16Number* T; + cmsUInt32Number i; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; - } - else - if (ry >= rz && rz >= rx) { - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); + k0 = FIXED_TO_INT(fk); + rk = FIXED_REST_TO_INT(fk); - } - else - if (rz >= ry && ry >= rx) { + K0 = p16 -> opta[4] * k0; + K1 = p16 -> opta[4] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); + + p1 = *p16; + memmove(&p1.Domain[0], &p16 ->Domain[1], 4*sizeof(cmsUInt32Number)); - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; + T = LutTable + K0; + p1.Table = T; + + Eval4Inputs(Input + 1, Tmp1, &p1); - } - else { - c1 = c2 = c3 = 0; - // assert(FALSE); + T = LutTable + K1; + p1.Table = T; + + Eval4Inputs(Input + 1, Tmp2, &p1); + + for (i=0; i < p16 -> nOutputs; i++) { + + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); } - Rest = c1 * rx + c2 * ry + c3 * rz; +} + + +static +void Eval5InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; - // There is a lot of math hidden in this expression. The rest is in fixed domain - // and the result in 0..ffff domain. So the complete expression should be - // ROUND_FIXED_TO_INT(ToFixedDomain(Rest)) But that can be optimized as (Rest + 0x7FFF) / 0xFFFF + K0 = p -> opta[4] * k0; + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[4]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 4*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval4InputsFloat(Input + 1, Tmp1, &p1); - Output[OutChan] = (WORD) (c0 + ((Rest + 0x7FFF) / 0xFFFF)); + T = LutTable + K1; + p1.Table = T; + + Eval4InputsFloat(Input + 1, Tmp2, &p1); - } + for (i=0; i < p -> nOutputs; i++) { + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + } } -#undef DENS +static +void Eval6Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + const cmsUInt16Number* T; + cmsUInt32Number i; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); + k0 = FIXED_TO_INT(fk); + rk = FIXED_REST_TO_INT(fk); + + K0 = p16 -> opta[5] * k0; + K1 = p16 -> opta[5] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); + + p1 = *p16; + memmove(&p1.Domain[0], &p16 ->Domain[1], 5*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval5Inputs(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval5Inputs(Input + 1, Tmp2, &p1); + + for (i=0; i < p16 -> nOutputs; i++) { + + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } + +} + -#endif +static +void Eval6InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[5] * k0; + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[5]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 5*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval5InputsFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval5InputsFloat(Input + 1, Tmp2, &p1); + + for (i=0; i < p -> nOutputs; i++) { + + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + } +} + + +static +void Eval7Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + const cmsUInt16Number* T; + cmsUInt32Number i; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; -// A optimized interpolation for 8-bit input. + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); + k0 = FIXED_TO_INT(fk); + rk = FIXED_REST_TO_INT(fk); + + K0 = p16 -> opta[6] * k0; + K1 = p16 -> opta[6] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); -#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) + p1 = *p16; + memmove(&p1.Domain[0], &p16 ->Domain[1], 6*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval6Inputs(Input + 1, Tmp1, &p1); -void cmsTetrahedralInterp8(WORD Input[], - WORD Output[], - WORD LutTable[], - LPL16PARAMS p) + T = LutTable + K1; + p1.Table = T; + + Eval6Inputs(Input + 1, Tmp2, &p1); + + for (i=0; i < p16 -> nOutputs; i++) { + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } +} + + +static +void Eval7InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) { + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; - int r, g, b; - Fixed32 rx, ry, rz; - Fixed32 c1, c2, c3, Rest; - int OutChan; - register Fixed32 X0, X1, Y0, Y1, Z0, Z1; - int TotalOut = p -> nOutputs; - register LPL8PARAMS p8 = p ->p8; + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[6] * k0; + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[6]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 6*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval6InputsFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval6InputsFloat(Input + 1, Tmp2, &p1); + + + for (i=0; i < p -> nOutputs; i++) { + + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + + } +} + +static +void Eval8Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + const cmsUInt16Number* T; + cmsUInt32Number i; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); + k0 = FIXED_TO_INT(fk); + rk = FIXED_REST_TO_INT(fk); + + K0 = p16 -> opta[7] * k0; + K1 = p16 -> opta[7] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); + + p1 = *p16; + memmove(&p1.Domain[0], &p16 ->Domain[1], 7*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval7Inputs(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + Eval7Inputs(Input + 1, Tmp2, &p1); + + for (i=0; i < p16 -> nOutputs; i++) { + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } +} - r = Input[0] >> 8; - g = Input[1] >> 8; - b = Input[2] >> 8; - - X0 = X1 = p8->X0[r]; - Y0 = Y1 = p8->Y0[g]; - Z0 = Z1 = p8->Z0[b]; +static +void Eval8InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; - X1 += (r == 255) ? 0 : p ->opta3; - Y1 += (g == 255) ? 0 : p ->opta2; - Z1 += (b == 255) ? 0 : p ->opta1; + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[7] * k0; + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[7]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 7*sizeof(cmsUInt32Number)); - rx = p8 ->rx[r]; - ry = p8 ->ry[g]; - rz = p8 ->rz[b]; + T = LutTable + K0; + p1.Table = T; + + Eval7InputsFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval7InputsFloat(Input + 1, Tmp2, &p1); - // These are the 6 Tetrahedral - for (OutChan=0; OutChan < TotalOut; OutChan++) { + for (i=0; i < p -> nOutputs; i++) { - if (rx >= ry && ry >= rz) - { + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; - c1 = DENS(X1, Y0, Z0) - DENS(X0, Y0, Z0); - c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - + Output[i] = y0 + (y1 - y0) * rest; } - else - if (rx >= rz && rz >= ry) - { - c1 = DENS(X1, Y0, Z0) - DENS(X0, Y0, Z0); - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); +} + +// The default factory +static +cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags) +{ + + cmsInterpFunction Interpolation; + cmsBool IsFloat = (dwFlags & CMS_LERP_FLAGS_FLOAT); + cmsBool IsTrilinear = (dwFlags & CMS_LERP_FLAGS_TRILINEAR); + + memset(&Interpolation, 0, sizeof(Interpolation)); + + // Safety check + if (nInputChannels >= 4 && nOutputChannels >= MAX_STAGE_CHANNELS) + return Interpolation; + + switch (nInputChannels) { + + case 1: // Gray LUT / linear + + if (nOutputChannels == 1) { - } - else - if (rz >= rx && rx >= ry) - { + if (IsFloat) + Interpolation.LerpFloat = LinLerp1Dfloat; + else + Interpolation.Lerp16 = LinLerp1D; + + } + else { - c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - DENS(X0, Y0, Z0); + if (IsFloat) + Interpolation.LerpFloat = Eval1InputFloat; + else + Interpolation.Lerp16 = Eval1Input; + } + break; - } - else - if (ry >= rx && rx >= rz) - { + case 2: // Duotone + if (IsFloat) + Interpolation.LerpFloat = BilinearInterpFloat; + else + Interpolation.Lerp16 = BilinearInterp16; + break; + + case 3: // RGB et al + + if (IsTrilinear) { - c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); - c2 = DENS(X0, Y1, Z0) - DENS(X0, Y0, Z0); - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + if (IsFloat) + Interpolation.LerpFloat = TrilinearInterpFloat; + else + Interpolation.Lerp16 = TrilinearInterp16; + } + else { + + if (IsFloat) + Interpolation.LerpFloat = TetrahedralInterpFloat; + else { - } - else - if (ry >= rz && rz >= rx) - { + Interpolation.Lerp16 = TetrahedralInterp16; + } + } + break; - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z0) - DENS(X0, Y0, Z0); - c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + case 4: // CMYK lut + + if (IsFloat) + Interpolation.LerpFloat = Eval4InputsFloat; + else + Interpolation.Lerp16 = Eval4Inputs; + break; - } - else - if (rz >= ry && ry >= rx) - { - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - DENS(X0, Y0, Z0); + case 5: // 5 Inks + if (IsFloat) + Interpolation.LerpFloat = Eval5InputsFloat; + else + Interpolation.Lerp16 = Eval5Inputs; + break; + + case 6: // 6 Inks + if (IsFloat) + Interpolation.LerpFloat = Eval6InputsFloat; + else + Interpolation.Lerp16 = Eval6Inputs; + break; - } - else { - c1 = c2 = c3 = 0; - // assert(FALSE); - } + case 7: // 7 inks + if (IsFloat) + Interpolation.LerpFloat = Eval7InputsFloat; + else + Interpolation.Lerp16 = Eval7Inputs; + break; + case 8: // 8 inks + if (IsFloat) + Interpolation.LerpFloat = Eval8InputsFloat; + else + Interpolation.Lerp16 = Eval8Inputs; + break; - Rest = c1 * rx + c2 * ry + c3 * rz; + break; - Output[OutChan] = (WORD) (DENS(X0,Y0,Z0) + ((Rest + 0x7FFF) / 0xFFFF)); + default: + Interpolation.Lerp16 = NULL; } + return Interpolation; } - -#undef DENS - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsio0.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsio0.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsio0.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -49,748 +50,1928 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" // Generic I/O, tag dictionary management, profile struct - +// IOhandlers are abstractions used by littleCMS to read from whatever file, stream, +// memory block or any storage. Each IOhandler provides implementations for read, +// write, seek and tell functions. LittleCMS code deals with IO across those objects. +// In this way, is easier to add support for new storage media. -#include "lcms.h" +// NULL stream, for taking care of used space ------------------------------------- - -// Memory-based stream --------------------------------------------------- +// NULL IOhandler basically does nothing but keep track on how many bytes have been +// written. This is handy when creating profiles, where the file size is needed in the +// header. Then, whole profile is serialized across NULL IOhandler and a second pass +// writes the bytes to the pertinent IOhandler. typedef struct { - LPBYTE Block; // Points to allocated memory - size_t Size; // Size of allocated memory - size_t Pointer; // Points to current location - int FreeBlockOnClose; // As title + cmsUInt32Number Pointer; // Points to current location +} FILENULL; + +static +cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + cmsUInt32Number len = size * count; + ResData -> Pointer += len; + return count; + + cmsUNUSED_PARAMETER(Buffer); +} - } FILEMEM; +static +cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + ResData ->Pointer = offset; + return TRUE; +} + +static +cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + return ResData -> Pointer; +} static -LPVOID MemoryOpen(LPBYTE Block, size_t Size, char Mode) +cmsBool NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr) { - FILEMEM* fm = (FILEMEM*) _cmsMalloc(sizeof(FILEMEM)); - if (fm == NULL) return NULL; + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + ResData ->Pointer += size; + if (ResData ->Pointer > iohandler->UsedSpace) + iohandler->UsedSpace = ResData ->Pointer; + + return TRUE; - ZeroMemory(fm, sizeof(FILEMEM)); + cmsUNUSED_PARAMETER(Ptr); +} - if (Mode == 'r') { +static +cmsBool NULLClose(cmsIOHANDLER* iohandler) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; - fm ->Block = (LPBYTE) _cmsMalloc(Size); - if (fm ->Block == NULL) { - _cmsFree(fm); - return NULL; - } + _cmsFree(iohandler ->ContextID, ResData); + _cmsFree(iohandler ->ContextID, iohandler); + return TRUE; +} - CopyMemory(fm->Block, Block, Size); - fm ->FreeBlockOnClose = TRUE; - } - else { - fm ->Block = Block; - fm ->FreeBlockOnClose = FALSE; - } +// The NULL IOhandler creator +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID) +{ + struct _cms_io_handler* iohandler = NULL; + FILENULL* fm = NULL; - fm ->Size = Size; + iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler)); + if (iohandler == NULL) return NULL; + + fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL)); + if (fm == NULL) goto Error; + fm ->Pointer = 0; - return (LPVOID) fm; + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + iohandler ->ReportedSize = 0; + iohandler ->PhysicalFile[0] = 0; + + iohandler ->Read = NULLRead; + iohandler ->Seek = NULLSeek; + iohandler ->Close = NULLClose; + iohandler ->Tell = NULLTell; + iohandler ->Write = NULLWrite; + + return iohandler; + +Error: + if (iohandler) _cmsFree(ContextID, iohandler); + return NULL; + } -static -size_t MemoryRead(LPVOID buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc) -{ - FILEMEM* ResData = (FILEMEM*) Icc ->stream; - LPBYTE Ptr; - size_t len = size * count; - size_t extent = ResData -> Pointer + len; +// Memory-based stream -------------------------------------------------------------- + +// Those functions implements an iohandler which takes a block of memory as storage medium. - if (len == 0) { - return 0; - } +typedef struct { + cmsUInt8Number* Block; // Points to allocated memory + cmsUInt32Number Size; // Size of allocated memory + cmsUInt32Number Pointer; // Points to current location + int FreeBlockOnClose; // As title + +} FILEMEM; - if (len / size != count) { - cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Integer overflow with count / size."); - return 0; - } +static +cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + cmsUInt8Number* Ptr; + cmsUInt32Number len = size * count; - if (extent < len || extent < ResData -> Pointer) { - cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Integer overflow with len."); - return 0; - } + if (ResData -> Pointer + len > ResData -> Size){ - if (ResData -> Pointer + len > ResData -> Size) { - - len = (ResData -> Size - ResData -> Pointer); - cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Got %d bytes, block should be of %d bytes", len * size, count * size); - return 0; - } + len = (ResData -> Size - ResData -> Pointer); + cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size); + return 0; + } Ptr = ResData -> Block; Ptr += ResData -> Pointer; - CopyMemory(buffer, Ptr, len); - ResData -> Pointer += (int) len; + memmove(Buffer, Ptr, len); + ResData -> Pointer += len; return count; } // SEEK_CUR is assumed - static -LCMSBOOL MemorySeek(struct _lcms_iccprofile_struct* Icc, size_t offset) +cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset) { - FILEMEM* ResData = (FILEMEM*) Icc ->stream; + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; if (offset > ResData ->Size) { - cmsSignalError(LCMS_ERRC_ABORTED, "Pointer error; probably corrupted file"); - return TRUE; + cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK, "Too few data; probably corrupted profile"); + return FALSE; } - ResData ->Pointer = (DWORD) offset; - return FALSE; + ResData ->Pointer = offset; + return TRUE; } -// FTell - +// Tell for memory static -size_t MemoryTell(struct _lcms_iccprofile_struct* Icc) +cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler) { - FILEMEM* ResData = (FILEMEM*) Icc ->stream; + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + if (ResData == NULL) return 0; return ResData -> Pointer; } -// Writes data to memory, also keeps used space for further reference. NO CHECK IS PERFORMED - +// Writes data to memory, also keeps used space for further reference. static -LCMSBOOL MemoryWrite(struct _lcms_iccprofile_struct* Icc, size_t size, void *Ptr) +cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr) { - FILEMEM* ResData = (FILEMEM*) Icc ->stream; - - if (size == 0) return TRUE; + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; - if (ResData != NULL) - CopyMemory(ResData ->Block + ResData ->Pointer, Ptr, size); - - ResData->Pointer += size; - Icc->UsedSpace += size; - - return TRUE; -} - + if (ResData == NULL) return FALSE; // Housekeeping -static -LCMSBOOL MemoryGrow(struct _lcms_iccprofile_struct* Icc, size_t size) -{ - FILEMEM* ResData = (FILEMEM*) Icc->stream; + // Check for available space. Clip. + if (ResData->Pointer + size > ResData->Size) { + size = ResData ->Size - ResData->Pointer; + } - void* newBlock = NULL; + if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing - /* Follow same policies as functions in lcms.h */ - if (ResData->Size + size < 0) return NULL; - if (ResData->Size + size > ((size_t)1024*1024*500)) return NULL; - - newBlock = realloc(ResData->Block, ResData->Size + size); + memmove(ResData ->Block + ResData ->Pointer, Ptr, size); + ResData ->Pointer += size; - if (!newBlock) { - return FALSE; - } - ResData->Block = newBlock; - ResData->Size += size; + if (ResData ->Pointer > iohandler->UsedSpace) + iohandler->UsedSpace = ResData ->Pointer; + return TRUE; } static -LCMSBOOL MemoryClose(struct _lcms_iccprofile_struct* Icc) +cmsBool MemoryClose(struct _cms_io_handler* iohandler) { - FILEMEM* ResData = (FILEMEM*) Icc ->stream; + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; if (ResData ->FreeBlockOnClose) { - if (ResData ->Block) _cmsFree(ResData ->Block); + if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block); } - _cmsFree(ResData); - return 0; + + _cmsFree(iohandler ->ContextID, ResData); + _cmsFree(iohandler ->ContextID, iohandler); + + return TRUE; } +// Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes +// a copy of the memory block for letting user to free the memory after invoking open profile. In write +// mode ("w"), Buffere points to the begin of memory block to be written. +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode) +{ + cmsIOHANDLER* iohandler = NULL; + FILEMEM* fm = NULL; + + _cmsAssert(AccessMode != NULL); + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + switch (*AccessMode) { + + case 'r': + fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); + if (fm == NULL) goto Error; + + if (Buffer == NULL) { + cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer"); + goto Error; + } + + fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size); + if (fm ->Block == NULL) { + + _cmsFree(ContextID, fm); + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", size); + return NULL; + } + + + memmove(fm->Block, Buffer, size); + fm ->FreeBlockOnClose = TRUE; + fm ->Size = size; + fm ->Pointer = 0; + iohandler -> ReportedSize = size; + break; + + case 'w': + fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); + if (fm == NULL) goto Error; + + fm ->Block = (cmsUInt8Number*) Buffer; + fm ->FreeBlockOnClose = FALSE; + fm ->Size = size; + fm ->Pointer = 0; + iohandler -> ReportedSize = 0; + break; + + default: + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode); + return NULL; + } + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + iohandler ->PhysicalFile[0] = 0; + + iohandler ->Read = MemoryRead; + iohandler ->Seek = MemorySeek; + iohandler ->Close = MemoryClose; + iohandler ->Tell = MemoryTell; + iohandler ->Write = MemoryWrite; + + return iohandler; + +Error: + if (fm) _cmsFree(ContextID, fm); + if (iohandler) _cmsFree(ContextID, iohandler); + return NULL; +} // File-based stream ------------------------------------------------------- +// Read count elements of size bytes each. Return number of elements read static -LPVOID FileOpen(const char* filename) +cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) { - return (void*) fopen(filename, "rb"); -} + cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream); -static -size_t FileRead(void *buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc) -{ - size_t nReaded = fread(buffer, size, count, (FILE*) Icc->stream); if (nReaded != count) { - cmsSignalError(LCMS_ERRC_ABORTED, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); + cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); return 0; } return nReaded; } +// Position file pointer in the file +static +cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) +{ + if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) { + + cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file"); + return FALSE; + } + + return TRUE; +} + +// Returns file pointer position or 0 on error, which is also a valid position. +static +cmsUInt32Number FileTell(cmsIOHANDLER* iohandler) +{ + long t = ftell((FILE*)iohandler ->stream); + if (t == -1L) { + cmsSignalError(iohandler->ContextID, cmsERROR_FILE, "Tell error; probably corrupted file"); + return 0; + } + + return (cmsUInt32Number)t; +} + +// Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error +static +cmsBool FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer) +{ + if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written + + iohandler->UsedSpace += size; + return (fwrite(Buffer, size, 1, (FILE*)iohandler->stream) == 1); +} + +// Closes the file +static +cmsBool FileClose(cmsIOHANDLER* iohandler) +{ + if (fclose((FILE*) iohandler ->stream) != 0) return FALSE; + _cmsFree(iohandler ->ContextID, iohandler); + return TRUE; +} + +// Create a iohandler for disk based files. +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode) +{ + cmsIOHANDLER* iohandler = NULL; + FILE* fm = NULL; + cmsInt32Number fileLen; + + _cmsAssert(FileName != NULL); + _cmsAssert(AccessMode != NULL); + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + switch (*AccessMode) { + + case 'r': + fm = fopen(FileName, "rb"); + if (fm == NULL) { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName); + return NULL; + } + fileLen = cmsfilelength(fm); + if (fileLen < 0) + { + fclose(fm); + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName); + return NULL; + } + + iohandler -> ReportedSize = (cmsUInt32Number) fileLen; + break; + + case 'w': + fm = fopen(FileName, "wb"); + if (fm == NULL) { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName); + return NULL; + } + iohandler -> ReportedSize = 0; + break; + + default: + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode); + return NULL; + } + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + + // Keep track of the original file + strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1); + iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0; + + iohandler ->Read = FileRead; + iohandler ->Seek = FileSeek; + iohandler ->Close = FileClose; + iohandler ->Tell = FileTell; + iohandler ->Write = FileWrite; + + return iohandler; +} + +// Create a iohandler for stream based files +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream) +{ + cmsIOHANDLER* iohandler = NULL; + cmsInt32Number fileSize; + + fileSize = cmsfilelength(Stream); + if (fileSize < 0) + { + cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream"); + return NULL; + } + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + iohandler -> ContextID = ContextID; + iohandler -> stream = (void*) Stream; + iohandler -> UsedSpace = 0; + iohandler -> ReportedSize = (cmsUInt32Number) fileSize; + iohandler -> PhysicalFile[0] = 0; + + iohandler ->Read = FileRead; + iohandler ->Seek = FileSeek; + iohandler ->Close = FileClose; + iohandler ->Tell = FileTell; + iohandler ->Write = FileWrite; + + return iohandler; +} + + + +// Close an open IO handler +cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) +{ + return io -> Close(io); +} + +// ------------------------------------------------------------------------------------------------------- + +cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile; + + if (Icc == NULL) return NULL; + return Icc->IOhandler; +} + +// Creates an empty structure holding all required parameters +cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) +{ + time_t now = time(NULL); + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE)); + if (Icc == NULL) return NULL; + + Icc ->ContextID = ContextID; + + // Set it to empty + Icc -> TagCount = 0; + + // Set default version + Icc ->Version = 0x02100000; + + // Set creation date/time + memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created)); + + // Create a mutex if the user provided proper plugin. NULL otherwise + Icc ->UsrMutex = _cmsCreateMutex(ContextID); + + // Return the handle + return (cmsHPROFILE) Icc; +} + +cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + if (Icc == NULL) return NULL; + return Icc -> ContextID; +} + + +// Return the number of tags +cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + if (Icc == NULL) return -1; + + return (cmsInt32Number) Icc->TagCount; +} + +// Return the tag signature of a given tag number +cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + if (n > Icc->TagCount) return (cmsTagSignature) 0; // Mark as not available + if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check + + return Icc ->TagNames[n]; +} + + +static +int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig) +{ + int i; + + for (i=0; i < (int) Profile -> TagCount; i++) { + + if (sig == Profile -> TagNames[i]) + return i; + } + + return -1; +} + +// Search for a specific tag in tag dictionary. Returns position or -1 if tag not found. +// If followlinks is turned on, then the position of the linked tag is returned +int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks) +{ + int n; + cmsTagSignature LinkedSig; + + do { + + // Search for given tag in ICC profile directory + n = SearchOneTag(Icc, sig); + if (n < 0) + return -1; // Not found + + if (!lFollowLinks) + return n; // Found, don't follow links + + // Is this a linked tag? + LinkedSig = Icc ->TagLinked[n]; + + // Yes, follow link + if (LinkedSig != (cmsTagSignature) 0) { + sig = LinkedSig; + } + + } while (LinkedSig != (cmsTagSignature) 0); + + return n; +} + +// Deletes a tag entry static -LCMSBOOL FileSeek(struct _lcms_iccprofile_struct* Icc, size_t offset) +void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i) +{ + _cmsAssert(Icc != NULL); + _cmsAssert(i >= 0); + + + if (Icc -> TagPtrs[i] != NULL) { + + // Free previous version + if (Icc ->TagSaveAsRaw[i]) { + _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); + } + else { + cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; + + if (TypeHandler != NULL) { + + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameter + LocalTypeHandler.ICCVersion = Icc ->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); + Icc ->TagPtrs[i] = NULL; + } + } + + } +} + + +// Creates a new tag entry +static +cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) +{ + int i; + + // Search for the tag + i = _cmsSearchTag(Icc, sig, FALSE); + if (i >= 0) { + + // Already exists? delete it + _cmsDeleteTagByPos(Icc, i); + *NewPos = i; + } + else { + + // No, make a new one + + if (Icc -> TagCount >= MAX_TABLE_TAG) { + cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); + return FALSE; + } + + *NewPos = (int) Icc ->TagCount; + Icc -> TagCount++; + } + + return TRUE; +} + + +// Check existence +cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile; + return _cmsSearchTag(Icc, sig, FALSE) >= 0; +} + + + +// Enforces that the profile version is per. spec. +// Operates on the big endian bytes from the profile. +// Called before converting to platform endianness. +// Byte 0 is BCD major version, so max 9. +// Byte 1 is 2 BCD digits, one per nibble. +// Reserved bytes 2 & 3 must be 0. +static +cmsUInt32Number _validatedVersion(cmsUInt32Number DWord) +{ + cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord; + cmsUInt8Number temp1; + cmsUInt8Number temp2; + + if (*pByte > 0x09) *pByte = (cmsUInt8Number) 0x09; + temp1 = (cmsUInt8Number) (*(pByte+1) & 0xf0); + temp2 = (cmsUInt8Number) (*(pByte+1) & 0x0f); + if (temp1 > 0x90U) temp1 = 0x90U; + if (temp2 > 0x09U) temp2 = 0x09U; + *(pByte+1) = (cmsUInt8Number)(temp1 | temp2); + *(pByte+2) = (cmsUInt8Number)0; + *(pByte+3) = (cmsUInt8Number)0; + + return DWord; +} + +// Read profile header and validate it +cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) +{ + cmsTagEntry Tag; + cmsICCHeader Header; + cmsUInt32Number i, j; + cmsUInt32Number HeaderSize; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsUInt32Number TagCount; + + + // Read the header + if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) { + return FALSE; + } + + // Validate file as an ICC profile + if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) { + cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature"); + return FALSE; + } + + // Adjust endianness of the used parameters + Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass); + Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace); + Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs); + + Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent); + Icc -> flags = _cmsAdjustEndianess32(Header.flags); + Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer); + Icc -> model = _cmsAdjustEndianess32(Header.model); + Icc -> creator = _cmsAdjustEndianess32(Header.creator); + + _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes); + Icc -> Version = _cmsAdjustEndianess32(_validatedVersion(Header.version)); + + // Get size as reported in header + HeaderSize = _cmsAdjustEndianess32(Header.size); + + // Make sure HeaderSize is lower than profile size + if (HeaderSize >= Icc ->IOhandler ->ReportedSize) + HeaderSize = Icc ->IOhandler ->ReportedSize; + + + // Get creation date/time + _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created); + + // The profile ID are 32 raw bytes + memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16); + + + // Read tag directory + if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE; + if (TagCount > MAX_TABLE_TAG) { + + cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount); + return FALSE; + } + + + // Read tag directory + Icc -> TagCount = 0; + for (i=0; i < TagCount; i++) { + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE; + if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE; + if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE; + + // Perform some sanity check. Offset + size should fall inside file. + if (Tag.offset + Tag.size > HeaderSize || + Tag.offset + Tag.size < Tag.offset) + continue; + + Icc -> TagNames[Icc ->TagCount] = Tag.sig; + Icc -> TagOffsets[Icc ->TagCount] = Tag.offset; + Icc -> TagSizes[Icc ->TagCount] = Tag.size; + + // Search for links + for (j=0; j < Icc ->TagCount; j++) { + + if ((Icc ->TagOffsets[j] == Tag.offset) && + (Icc ->TagSizes[j] == Tag.size)) { + + Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; + } + + } + + Icc ->TagCount++; + } + + return TRUE; +} + +// Saves profile header +cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace) +{ + cmsICCHeader Header; + cmsUInt32Number i; + cmsTagEntry Tag; + cmsUInt32Number Count; + + Header.size = _cmsAdjustEndianess32(UsedSpace); + Header.cmmId = _cmsAdjustEndianess32(lcmsSignature); + Header.version = _cmsAdjustEndianess32(Icc ->Version); + + Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass); + Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace); + Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS); + + // NOTE: in v4 Timestamp must be in UTC rather than in local time + _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created); + + Header.magic = _cmsAdjustEndianess32(cmsMagicNumber); + +#ifdef CMS_IS_WINDOWS_ + Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); +#else + Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh); +#endif + + Header.flags = _cmsAdjustEndianess32(Icc -> flags); + Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer); + Header.model = _cmsAdjustEndianess32(Icc -> model); + + _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes); + + // Rendering intent in the header (for embedded profiles) + Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent); + + // Illuminant is always D50 + Header.illuminant.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->X)); + Header.illuminant.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y)); + Header.illuminant.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z)); + + // Created by LittleCMS (that's me!) + Header.creator = _cmsAdjustEndianess32(lcmsSignature); + + memset(&Header.reserved, 0, sizeof(Header.reserved)); + + // Set profile ID. Endianness is always big endian + memmove(&Header.profileID, &Icc ->ProfileID, 16); + + // Dump the header + if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE; + + // Saves Tag directory + + // Get true count + Count = 0; + for (i=0; i < Icc -> TagCount; i++) { + if (Icc ->TagNames[i] != (cmsTagSignature) 0) + Count++; + } + + // Store number of tags + if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE; + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue; // It is just a placeholder + + Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagNames[i]); + Tag.offset = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagOffsets[i]); + Tag.size = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagSizes[i]); + + if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- Set/Get several struct members + + +cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> RenderingIntent; +} + +void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> RenderingIntent = RenderingIntent; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return (cmsUInt32Number) Icc -> flags; +} + +void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> flags = (cmsUInt32Number) Flags; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->manufacturer; +} + +void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> manufacturer = manufacturer; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->creator; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->model; +} + +void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> model = model; +} + +void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); +} + +void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); +} + +void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(ProfileID, Icc ->ProfileID.ID8, 16); +} + +void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(&Icc -> ProfileID, ProfileID, 16); +} + +cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(Dest, &Icc ->Created, sizeof(struct tm)); + return TRUE; +} + +cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> PCS; +} + +void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> PCS = pcs; +} + +cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> ColorSpace; +} + +void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> ColorSpace = sig; +} + +cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> DeviceClass; +} + +void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> DeviceClass = sig; +} + +cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> Version; +} + +void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> Version = Version; +} + +// Get an hexadecimal number with same digits as v +static +cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut) +{ + char Buff[100]; + int i, len; + cmsUInt32Number out; + + for (len=0; in > 0 && len < 100; len++) { + + Buff[len] = (char) (in % BaseIn); + in /= BaseIn; + } + + for (i=len-1, out=0; i >= 0; --i) { + out = out * BaseOut + Buff[i]; + } + + return out; +} + +void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + // 4.2 -> 0x4200000 + + Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16; +} + +cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsUInt32Number n = Icc -> Version >> 16; + + return BaseToBase(n, 16, 10) / 100.0; +} +// -------------------------------------------------------------------------------------------------------------- + + +// Create profile from IOhandler +cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io) { - if (fseek((FILE*) Icc ->stream, (long) offset, SEEK_SET) != 0) { + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = io; + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + +// Create profile from IOhandler +cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = io; + if (write) { + + NewIcc -> IsWrite = TRUE; + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + + +// Create profile from disk file +cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (*sAccess == 'W' || *sAccess == 'w') { + + NewIcc -> IsWrite = TRUE; + + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) +{ + return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); +} + + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (*sAccess == 'w') { + + NewIcc -> IsWrite = TRUE; + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; + +} + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) +{ + return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); +} + + +// Open from memory block +cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty; + + hEmpty = cmsCreateProfilePlaceholder(ContextID); + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + // Ok, in this case const void* is casted to void* just because open IO handler + // shares read and writing modes. Don't abuse this feature! + NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r"); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (!_cmsReadHeader(NewIcc)) goto Error; + + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize) +{ + return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize); +} + + + +// Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig +static +cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) +{ + cmsUInt8Number* Data; + cmsUInt32Number i; + cmsUInt32Number Begin; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsTagDescriptor* TagDescriptor; + cmsTagTypeSignature TypeBase; + cmsTagTypeSignature Type; + cmsTagTypeHandler* TypeHandler; + cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc); + cmsTagTypeHandler LocalTypeHandler; + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue; + + // Linked tags are not written + if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue; + + Icc -> TagOffsets[i] = Begin = io ->UsedSpace; + + Data = (cmsUInt8Number*) Icc -> TagPtrs[i]; + + if (!Data) { + + // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. + // In this case a blind copy of the block data is performed + if (FileOrig != NULL && Icc -> TagOffsets[i]) { + + cmsUInt32Number TagSize = FileOrig -> TagSizes[i]; + cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i]; + void* Mem; + + if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE; + + Mem = _cmsMalloc(Icc ->ContextID, TagSize); + if (Mem == NULL) return FALSE; + + if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE; + if (!io ->Write(io, TagSize, Mem)) return FALSE; + _cmsFree(Icc ->ContextID, Mem); + + Icc -> TagSizes[i] = (io ->UsedSpace - Begin); + + + // Align to 32 bit boundary. + if (! _cmsWriteAlignment(io)) + return FALSE; + } + + continue; + } + + + // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done) + if (Icc ->TagSaveAsRaw[i]) { + + if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE; + } + else { + + // Search for support on this tag + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]); + if (TagDescriptor == NULL) continue; // Unsupported, ignore it + + if (TagDescriptor ->DecideType != NULL) { + + Type = TagDescriptor ->DecideType(Version, Data); + } + else { + + Type = TagDescriptor ->SupportedTypes[0]; + } + + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); + + if (TypeHandler == NULL) { + cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); + continue; + } + + TypeBase = TypeHandler ->Signature; + if (!_cmsWriteTypeBase(io, TypeBase)) + return FALSE; - cmsSignalError(LCMS_ERRC_ABORTED, "Seek error; probably corrupted file"); - return TRUE; + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) TypeBase); + cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String); + return FALSE; + } + } + + + Icc -> TagSizes[i] = (io ->UsedSpace - Begin); + + // Align to 32 bit boundary. + if (! _cmsWriteAlignment(io)) + return FALSE; + } + + + return TRUE; +} + + +// Fill the offset and size fields for all linked tags +static +cmsBool SetLinks( _cmsICCPROFILE* Icc) +{ + cmsUInt32Number i; + + for (i=0; i < Icc -> TagCount; i++) { + + cmsTagSignature lnk = Icc ->TagLinked[i]; + if (lnk != (cmsTagSignature) 0) { + + int j = _cmsSearchTag(Icc, lnk, FALSE); + if (j >= 0) { + + Icc ->TagOffsets[i] = Icc ->TagOffsets[j]; + Icc ->TagSizes[i] = Icc ->TagSizes[j]; + } + + } + } + + return TRUE; +} + +// Low-level save to IOHANDLER. It returns the number of bytes used to +// store the profile, or zero on error. io may be NULL and in this case +// no data is written--only sizes are calculated +cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + _cmsICCPROFILE Keep; + cmsIOHANDLER* PrevIO = NULL; + cmsUInt32Number UsedSpace; + cmsContext ContextID; + + _cmsAssert(hProfile != NULL); + + if (!_cmsLockMutex(Icc->ContextID, Icc->UsrMutex)) return 0; + memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); + + ContextID = cmsGetProfileContextID(hProfile); + PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID); + if (PrevIO == NULL) { + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + return 0; + } + + // Pass #1 does compute offsets + + if (!_cmsWriteHeader(Icc, 0)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; + + UsedSpace = PrevIO ->UsedSpace; + + // Pass #2 does save to iohandler + + if (io != NULL) { + + Icc ->IOhandler = io; + if (!SetLinks(Icc)) goto Error; + if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; + } + + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + if (!cmsCloseIOhandler(PrevIO)) + UsedSpace = 0; // As a error marker + + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + + return UsedSpace; + + +Error: + cmsCloseIOhandler(PrevIO); + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + + return 0; +} + + +// Low-level save to disk. +cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w"); + cmsBool rc; + + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + if (rc == FALSE) { // remove() is C99 per 7.19.4.1 + remove(FileName); // We have to IGNORE return value in this case + } + return rc; +} + +// Same as anterior, but for streams +cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream) +{ + cmsBool rc; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream); + + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + return rc; +} + + +// Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only +cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded) +{ + cmsBool rc; + cmsIOHANDLER* io; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + _cmsAssert(BytesNeeded != NULL); + + // Should we just calculate the needed space? + if (MemPtr == NULL) { + + *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); + return (*BytesNeeded == 0) ? FALSE : TRUE; + } + + // That is a real write operation + io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w"); + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + return rc; +} + + + +// Closes a profile freeing any involved resources +cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsBool rc = TRUE; + cmsUInt32Number i; + + if (!Icc) return FALSE; + + // Was open in write mode? + if (Icc ->IsWrite) { + + Icc ->IsWrite = FALSE; // Assure no further writing + rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile); + } + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc -> TagPtrs[i]) { + + cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; + + if (TypeHandler != NULL) { + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + + LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters + LocalTypeHandler.ICCVersion = Icc ->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); + } + else + _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); + } + } + + if (Icc ->IOhandler != NULL) { + rc &= cmsCloseIOhandler(Icc->IOhandler); + } + + _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex); + + _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory + + return rc; +} + + +// ------------------------------------------------------------------------------------------------------------------- + + +// Returns TRUE if a given tag is supported by a plug-in +static +cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type) +{ + cmsUInt32Number i, nMaxTypes; + + nMaxTypes = TagDescriptor->nSupportedTypes; + if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN) + nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN; + + for (i=0; i < nMaxTypes; i++) { + if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE; } return FALSE; } -static -size_t FileTell(struct _lcms_iccprofile_struct* Icc) -{ - return ftell((FILE*) Icc ->stream); -} - -// Writes data to stream, also keeps used space for further reference - - -static -LCMSBOOL FileWrite(struct _lcms_iccprofile_struct* Icc, size_t size, LPVOID Ptr) +// That's the main read function +void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) { - if (size == 0) return TRUE; - - Icc->UsedSpace += size; - - if (Icc->stream == NULL) { + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsTagTypeHandler* TypeHandler; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor; + cmsTagTypeSignature BaseType; + cmsUInt32Number Offset, TagSize; + cmsUInt32Number ElemCount; + int n; - return TRUE; - } + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; - return (fwrite(Ptr, size, 1, (FILE*) Icc->stream) == 1); -} + n = _cmsSearchTag(Icc, sig, TRUE); + if (n < 0) goto Error; // Not found, return NULL -static -LCMSBOOL FileGrow(struct _lcms_iccprofile_struct* Icc, size_t size) -{ - return TRUE; -} + // If the element is already in memory, return the pointer + if (Icc -> TagPtrs[n]) { + + if (Icc->TagTypeHandlers[n] == NULL) goto Error; + + // Sanity check + BaseType = Icc->TagTypeHandlers[n]->Signature; + if (BaseType == 0) goto Error; + + TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig); + if (TagDescriptor == NULL) goto Error; + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; + + if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc -> TagPtrs[n]; + } + + // We need to read it. Get the offset and size to the file + Offset = Icc -> TagOffsets[n]; + TagSize = Icc -> TagSizes[n]; + + if (TagSize < 8) goto Error; -static -LCMSBOOL FileClose(struct _lcms_iccprofile_struct* Icc) -{ - return fclose((FILE*) Icc ->stream); -} + // Seek to its location + if (!io -> Seek(io, Offset)) + goto Error; + + // Search for support on this tag + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, sig); -// ---------------------------------------------------------------------------------------------------- + // An unknown element was found. + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String); + goto Error; // Unsupported. + } + + // if supported, get type and check if in list + BaseType = _cmsReadTypeBase(io); + if (BaseType == 0) goto Error; + + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; + + TagSize -= 8; // Alredy read by the type base logic + + // Get type handler + TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); + if (TypeHandler == NULL) goto Error; + LocalTypeHandler = *TypeHandler; -// Creates an empty structure holding all required parameters + // Read the tag + Icc -> TagTypeHandlers[n] = TypeHandler; + + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize); -cmsHPROFILE _cmsCreateProfilePlaceholder(void) -{ + // The tag type is supported, but something wrong happened and we cannot read the tag. + // let know the user about this (although it is just a warning) + if (Icc -> TagPtrs[n] == NULL) { - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) _cmsMalloc(sizeof(LCMSICCPROFILE)); - if (Icc == NULL) return NULL; + char String[5]; + + _cmsTagSignature2String(String, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); + goto Error; + } - // Empty values - ZeroMemory(Icc, sizeof(LCMSICCPROFILE)); + // This is a weird error that may be a symptom of something more serious, the number of + // stored item is actually less than the number of required elements. + if (ElemCount < TagDescriptor ->ElemCount) { - // Make sure illuminant is correct - Icc ->Illuminant = *cmsD50_XYZ(); + char String[5]; + + _cmsTagSignature2String(String, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", + String, TagDescriptor ->ElemCount, ElemCount); + } - // Set it to empty - Icc -> TagCount = 0; + + // Return the data + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc -> TagPtrs[n]; - // Return the handle - return (cmsHPROFILE) Icc; + + // Return error and unlock tha data +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return NULL; } -// Return the number of tags -icInt32Number LCMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return Icc->TagCount; -} - -// Return the tag signature of a given tag number -icTagSignature LCMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, icInt32Number n) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - - if (n < 0 || n > Icc->TagCount) return (icTagSignature) 0; // Mark as not available - - return Icc ->TagNames[n]; -} - - -// Search for a specific tag in tag dictionary -// Returns position or -1 if tag not found - -icInt32Number _cmsSearchTag(LPLCMSICCPROFILE Profile, icTagSignature sig, LCMSBOOL lSignalError) -{ - icInt32Number i; - - if (sig == 0) return -1; // 0 identifies a special tag holding raw memory. - - for (i=0; i < Profile -> TagCount; i++) { - - if (sig == Profile -> TagNames[i]) - return i; - } - - if (lSignalError) - cmsSignalError(LCMS_ERRC_ABORTED, "Tag '%lx' not found", sig); - - return -1; -} - - -// Check existance - -LCMSBOOL LCMSEXPORT cmsIsTag(cmsHPROFILE hProfile, icTagSignature sig) +// Get true type of data +cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) { - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - return _cmsSearchTag(Icc, sig, FALSE) >= 0; -} - - - -// Search for a particular tag, replace if found or add new one else - -LPVOID _cmsInitTag(LPLCMSICCPROFILE Icc, icTagSignature sig, size_t size, const void* Init) -{ - LPVOID Ptr; - icInt32Number i; - - i = _cmsSearchTag(Icc, sig, FALSE); - - if (i >=0) { - - if (Icc -> TagPtrs[i]) _cmsFree(Icc -> TagPtrs[i]); - } - else { + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsTagTypeHandler* TypeHandler; + int n; - i = Icc -> TagCount; - Icc -> TagCount++; - - if (Icc ->TagCount >= MAX_TABLE_TAG) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Too many tags (%d)", MAX_TABLE_TAG); - Icc ->TagCount = MAX_TABLE_TAG-1; - return NULL; - } - } + // Search for given tag in ICC profile directory + n = _cmsSearchTag(Icc, sig, TRUE); + if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL - - Ptr = _cmsMalloc(size); - if (Ptr == NULL) return NULL; - - CopyMemory(Ptr, Init, size); - - Icc ->TagNames[i] = sig; - Icc ->TagSizes[i] = size; - Icc ->TagPtrs[i] = Ptr; - - return Ptr; + // Get the handler. The true type is there + TypeHandler = Icc -> TagTypeHandlers[n]; + return TypeHandler ->Signature; } - - - -// Creates a profile from file read placeholder - -LPLCMSICCPROFILE _cmsCreateProfileFromFilePlaceholder(const char* FileName) +// Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already +// in that list, the previous version is deleted. +cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) { - LPLCMSICCPROFILE NewIcc; - LPVOID ICCfile = FileOpen(FileName); + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsTagTypeHandler* TypeHandler = NULL; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor = NULL; + cmsTagTypeSignature Type; + int i; + cmsFloat64Number Version; + char TypeString[5], SigString[5]; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; - if (ICCfile == NULL) { + // To delete tags. + if (data == NULL) { + + // Delete the tag + i = _cmsSearchTag(Icc, sig, FALSE); + if (i >= 0) { - cmsSignalError(LCMS_ERRC_ABORTED, "File '%s' not found", FileName); - return NULL; + // Use zero as a mark of deleted + _cmsDeleteTagByPos(Icc, i); + Icc ->TagNames[i] = (cmsTagSignature) 0; + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; + } + // Didn't find the tag + goto Error; } - NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder(); - if (NewIcc == NULL) return NULL; - - strncpy(NewIcc -> PhysicalFile, FileName, MAX_PATH-1); - NewIcc -> PhysicalFile[MAX_PATH-1] = 0; - - NewIcc ->stream = ICCfile; + if (!_cmsNewTag(Icc, sig, &i)) goto Error; - NewIcc ->Read = FileRead; - NewIcc ->Seek = FileSeek; - NewIcc ->Tell = FileTell; - NewIcc ->Close = FileClose; - NewIcc ->Grow = FileGrow; - NewIcc ->Write = NULL; - - NewIcc ->IsWrite = FALSE; - - + // This is not raw + Icc ->TagSaveAsRaw[i] = FALSE; - - return NewIcc; -} - - -// Creates a profile from memory read placeholder + // This is not a link + Icc ->TagLinked[i] = (cmsTagSignature) 0; -LPLCMSICCPROFILE _cmsCreateProfileFromMemPlaceholder(LPVOID MemPtr, DWORD dwSize) -{ - - LPLCMSICCPROFILE NewIcc; - LPVOID ICCfile = MemoryOpen((LPBYTE) MemPtr, (size_t) dwSize, 'r'); - - - if (ICCfile == NULL) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't allocate %ld bytes for profile", dwSize); - return NULL; + // Get information about the TAG. + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL){ + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); + goto Error; } - NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder(); - if (NewIcc == NULL) return NULL; - - NewIcc -> PhysicalFile[0] = 0; - NewIcc ->stream = ICCfile; + // Now we need to know which type to use. It depends on the version. + Version = cmsGetProfileVersion(hProfile); - NewIcc ->Read = MemoryRead; - NewIcc ->Seek = MemorySeek; - NewIcc ->Tell = MemoryTell; - NewIcc ->Close = MemoryClose; - NewIcc ->Grow = MemoryGrow; - NewIcc ->Write = MemoryWrite; + if (TagDescriptor ->DecideType != NULL) { - NewIcc ->IsWrite = FALSE; - - - return NewIcc; -} - + // Let the tag descriptor to decide the type base on depending on + // the data. This is useful for example on parametric curves, where + // curves specified by a table cannot be saved as parametric and needs + // to be casted to single v2-curves, even on v4 profiles. -// Turn a placeholder into file writter - -void _cmsSetSaveToDisk(LPLCMSICCPROFILE Icc, const char* FileName) -{ - - if (FileName == NULL) { - - Icc ->stream = NULL; + Type = TagDescriptor ->DecideType(Version, data); } else { - Icc ->stream = fopen(FileName, "wb"); - if (Icc ->stream == NULL) - cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to file '%s'", FileName); + Type = TagDescriptor ->SupportedTypes[0]; } - Icc ->Write = FileWrite; // Save to disk - Icc ->Close = FileClose; -} + // Does the tag support this type? + if (!IsTypeSupported(TagDescriptor, Type)) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); + goto Error; + } + // Does we have a handler for this type? + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); + if (TypeHandler == NULL) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); + goto Error; // Should never happen + } -// Turn a placeholder into memory writter - -void _cmsSetSaveToMemory(LPLCMSICCPROFILE Icc, LPVOID MemPtr, size_t dwSize) -{ - - if (MemPtr == NULL) { + // Fill fields on icc structure + Icc ->TagTypeHandlers[i] = TypeHandler; + Icc ->TagNames[i] = sig; + Icc ->TagSizes[i] = 0; + Icc ->TagOffsets[i] = 0; - Icc ->stream = NULL; - } - else { + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); - Icc ->stream = (FILEMEM*) MemoryOpen((LPBYTE) MemPtr, dwSize, 'w'); - if (Icc ->stream == NULL) - cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to memory"); + if (Icc ->TagPtrs[i] == NULL) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); + + goto Error; } - Icc ->Write = MemoryWrite; - Icc ->Close = MemoryClose; -} - - -// ----------------------------------------------------------------------- Set/Get several struct members - - - + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; -LCMSBOOL LCMSEXPORT cmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - *Dest = Icc -> MediaWhitePoint; - return TRUE; -} +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; - -LCMSBOOL LCMSEXPORT cmsTakeMediaBlackPoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - *Dest = Icc -> MediaBlackPoint; - return TRUE; } -LCMSBOOL LCMSEXPORT cmsTakeIluminant(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - *Dest = Icc -> Illuminant; - return TRUE; -} +// Read and write raw data. The only way those function would work and keep consistence with normal read and write +// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained +// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where +// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows +// to write a tag as raw data and the read it as handled. -int LCMSEXPORT cmsTakeRenderingIntent(cmsHPROFILE hProfile) +cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) { - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return (int) Icc -> RenderingIntent; -} + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + void *Object; + int i; + cmsIOHANDLER* MemIO; + cmsTagTypeHandler* TypeHandler = NULL; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor = NULL; + cmsUInt32Number rc; + cmsUInt32Number Offset, TagSize; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + // Search for given tag in ICC profile directory + i = _cmsSearchTag(Icc, sig, TRUE); + if (i < 0) goto Error; // Not found, + + // It is already read? + if (Icc -> TagPtrs[i] == NULL) { + + // No yet, get original position + Offset = Icc ->TagOffsets[i]; + TagSize = Icc ->TagSizes[i]; + + // read the data directly, don't keep copy + if (data != NULL) { + + if (BufferSize < TagSize) + TagSize = BufferSize; + + if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; + if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TagSize; + } + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc ->TagSizes[i]; + } + + // The data has been already read, or written. But wait!, maybe the user choosed to save as + // raw data. In this case, return the raw data directly + if (Icc ->TagSaveAsRaw[i]) { + + if (data != NULL) { + + TagSize = Icc ->TagSizes[i]; + if (BufferSize < TagSize) + TagSize = BufferSize; + + memmove(data, Icc ->TagPtrs[i], TagSize); + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TagSize; + } -void LCMSEXPORT cmsSetRenderingIntent(cmsHPROFILE hProfile, int RenderingIntent) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - Icc -> RenderingIntent = (icRenderingIntent) RenderingIntent; -} + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc ->TagSizes[i]; + } + + // Already readed, or previously set by cmsWriteTag(). We need to serialize that + // data to raw in order to maintain consistency. + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + Object = cmsReadTag(hProfile, sig); + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + if (Object == NULL) goto Error; + + // Now we need to serialize to a memory block: just use a memory iohandler + + if (data == NULL) { + MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); + } else{ + MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); + } + if (MemIO == NULL) goto Error; + // Obtain type handling for the tag + TypeHandler = Icc ->TagTypeHandlers[i]; + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL) { + cmsCloseIOhandler(MemIO); + goto Error; + } -DWORD LCMSEXPORT cmsTakeHeaderFlags(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return (DWORD) Icc -> flags; + if (TypeHandler == NULL) goto Error; + + // Serialize + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + + if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + // Get Size and close + rc = MemIO ->Tell(MemIO); + cmsCloseIOhandler(MemIO); // Ignore return code this time + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return rc; + +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return 0; } -void LCMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, DWORD Flags) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - Icc -> flags = (icUInt32Number) Flags; -} - -DWORD LCMSEXPORT cmsTakeHeaderAttributes(cmsHPROFILE hProfile) +// Similar to the anterior. This function allows to write directly to the ICC profile any data, without +// checking anything. As a rule, mixing Raw with cooked doesn't work, so writing a tag as raw and then reading +// it as cooked without serializing does result into an error. If that is what you want, you will need to dump +// the profile to memry or disk and then reopen it. +cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) { - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return (DWORD) Icc -> attributes; -} + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + if (!_cmsNewTag(Icc, sig, &i)) { + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } -void LCMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, DWORD Flags) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - Icc -> attributes = (icUInt32Number) Flags; + // Mark the tag as being written as RAW + Icc ->TagSaveAsRaw[i] = TRUE; + Icc ->TagNames[i] = sig; + Icc ->TagLinked[i] = (cmsTagSignature) 0; + + // Keep a copy of the block + Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); + Icc ->TagSizes[i] = Size; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + + if (Icc->TagPtrs[i] == NULL) { + Icc->TagNames[i] = (cmsTagSignature) 0; + return FALSE; + } + return TRUE; } - -const BYTE* LCMSEXPORT cmsTakeProfileID(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return Icc ->ProfileID; -} - -void LCMSEXPORT cmsSetProfileID(cmsHPROFILE hProfile, LPBYTE ProfileID) +// Using this function you can collapse several tag entries to the same block in the profile +cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) { - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - CopyMemory(Icc -> ProfileID, ProfileID, 16); -} + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; + if (!_cmsNewTag(Icc, sig, &i)) { + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } -LCMSBOOL LCMSEXPORT cmsTakeCreationDateTime(struct tm *Dest, cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - CopyMemory(Dest, &Icc ->Created, sizeof(struct tm)); + // Keep necessary information + Icc ->TagSaveAsRaw[i] = FALSE; + Icc ->TagNames[i] = sig; + Icc ->TagLinked[i] = dest; + + Icc ->TagPtrs[i] = NULL; + Icc ->TagSizes[i] = 0; + Icc ->TagOffsets[i] = 0; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return TRUE; } -icColorSpaceSignature LCMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return Icc -> PCS; -} - - -void LCMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, icColorSpaceSignature pcs) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - Icc -> PCS = pcs; -} - -icColorSpaceSignature LCMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return Icc -> ColorSpace; -} - -void LCMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, icColorSpaceSignature sig) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - Icc -> ColorSpace = sig; -} - -icProfileClassSignature LCMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return Icc -> DeviceClass; -} - -DWORD LCMSEXPORT cmsGetProfileICCversion(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - return (DWORD) Icc -> Version; -} - -void LCMSEXPORT cmsSetProfileICCversion(cmsHPROFILE hProfile, DWORD Version) +// Returns the tag linked to sig, in the case two tags are sharing same resource +cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig) { - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - Icc -> Version = Version; -} - - -void LCMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, icProfileClassSignature sig) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile; - Icc -> DeviceClass = sig; -} - - -// -------------------------------------------------------------------------------------------------------------- - - -static -int SizeOfGammaTab(LPGAMMATABLE In) -{ - return sizeof(GAMMATABLE) + (In -> nEntries - 1)*sizeof(WORD); -} - - -// Creates a phantom tag holding a memory block - -static -LPVOID DupBlock(LPLCMSICCPROFILE Icc, LPVOID Block, size_t size) -{ - if (Block != NULL && size > 0) - return _cmsInitTag(Icc, (icTagSignature) 0, size, Block); - else - return NULL; - -} - -// This is tricky, since LUT structs does have pointers - -LCMSBOOL LCMSEXPORT _cmsAddLUTTag(cmsHPROFILE hProfile, icTagSignature sig, const void* lut) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - LPLUT Orig, Stored; - unsigned int i; - - // The struct itself - - Orig = (LPLUT) lut; - Stored = (LPLUT) _cmsInitTag(Icc, (icTagSignature) sig, sizeof(LUT), lut); - - // dup' the memory blocks - for (i=0; i < Orig ->InputChan; i++) - Stored -> L1[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L1[i], - sizeof(WORD) * Orig ->In16params.nSamples); + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; - for (i=0; i < Orig ->OutputChan; i++) - Stored -> L2[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L2[i], - sizeof(WORD) * Orig ->Out16params.nSamples); - - Stored -> T = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->T, Orig -> Tsize); - - // Zero any additional pointer - Stored ->CLut16params.p8 = NULL; - return TRUE; -} - - -LCMSBOOL LCMSEXPORT _cmsAddXYZTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* XYZ) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, sizeof(cmsCIEXYZ), XYZ); - return TRUE; -} - - -LCMSBOOL LCMSEXPORT _cmsAddTextTag(cmsHPROFILE hProfile, icTagSignature sig, const char* Text) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, strlen(Text)+1, (LPVOID) Text); - return TRUE; -} - -LCMSBOOL LCMSEXPORT _cmsAddGammaTag(cmsHPROFILE hProfile, icTagSignature sig, LPGAMMATABLE TransferFunction) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, SizeOfGammaTab(TransferFunction), TransferFunction); - return TRUE; -} - - -LCMSBOOL LCMSEXPORT _cmsAddChromaticityTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIExyYTRIPLE Chrm) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, sizeof(cmsCIExyYTRIPLE), Chrm); - return TRUE; -} - + // Search for given tag in ICC profile directory + i = _cmsSearchTag(Icc, sig, FALSE); + if (i < 0) return (cmsTagSignature) 0; // Not found, return 0 -LCMSBOOL LCMSEXPORT _cmsAddSequenceDescriptionTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsSEQ pseq) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, sizeof(int) + pseq -> n * sizeof(cmsPSEQDESC), pseq); - return TRUE; - -} - - -LCMSBOOL LCMSEXPORT _cmsAddNamedColorTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc); - return TRUE; + return Icc -> TagLinked[i]; } - - -LCMSBOOL LCMSEXPORT _cmsAddDateTimeTag(cmsHPROFILE hProfile, icTagSignature sig, struct tm *DateTime) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, sizeof(struct tm), DateTime); - return TRUE; -} - - -LCMSBOOL LCMSEXPORT _cmsAddColorantTableTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc); - return TRUE; -} - - -LCMSBOOL LCMSEXPORT _cmsAddChromaticAdaptationTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* mat) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - _cmsInitTag(Icc, sig, 3*sizeof(cmsCIEXYZ), mat); - return TRUE; - -} - - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsio1.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsio1.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsio1.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,1860 +49,107 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// ICC profile serialization - - -#include "lcms.h" - -// ----------------------------------------------------------------- Tag Serialization - -// Alignment of ICC file format uses 4 bytes DWORD - -#define ALIGNLONG(x) (((x)+3) & ~(3)) // Aligns to DWORD boundary - - -static int GlobalLanguageCode; // Language & country descriptors, for ICC 4.0 support -static int GlobalCountryCode; - - -#ifdef __BEOS__ -# define USE_CUSTOM_SWAB 1 -#endif - - -#ifdef USE_CUSTOM_SWAB - -// Replacement to swab function, thanks to YNOP -// for providing the BeOS port // -// from: @(#)swab.c 5.10 (Berkeley) 3/6/91 - -static -void xswab(const void *from, void *to, size_t len) -{ - register unsigned long temp; - register int n; - register char *fp, *tp; - - n = (len >> 1) + 1; - fp = (char *)from; - tp = (char *)to; -#define STEP temp = *fp++,*tp++ = *fp++,*tp++ = temp - /* round to multiple of 8 */ - while ((--n) & 07) - STEP; - n >>= 3; - while (--n >= 0) { - - STEP; STEP; STEP; STEP; - STEP; STEP; STEP; STEP; - } -#undef STEP -} -#else -#define xswab swab -#endif - - -// -// Little-Endian to Big-Endian +//--------------------------------------------------------------------------------- // -#ifdef USE_BIG_ENDIAN -#define AdjustEndianess16(a) -#define AdjustEndianess32(a) -#define AdjustEndianessArray16(a, b) -#else - -static -void AdjustEndianess16(LPBYTE pByte) -{ - BYTE tmp; - - tmp = pByte[0]; - pByte[0] = pByte[1]; - pByte[1] = tmp; -} - -static -void AdjustEndianess32(LPBYTE pByte) -{ - BYTE temp1; - BYTE temp2; - - temp1 = *pByte++; - temp2 = *pByte++; - *(pByte-1) = *pByte; - *pByte++ = temp2; - *(pByte-3) = *pByte; - *pByte = temp1; -} - - -// swap bytes in a array of words - -static -void AdjustEndianessArray16(LPWORD p, size_t num_words) -{ - xswab((char*) p, (char*)p, (int) num_words * sizeof(WORD)); -} - -#endif - - -// Transports to properly encoded values - note that icc profiles does use -// big endian notation. - -static -icInt32Number TransportValue32(icInt32Number Value) -{ - icInt32Number Temp = Value; +#include "lcms2_internal.h" - AdjustEndianess32((LPBYTE) &Temp); - return Temp; -} - -static -WORD TransportValue16(WORD Value) -{ - WORD Temp = Value; - - AdjustEndianess16((LPBYTE) &Temp); - return Temp; -} - - -// from Fixed point 8.8 to double - -static -double Convert8Fixed8(WORD fixed8) -{ - BYTE msb, lsb; - - lsb = (BYTE) (fixed8 & 0xff); - msb = (BYTE) (((WORD) fixed8 >> 8) & 0xff); - - return (double) ((double) msb + ((double) lsb / 256.0)); -} - - -// from Fixed point 15.16 to double -static -double Convert15Fixed16(icS15Fixed16Number fix32) -{ - double floater, sign, mid, hack; - int Whole, FracPart; - - - AdjustEndianess32((LPBYTE) &fix32); - - sign = (fix32 < 0 ? -1 : 1); - fix32 = abs(fix32); - - Whole = LOWORD(fix32 >> 16); - FracPart = LOWORD(fix32 & 0x0000ffffL); - - hack = 65536.0; - mid = (double) FracPart / hack; - floater = (double) Whole + mid; - - return sign * floater; -} - - -// Auxiliar-- read base and return type +// Read tags using low-level functions, provides necessary glue code to adapt versions, etc. -static -icTagTypeSignature ReadBase(LPLCMSICCPROFILE Icc) -{ - icTagBase Base; - - if (Icc -> Read(&Base, sizeof(icTagBase), 1, Icc) != 1) - return (icTagTypeSignature) 0; - AdjustEndianess32((LPBYTE) &Base.sig); - - return Base.sig; -} - - -static -void DecodeDateTimeNumber(const icDateTimeNumber *Source, struct tm *Dest) -{ - Dest->tm_sec = TransportValue16(Source->seconds); - Dest->tm_min = TransportValue16(Source->minutes); - Dest->tm_hour = TransportValue16(Source->hours); - Dest->tm_mday = TransportValue16(Source->day); - Dest->tm_mon = TransportValue16(Source->month) - 1; - Dest->tm_year = TransportValue16(Source->year) - 1900; - Dest->tm_wday = -1; - Dest->tm_yday = -1; - Dest->tm_isdst = 0; -} - -static -void EncodeDateTimeNumber(icDateTimeNumber *Dest, const struct tm *Source) -{ - Dest->seconds = TransportValue16((WORD) Source->tm_sec); - Dest->minutes = TransportValue16((WORD) Source->tm_min); - Dest->hours = TransportValue16((WORD) Source->tm_hour); - Dest->day = TransportValue16((WORD) Source->tm_mday); - Dest->month = TransportValue16((WORD) (Source->tm_mon + 1)); - Dest->year = TransportValue16((WORD) (Source->tm_year + 1900)); -} - - -// Jun-21-2000: Some profiles (those that comes with W2K) comes -// with the media white (media black?) x 100. Add a sanity check - -static -void NormalizeXYZ(LPcmsCIEXYZ Dest) -{ - while (Dest -> X > 2. && - Dest -> Y > 2. && - Dest -> Z > 2.) { - - Dest -> X /= 10.; - Dest -> Y /= 10.; - Dest -> Z /= 10.; - } -} - -// Evaluates a XYZ tristimulous across chromatic adaptation matrix - -static -void EvalCHRM(LPcmsCIEXYZ Dest, LPMAT3 Chrm, LPcmsCIEXYZ Src) -{ - VEC3 d, s; - - s.n[VX] = Src -> X; - s.n[VY] = Src -> Y; - s.n[VZ] = Src -> Z; - - MAT3eval(&d, Chrm, &s); - - Dest ->X = d.n[VX]; - Dest ->Y = d.n[VY]; - Dest ->Z = d.n[VZ]; - -} - - -// Read profile header and validate it - -static -LPLCMSICCPROFILE ReadHeader(LPLCMSICCPROFILE Icc, LCMSBOOL lIsFromMemory) -{ - icTag Tag; - icHeader Header; - icInt32Number TagCount, i; - icUInt32Number extent; - - if (Icc -> Read(&Header, sizeof(icHeader), 1, Icc) != 1) - goto ErrorCleanup; - - // Convert endian - - AdjustEndianess32((LPBYTE) &Header.size); - AdjustEndianess32((LPBYTE) &Header.cmmId); - AdjustEndianess32((LPBYTE) &Header.version); - AdjustEndianess32((LPBYTE) &Header.deviceClass); - AdjustEndianess32((LPBYTE) &Header.colorSpace); - AdjustEndianess32((LPBYTE) &Header.pcs); - AdjustEndianess32((LPBYTE) &Header.magic); - AdjustEndianess32((LPBYTE) &Header.flags); - AdjustEndianess32((LPBYTE) &Header.attributes[0]); - AdjustEndianess32((LPBYTE) &Header.renderingIntent); - - // Validate it - - if (Header.magic != icMagicNumber) goto ErrorCleanup; - - if (Icc ->Read(&TagCount, sizeof(icInt32Number), 1, Icc) != 1) - goto ErrorCleanup; - - AdjustEndianess32((LPBYTE) &TagCount); +// LUT tags +static const cmsTagSignature Device2PCS16[] = {cmsSigAToB0Tag, // Perceptual + cmsSigAToB1Tag, // Relative colorimetric + cmsSigAToB2Tag, // Saturation + cmsSigAToB1Tag }; // Absolute colorimetric - Icc -> DeviceClass = Header.deviceClass; - Icc -> ColorSpace = Header.colorSpace; - Icc -> PCS = Header.pcs; - Icc -> RenderingIntent = (icRenderingIntent) Header.renderingIntent; - Icc -> flags = Header.flags; - Icc -> attributes = Header.attributes[0]; - Icc -> Illuminant.X = Convert15Fixed16(Header.illuminant.X); - Icc -> Illuminant.Y = Convert15Fixed16(Header.illuminant.Y); - Icc -> Illuminant.Z = Convert15Fixed16(Header.illuminant.Z); - Icc -> Version = Header.version; - - // Get creation date/time - - DecodeDateTimeNumber(&Header.date, &Icc ->Created); - - // Fix illuminant, some profiles are broken in this field! - - Icc ->Illuminant = *cmsD50_XYZ(); - - // The profile ID are 16 raw bytes - - CopyMemory(Icc ->ProfileID, Header.reserved, 16); - - // Get rid of possible wrong profiles - - NormalizeXYZ(&Icc -> Illuminant); - - // Read tag directory - - if (TagCount > MAX_TABLE_TAG || TagCount < 0) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Too many tags (%d)", TagCount); - goto ErrorCleanup; - } - - Icc -> TagCount = TagCount; - for (i=0; i < TagCount; i++) { - - if (Icc ->Read(&Tag, sizeof(icTag), 1, Icc) != 1) - goto ErrorCleanup; - - AdjustEndianess32((LPBYTE) &Tag.offset); - AdjustEndianess32((LPBYTE) &Tag.size); - AdjustEndianess32((LPBYTE) &Tag.sig); // Signature - - // Perform some sanity check. Offset + size should fall inside file. - extent = Tag.offset + Tag.size; - if (extent > Header.size || extent < Tag.offset) - goto ErrorCleanup; - - Icc -> TagNames[i] = Tag.sig; - Icc -> TagOffsets[i] = Tag.offset; - Icc -> TagSizes[i] = Tag.size; - } - - return Icc; - - -ErrorCleanup: - - Icc ->Close(Icc); - - if (lIsFromMemory) - cmsSignalError(LCMS_ERRC_ABORTED, "Corrupted memory profile"); - else - cmsSignalError(LCMS_ERRC_ABORTED, "Corrupted profile: '%s'", Icc->PhysicalFile); - - - _cmsFree(Icc); - return NULL; -} - -static -unsigned int uipow(unsigned int a, unsigned int b) { - unsigned int rv = 1; - for (; b > 0; b--) - rv *= a; - return rv; -} - - - -// Convert between notations. - -#define TO16_TAB(x) (WORD) (((x) << 8) | (x)) - - -// LUT8 can come only in Lab space. There is a fatal flaw in -// converting from Lut8 to Lut16. Due to particular encoding -// of Lab, different actions should be taken from input and -// output Lab8 LUTS. For input, is as easy as applying a << 8, -// since numbers comes in fixed point. However, for output LUT -// things goes a bit more complex.... LUT 16 is supposed to -// have a domain of 0..ff00, so we should remap the LUT in order -// to get things working. Affected signatures are B2Axx tags, -// preview and gamut. - -// I do solve it by multiplying input matrix by: -// -// | 0xffff/0xff00 0 0 | -// | 0 0xffff/0xff00 0 | -// | 0 0 0xffff/0xff00 | -// -// The input values got then remapped to adequate domain - -static -void FixLUT8(LPLUT Lut, icTagSignature sig, size_t nTabSize) -{ - MAT3 Fixup, Original, Result; - LPWORD PtrW; - size_t i; - - switch (sig) { - +static const cmsTagSignature Device2PCSFloat[] = {cmsSigDToB0Tag, // Perceptual + cmsSigDToB1Tag, // Relative colorimetric + cmsSigDToB2Tag, // Saturation + cmsSigDToB3Tag }; // Absolute colorimetric - case icSigBToA0Tag: - case icSigBToA1Tag: - case icSigBToA2Tag: - case icSigGamutTag: - case icSigPreview0Tag: - case icSigPreview1Tag: - case icSigPreview2Tag: - - - VEC3init(&Fixup.v[0], (double) 0xFFFF/0xFF00, 0, 0); - VEC3init(&Fixup.v[1], 0, (double) 0xFFFF/0xFF00, 0); - VEC3init(&Fixup.v[2], 0, 0, (double) 0xFFFF/0xFF00); - - - MAT3fromFix(&Original, &Lut->Matrix); - MAT3per(&Result, &Original, &Fixup); - MAT3toFix(&Lut->Matrix, &Result); - - Lut -> wFlags |= LUT_HASMATRIX; - break; - - // For input, clear low part since this has to be - // Lab in fixed point - - default: - - PtrW = Lut -> T; - for (i = 0; i < nTabSize; i++) { - - *PtrW++ &= 0xFF00; - } - } - -} - -// On Lab -> Lab abstract or Lab identities, fix both sides of LUT - -static -void FixLUT8bothSides(LPLUT Lut, size_t nTabSize) -{ - MAT3 Fixup, Original, Result; - LPWORD PtrW; - size_t i; - - VEC3init(&Fixup.v[0], (double) 0xFFFF/0xFF00, 0, 0); - VEC3init(&Fixup.v[1], 0, (double) 0xFFFF/0xFF00, 0); - VEC3init(&Fixup.v[2], 0, 0, (double) 0xFFFF/0xFF00); - - MAT3fromFix(&Original, &Lut->Matrix); - MAT3per(&Result, &Original, &Fixup); - MAT3toFix(&Lut->Matrix, &Result); - - Lut -> wFlags |= LUT_HASMATRIX; - - PtrW = Lut -> T; - for (i = 0; i < nTabSize; i++) { - - *PtrW++ &= 0xFF00; - } +static const cmsTagSignature PCS2Device16[] = {cmsSigBToA0Tag, // Perceptual + cmsSigBToA1Tag, // Relative colorimetric + cmsSigBToA2Tag, // Saturation + cmsSigBToA1Tag }; // Absolute colorimetric -} - - -// The infamous LUT 8 - -static -LCMSBOOL ReadLUT8(LPLCMSICCPROFILE Icc, LPLUT NewLUT, icTagSignature sig) -{ - icLut8 LUT8; - LPBYTE Temp; - size_t nTabSize; - unsigned int i, j; - unsigned int AllLinear; - LPWORD PtrW; - - if (Icc ->Read(&LUT8, sizeof(icLut8) - SIZEOF_UINT8_ALIGNED, 1, Icc) != 1) return FALSE; - - NewLUT -> wFlags = LUT_HASTL1|LUT_HASTL2|LUT_HAS3DGRID; - NewLUT -> cLutPoints = LUT8.clutPoints; - NewLUT -> InputChan = LUT8.inputChan; - NewLUT -> OutputChan = LUT8.outputChan; - NewLUT -> InputEntries = 256; - NewLUT -> OutputEntries = 256; - - // Do some checking - if (!_cmsValidateLUT(NewLUT)) { - return FALSE; - } - - AdjustEndianess32((LPBYTE) &LUT8.e00); - AdjustEndianess32((LPBYTE) &LUT8.e01); - AdjustEndianess32((LPBYTE) &LUT8.e02); - AdjustEndianess32((LPBYTE) &LUT8.e10); - AdjustEndianess32((LPBYTE) &LUT8.e11); - AdjustEndianess32((LPBYTE) &LUT8.e12); - AdjustEndianess32((LPBYTE) &LUT8.e20); - AdjustEndianess32((LPBYTE) &LUT8.e21); - AdjustEndianess32((LPBYTE) &LUT8.e22); - - - // Matrix handling - - NewLUT -> Matrix.v[0].n[0] = (Fixed32) LUT8.e00; - NewLUT -> Matrix.v[0].n[1] = (Fixed32) LUT8.e01; - NewLUT -> Matrix.v[0].n[2] = (Fixed32) LUT8.e02; - NewLUT -> Matrix.v[1].n[0] = (Fixed32) LUT8.e10; - NewLUT -> Matrix.v[1].n[1] = (Fixed32) LUT8.e11; - NewLUT -> Matrix.v[1].n[2] = (Fixed32) LUT8.e12; - NewLUT -> Matrix.v[2].n[0] = (Fixed32) LUT8.e20; - NewLUT -> Matrix.v[2].n[1] = (Fixed32) LUT8.e21; - NewLUT -> Matrix.v[2].n[2] = (Fixed32) LUT8.e22; - - - // Only operates if not identity... - - if ((NewLUT -> InputChan == 3) && !MAT3isIdentity(&NewLUT -> Matrix, 0.0001)) { - - NewLUT -> wFlags |= LUT_HASMATRIX; - } +static const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag, // Perceptual + cmsSigBToD1Tag, // Relative colorimetric + cmsSigBToD2Tag, // Saturation + cmsSigBToD3Tag }; // Absolute colorimetric - // Copy input tables - - Temp = (LPBYTE) _cmsMalloc(256); - if (Temp == NULL) return FALSE; - - AllLinear = 0; - for (i=0; i < NewLUT -> InputChan; i++) { - - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * 256); - if (PtrW == NULL) { - _cmsFree(Temp); - return FALSE; - } - - NewLUT -> L1[i] = PtrW; - if (Icc ->Read(Temp, 1, 256, Icc) != 256) { - _cmsFree(Temp); - return FALSE; - } - - for (j=0; j < 256; j++) - PtrW[j] = TO16_TAB(Temp[j]); - AllLinear += cmsIsLinear(NewLUT -> L1[i], NewLUT -> InputEntries); - } - - // Linear input, so ignore full step - - if (AllLinear == NewLUT -> InputChan) { - - NewLUT -> wFlags &= ~LUT_HASTL1; - } - - _cmsFree(Temp); - - // Copy 3D CLUT - - nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints, - NewLUT->InputChan)); - - if (nTabSize > 0) { - - PtrW = (LPWORD) _cmsCalloc(sizeof(WORD), nTabSize); - if (PtrW == NULL) return FALSE; - - Temp = (LPBYTE) _cmsMalloc(nTabSize); - if (Temp == NULL) { - _cmsFree(PtrW); - return FALSE; - } - - if (Icc ->Read(Temp, 1, nTabSize, Icc) != nTabSize) { - _cmsFree(Temp); - _cmsFree(PtrW); - return FALSE; - } - - NewLUT -> T = PtrW; - NewLUT -> Tsize = (unsigned int) (nTabSize * sizeof(WORD)); - - for (i = 0; i < nTabSize; i++) { - - *PtrW++ = TO16_TAB(Temp[i]); - } - _cmsFree(Temp); - } - else { - NewLUT ->T = NULL; - NewLUT ->Tsize = 0; - NewLUT ->wFlags &= ~LUT_HAS3DGRID; - } - - - // Copy output tables - - Temp = (LPBYTE) _cmsMalloc(256); - if (Temp == NULL) { - return FALSE; - } - - AllLinear = 0; - for (i=0; i < NewLUT -> OutputChan; i++) { - - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * 256); - if (PtrW == NULL) { - _cmsFree(Temp); - return FALSE; - } - - NewLUT -> L2[i] = PtrW; - if (Icc ->Read(Temp, 1, 256, Icc) != 256) { - _cmsFree(Temp); - return FALSE; - } - - for (j=0; j < 256; j++) - PtrW[j] = TO16_TAB(Temp[j]); - AllLinear += cmsIsLinear(NewLUT -> L2[i], 256); - } - - // Linear input, so ignore full step - - if (AllLinear == NewLUT -> OutputChan) { - - NewLUT -> wFlags &= ~LUT_HASTL2; - } - - - _cmsFree(Temp); - - cmsCalcL16Params(NewLUT -> InputEntries, &NewLUT -> In16params); - cmsCalcL16Params(NewLUT -> OutputEntries, &NewLUT -> Out16params); - cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan, - NewLUT -> OutputChan, - &NewLUT -> CLut16params); - // Fixup - - if (Icc ->PCS == icSigLabData) { - - // Abstract or Lab identity - - if (Icc -> ColorSpace == icSigLabData) - - FixLUT8bothSides(NewLUT, nTabSize); - else - FixLUT8(NewLUT, sig, nTabSize); - - - // Now some additional fixup. Lab encoding on 8 bit makes - // impossible to place gray axis on a exact node. However, - // some profiles does claim to do that. Poor lcms will try - // to detect such condition and fix up "on the fly". - - switch (sig) { - - case icSigBToA0Tag: - case icSigBToA1Tag: - case icSigBToA2Tag: - case icSigGamutTag: - case icSigPreview0Tag: - case icSigPreview1Tag: - case icSigPreview2Tag: - { - LPWORD WhiteLab, ExpectedWhite; - WORD WhiteFixed[MAXCHANNELS], WhiteUnfixed[MAXCHANNELS]; - int j, nChannels; - double Dist, DistFixed, DistUnfixed; - - _cmsEndPointsBySpace(icSigLabData, &WhiteLab, NULL, NULL); +// Factors to convert from 1.15 fixed point to 0..1.0 range and vice-versa +#define InpAdj (1.0/MAX_ENCODEABLE_XYZ) // (65536.0/(65535.0*2.0)) +#define OutpAdj (MAX_ENCODEABLE_XYZ) // ((2.0*65535.0)/65536.0) - if (_cmsEndPointsBySpace(Icc -> ColorSpace, - &ExpectedWhite, NULL, &nChannels)) { - - // 1.- Find white obtained by both combinations - - NewLUT -> FixGrayAxes = FALSE; - cmsEvalLUT(NewLUT, WhiteLab, WhiteUnfixed); - - NewLUT -> FixGrayAxes = TRUE; - cmsEvalLUT(NewLUT, WhiteLab, WhiteFixed); - - // 2.- Which method gives closer white? - - DistFixed = DistUnfixed = 0; - for (j=0; j < nChannels; j++) { - - Dist = ExpectedWhite[j] - WhiteFixed[j]; - DistFixed += Dist*Dist; - Dist = ExpectedWhite[j] - WhiteUnfixed[j]; - DistUnfixed += Dist*Dist; - } - - // 3.- Decide method - - if (sqrt(DistFixed) < sqrt(DistUnfixed)) - NewLUT -> FixGrayAxes = TRUE; - else - NewLUT -> FixGrayAxes = FALSE; - } - - } - break; - - default:; - } - } - - return TRUE; -} - - - - -// Case LUT 16 - -static -LCMSBOOL ReadLUT16(LPLCMSICCPROFILE Icc, LPLUT NewLUT) -{ - icLut16 LUT16; - size_t nTabSize; - unsigned int i; - unsigned int AllLinear; - LPWORD PtrW; - - - if (Icc ->Read(&LUT16, sizeof(icLut16)- SIZEOF_UINT16_ALIGNED, 1, Icc) != 1) - return FALSE; - - NewLUT -> wFlags = LUT_HASTL1 | LUT_HASTL2 | LUT_HAS3DGRID; - NewLUT -> cLutPoints = LUT16.clutPoints; - NewLUT -> InputChan = LUT16.inputChan; - NewLUT -> OutputChan = LUT16.outputChan; - - AdjustEndianess16((LPBYTE) &LUT16.inputEnt); - AdjustEndianess16((LPBYTE) &LUT16.outputEnt); - - NewLUT -> InputEntries = LUT16.inputEnt; - NewLUT -> OutputEntries = LUT16.outputEnt; - - if (!_cmsValidateLUT(NewLUT)) { - return FALSE; - } - - // Matrix handling - - AdjustEndianess32((LPBYTE) &LUT16.e00); - AdjustEndianess32((LPBYTE) &LUT16.e01); - AdjustEndianess32((LPBYTE) &LUT16.e02); - AdjustEndianess32((LPBYTE) &LUT16.e10); - AdjustEndianess32((LPBYTE) &LUT16.e11); - AdjustEndianess32((LPBYTE) &LUT16.e12); - AdjustEndianess32((LPBYTE) &LUT16.e20); - AdjustEndianess32((LPBYTE) &LUT16.e21); - AdjustEndianess32((LPBYTE) &LUT16.e22); - - NewLUT -> Matrix.v[0].n[0] = (Fixed32) LUT16.e00; - NewLUT -> Matrix.v[0].n[1] = (Fixed32) LUT16.e01; - NewLUT -> Matrix.v[0].n[2] = (Fixed32) LUT16.e02; - NewLUT -> Matrix.v[1].n[0] = (Fixed32) LUT16.e10; - NewLUT -> Matrix.v[1].n[1] = (Fixed32) LUT16.e11; - NewLUT -> Matrix.v[1].n[2] = (Fixed32) LUT16.e12; - NewLUT -> Matrix.v[2].n[0] = (Fixed32) LUT16.e20; - NewLUT -> Matrix.v[2].n[1] = (Fixed32) LUT16.e21; - NewLUT -> Matrix.v[2].n[2] = (Fixed32) LUT16.e22; - - // Only operates if not identity... - - if ((NewLUT -> InputChan == 3) && !MAT3isIdentity(&NewLUT -> Matrix, 0.0001)) { - - NewLUT -> wFlags |= LUT_HASMATRIX; - } - - - // Copy input tables - - AllLinear = 0; - for (i=0; i < NewLUT -> InputChan; i++) { - - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> InputEntries); - if (PtrW == NULL) return FALSE; - - NewLUT -> L1[i] = PtrW; - if (Icc ->Read(PtrW, sizeof(WORD), NewLUT -> InputEntries, Icc) != NewLUT -> InputEntries) { - return FALSE; - } - - AdjustEndianessArray16(PtrW, NewLUT -> InputEntries); - AllLinear += cmsIsLinear(NewLUT -> L1[i], NewLUT -> InputEntries); - } - - // Linear input, so ignore full step - - if (AllLinear == NewLUT -> InputChan) { - - NewLUT -> wFlags &= ~LUT_HASTL1; - } - - - // Copy 3D CLUT - - nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints, - NewLUT->InputChan)); - if (nTabSize > 0) { - - PtrW = (LPWORD) _cmsCalloc(sizeof(WORD), nTabSize); - if (PtrW == NULL) - return FALSE; - - NewLUT -> T = PtrW; - NewLUT -> Tsize = (unsigned int) (nTabSize * sizeof(WORD)); - - if (Icc -> Read(PtrW, sizeof(WORD), nTabSize, Icc) != nTabSize) { - return FALSE; - } - - AdjustEndianessArray16(NewLUT -> T, nTabSize); - } - else { - NewLUT ->T = NULL; - NewLUT ->Tsize = 0; - NewLUT -> wFlags &= ~LUT_HAS3DGRID; - } +// Several resources for gray conversions. +static const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X), (InpAdj*cmsD50Y), (InpAdj*cmsD50Z) }; +static const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 }; +static const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 }; +static const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; - // Copy output tables - - AllLinear = 0; - for (i=0; i < NewLUT -> OutputChan; i++) { - - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> OutputEntries); - if (PtrW == NULL) { - return FALSE; - } - - NewLUT -> L2[i] = PtrW; - if (Icc ->Read(PtrW, sizeof(WORD), NewLUT -> OutputEntries, Icc) != NewLUT -> OutputEntries) { - return FALSE; - } - - AdjustEndianessArray16(PtrW, NewLUT -> OutputEntries); - AllLinear += cmsIsLinear(NewLUT -> L2[i], NewLUT -> OutputEntries); - } - - // Linear output, ignore step - - if (AllLinear == NewLUT -> OutputChan) - { - NewLUT -> wFlags &= ~LUT_HASTL2; - } - - - cmsCalcL16Params(NewLUT -> InputEntries, &NewLUT -> In16params); - cmsCalcL16Params(NewLUT -> OutputEntries, &NewLUT -> Out16params); - cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan, - NewLUT -> OutputChan, - &NewLUT -> CLut16params); - - return TRUE; -} - - -// This is a shared routine for reading curves. It can handle v2 curves -// as linear, single gamma and table-based as well as v4 parametric curves. - -static -LPGAMMATABLE ReadCurve(LPLCMSICCPROFILE Icc) +// Get a media white point fixing some issues found in certain old profiles +cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile) { - icUInt32Number Count; - LPGAMMATABLE NewGamma; - icTagTypeSignature BaseType; - int n; - - - BaseType = ReadBase(Icc); - switch (BaseType) { - - - case ((icTagTypeSignature) 0x9478ee00): // Monaco 2 profiler is BROKEN! - case icSigCurveType: - - if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL; - AdjustEndianess32((LPBYTE) &Count); - - switch (Count) { - - case 0: // Linear. - - NewGamma = cmsAllocGamma(2); - if (!NewGamma) return NULL; - NewGamma -> GammaTable[0] = 0; - NewGamma -> GammaTable[1] = 0xFFFF; - return NewGamma; - - case 1: // Specified as the exponent of gamma function - { - WORD SingleGammaFixed; + cmsCIEXYZ* Tag; - if (Icc ->Read(&SingleGammaFixed, sizeof(WORD), 1, Icc) != 1) return NULL; - AdjustEndianess16((LPBYTE) &SingleGammaFixed); - return cmsBuildGamma(4096, Convert8Fixed8(SingleGammaFixed)); - } - - default: { // Curve - - NewGamma = cmsAllocGamma(Count); - if (!NewGamma) return NULL; - - if (Icc ->Read(NewGamma -> GammaTable, sizeof(WORD), Count, Icc) != Count) - return NULL; - AdjustEndianessArray16(NewGamma -> GammaTable, Count); - return NewGamma; - } - } - break; - - - // Parametric curves - case icSigParametricCurveType: { - - int ParamsByType[] = { 1, 3, 4, 5, 7 }; - double Params[10]; - icS15Fixed16Number Num; - icUInt32Number Reserved; - icUInt16Number Type; - int i; - - if (Icc -> Read(&Type, sizeof(icUInt16Number), 1, Icc) != 1) return NULL; - if (Icc -> Read(&Reserved, sizeof(icUInt16Number), 1, Icc) != 1) return NULL; - - AdjustEndianess16((LPBYTE) &Type); - if (Type > 4) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type); - return NULL; - } - - ZeroMemory(Params, 10* sizeof(double)); - n = ParamsByType[Type]; - - for (i=0; i < n; i++) { - Num = 0; - if (Icc -> Read(&Num, sizeof(icS15Fixed16Number), 1, Icc) != 1) return NULL; - Params[i] = Convert15Fixed16(Num); - } - - - NewGamma = cmsBuildParametricGamma(4096, Type+1, Params); - return NewGamma; - } - - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType); - return NULL; - } - -} - - -// Similar to anterior, but curve is reversed - -static -LPGAMMATABLE ReadCurveReversed(LPLCMSICCPROFILE Icc) -{ - - icTagTypeSignature BaseType; - LPGAMMATABLE NewGamma, ReturnGamma; - icUInt32Number Count; - int n; - - - BaseType = ReadBase(Icc); - - switch (BaseType) { - + _cmsAssert(Dest != NULL); - case 0x9478ee00L: // Monaco 2 profiler is BROKEN! - case icSigCurveType: - - if (Icc -> Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL; - AdjustEndianess32((LPBYTE) &Count); - - - switch (Count) { - - case 0: // Linear, reverse is same. - - NewGamma = cmsAllocGamma(2); - if (!NewGamma) return NULL; - - NewGamma -> GammaTable[0] = 0; - NewGamma -> GammaTable[1] = 0xFFFF; - return NewGamma; - - case 1: { - WORD SingleGammaFixed; - - if (Icc -> Read(&SingleGammaFixed, sizeof(WORD), 1, Icc) != 1) return NULL; - AdjustEndianess16((LPBYTE) &SingleGammaFixed); - return cmsBuildGamma(4096, 1./Convert8Fixed8(SingleGammaFixed)); - } - - default: { // Curve. Do our best to trying to reverse the curve - - NewGamma = cmsAllocGamma(Count); - if (!NewGamma) return NULL; - - if (Icc -> Read(NewGamma -> GammaTable, sizeof(WORD), Count, Icc) != Count) - return NULL; - - AdjustEndianessArray16(NewGamma -> GammaTable, Count); - - if (Count < 256) - Count = 256; // Reverse of simple curve has not necesarely to be simple - - ReturnGamma = cmsReverseGamma(Count, NewGamma); - cmsFreeGamma(NewGamma); - - return ReturnGamma; - } - } - break; - - - // Parametric curves - case icSigParametricCurveType: { - - int ParamsByType[] = { 1, 3, 4, 5, 7 }; - double Params[10]; - icS15Fixed16Number Num; - icUInt32Number Reserved; - icUInt16Number Type; - int i; - - - if (Icc -> Read(&Type, sizeof(icUInt16Number), 1, Icc) != 1) return NULL; - if (Icc -> Read(&Reserved, sizeof(icUInt16Number), 1, Icc) != 1) return NULL; - - AdjustEndianess16((LPBYTE) &Type); - if (Type > 4) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type); - return NULL; - } - - ZeroMemory(Params, 10* sizeof(double)); - n = ParamsByType[Type]; + Tag = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); - for (i=0; i < n; i++) { - if (Icc -> Read(&Num, sizeof(icS15Fixed16Number), 1, Icc) != 1) return NULL; - Params[i] = Convert15Fixed16(Num); - } - - - // Negative type as a mark of reversed curve - NewGamma = cmsBuildParametricGamma(4096, -(Type+1), Params); - return NewGamma; - } - - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType); - return NULL; - } - -} - - -// V4 stuff. Read matrix for LutAtoB and LutBtoA - -static -LCMSBOOL ReadMatrixOffset(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, DWORD dwFlags) -{ - - icS15Fixed16Number All[12]; - int i; - MAT3 m; - VEC3 o; - - if (Icc -> Seek(Icc, Offset)) return FALSE; - - if (Icc ->Read(All, sizeof(icS15Fixed16Number), 12, Icc) != 12) - return FALSE; - - for (i=0; i < 12; i++) - AdjustEndianess32((LPBYTE) &All[i]); - - - m.v[0].n[0] = FIXED_TO_DOUBLE((Fixed32) All[0]); - m.v[0].n[1] = FIXED_TO_DOUBLE((Fixed32) All[1]); - m.v[0].n[2] = FIXED_TO_DOUBLE((Fixed32) All[2]); - m.v[1].n[0] = FIXED_TO_DOUBLE((Fixed32) All[3]); - m.v[1].n[1] = FIXED_TO_DOUBLE((Fixed32) All[4]); - m.v[1].n[2] = FIXED_TO_DOUBLE((Fixed32) All[5]); - m.v[2].n[0] = FIXED_TO_DOUBLE((Fixed32) All[6]); - m.v[2].n[1] = FIXED_TO_DOUBLE((Fixed32) All[7]); - m.v[2].n[2] = FIXED_TO_DOUBLE((Fixed32) All[8]); - - o.n[0] = FIXED_TO_DOUBLE((Fixed32) All[9]); - o.n[1] = FIXED_TO_DOUBLE((Fixed32) All[10]); - o.n[2] = FIXED_TO_DOUBLE((Fixed32) All[11]); - - cmsSetMatrixLUT4(NewLUT, &m, &o, dwFlags); - - return TRUE; -} - - -// V4 stuff. Read CLUT part for LutAtoB and LutBtoA - -static -LCMSBOOL ReadCLUT(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT) -{ - unsigned int j; - icCLutStruct CLUT; - - if (Icc -> Seek(Icc, Offset)) return FALSE; - if (Icc ->Read(&CLUT, sizeof(icCLutStruct), 1, Icc) != 1) return FALSE; - - - for (j=1; j < NewLUT ->InputChan; j++) { - if (CLUT.gridPoints[0] != CLUT.gridPoints[j]) { - cmsSignalError(LCMS_ERRC_ABORTED, "CLUT with different granulatity is currently unsupported."); - return FALSE; - } - - + // If no wp, take D50 + if (Tag == NULL) { + *Dest = *cmsD50_XYZ(); + return TRUE; } - if (cmsAlloc3DGrid(NewLUT, CLUT.gridPoints[0], NewLUT ->InputChan, - NewLUT ->OutputChan) == NULL) return FALSE; - - // Precission can be 1 or 2 bytes - - if (CLUT.prec == 1) { + // V2 display profiles should give D50 + if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { - BYTE v; - unsigned int i; - - for (i=0; i < NewLUT->Tsize / sizeof(WORD); i++) { - if (Icc ->Read(&v, sizeof(BYTE), 1, Icc) != 1) return FALSE; - NewLUT->T[i] = TO16_TAB(v); + if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { + *Dest = *cmsD50_XYZ(); + return TRUE; } - - } - else - if (CLUT.prec == 2) { - - size_t n = NewLUT->Tsize / sizeof(WORD); - - if (Icc ->Read(NewLUT ->T, sizeof(WORD), n, Icc) != n) return FALSE; - AdjustEndianessArray16(NewLUT ->T, NewLUT->Tsize / sizeof(WORD)); - } - else { - cmsSignalError(LCMS_ERRC_ABORTED, "Unknow precission of '%d'", CLUT.prec); - return FALSE; } + // All seems ok + *Dest = *Tag; return TRUE; } -static -void ResampleCurves(LPGAMMATABLE Curves[], int nCurves) -{ - int i; - LPSAMPLEDCURVE sc; - - for (i=0; i < nCurves; i++) { - sc = cmsConvertGammaToSampledCurve(Curves[i], 4096); - cmsFreeGamma(Curves[i]); - Curves[i] = cmsConvertSampledCurveToGamma(sc, 0xFFFF); - cmsFreeSampledCurve(sc); - } - -} - - -static -void SkipAlignment(LPLCMSICCPROFILE Icc) -{ - BYTE Buffer[4]; - size_t At = Icc ->Tell(Icc); - int BytesToNextAlignedPos = (int) (At % 4); - - Icc ->Read(Buffer, 1, BytesToNextAlignedPos, Icc); -} - -// Read a set of curves from specific offset -static -LCMSBOOL ReadSetOfCurves(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, int nLocation) +// Chromatic adaptation matrix. Fix some issues as well +cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile) { - LPGAMMATABLE Curves[MAXCHANNELS]; - unsigned int i, nCurves; - - if (Icc -> Seek(Icc, Offset)) return FALSE; - - if (nLocation == 1 || nLocation == 3) - - nCurves = NewLUT ->InputChan; - else - nCurves = NewLUT ->OutputChan; - - ZeroMemory(Curves, sizeof(Curves)); - for (i=0; i < nCurves; i++) { - - Curves[i] = ReadCurve(Icc); - if (Curves[i] == NULL) goto Error; - SkipAlignment(Icc); - } - - // March-26'08: some V4 profiles may have different sampling - // rates, in this case resample all curves to maximum - - for (i=1; i < nCurves; i++) { - if (Curves[i]->nEntries != Curves[0]->nEntries) { - ResampleCurves(Curves, nCurves); - break; - } - } - - NewLUT = cmsAllocLinearTable(NewLUT, Curves, nLocation); - if (NewLUT == NULL) goto Error; - - for (i=0; i < nCurves; i++) - cmsFreeGamma(Curves[i]); - - return TRUE; - -Error: - - for (i=0; i < nCurves; i++) - if (Curves[i]) - cmsFreeGamma(Curves[i]); - - return FALSE; - - -} - -// V4 stuff. LutAtoB type -// -// [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2] -// -// Mat, Mat3, Ofs3, L3 are missing -// L1 = A curves -// L4 = M curves -// L2 = B curves - -static -LCMSBOOL ReadLUT_A2B(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig) -{ - icLutAtoB LUT16; - - if (Icc ->Read(&LUT16, sizeof(icLutAtoB), 1, Icc) != 1) return FALSE; - - NewLUT -> InputChan = LUT16.inputChan; - NewLUT -> OutputChan = LUT16.outputChan; - - // Validate the NewLUT here to avoid excessive number of channels - // (leading to stack-based buffer overflow in ReadSetOfCurves). - // Needs revalidation after table size is filled in. - if (!_cmsValidateLUT(NewLUT)) { - return FALSE; - } - - AdjustEndianess32((LPBYTE) &LUT16.offsetB); - AdjustEndianess32((LPBYTE) &LUT16.offsetMat); - AdjustEndianess32((LPBYTE) &LUT16.offsetM); - AdjustEndianess32((LPBYTE) &LUT16.offsetC); - AdjustEndianess32((LPBYTE) &LUT16.offsetA); - - if (LUT16.offsetB != 0) - ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 2); - - if (LUT16.offsetMat != 0) - ReadMatrixOffset(Icc, BaseOffset + LUT16.offsetMat, NewLUT, LUT_HASMATRIX4); - - - if (LUT16.offsetM != 0) - ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetM, NewLUT, 4); - - if (LUT16.offsetC != 0) - ReadCLUT(Icc, BaseOffset + LUT16.offsetC, NewLUT); - - if (LUT16.offsetA!= 0) - ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetA, NewLUT, 1); - - // Convert to v2 PCS - - if (Icc ->PCS == icSigLabData) { - - switch (sig) { - - case icSigAToB0Tag: - case icSigAToB1Tag: - case icSigAToB2Tag: - case icSigGamutTag: - case icSigPreview0Tag: - case icSigPreview1Tag: - case icSigPreview2Tag: - - NewLUT ->wFlags |= LUT_V4_INPUT_EMULATE_V2; - break; + cmsMAT3* Tag; - default:; - } - } - - - return TRUE; -} - -// V4 stuff. LutBtoA type - -static -LCMSBOOL ReadLUT_B2A(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig) -{ - icLutBtoA LUT16; - - if (Icc ->Read(&LUT16, sizeof(icLutBtoA), 1, Icc) != 1) return FALSE; - - NewLUT -> InputChan = LUT16.inputChan; - NewLUT -> OutputChan = LUT16.outputChan; - - // Validate the NewLUT here to avoid excessive number of channels - // (leading to stack-based buffer overflow in ReadSetOfCurves). - // Needs revalidation after table size is filled in. - if (!_cmsValidateLUT(NewLUT)) { - return FALSE; - } - - AdjustEndianess32((LPBYTE) &LUT16.offsetB); - AdjustEndianess32((LPBYTE) &LUT16.offsetMat); - AdjustEndianess32((LPBYTE) &LUT16.offsetM); - AdjustEndianess32((LPBYTE) &LUT16.offsetC); - AdjustEndianess32((LPBYTE) &LUT16.offsetA); - + _cmsAssert(Dest != NULL); - if (LUT16.offsetB != 0) - ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 1); - - if (LUT16.offsetMat != 0) - ReadMatrixOffset(Icc, BaseOffset + LUT16.offsetMat, NewLUT, LUT_HASMATRIX3); - - if (LUT16.offsetM != 0) - ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetM, NewLUT, 3); - - if (LUT16.offsetC != 0) - ReadCLUT(Icc, BaseOffset + LUT16.offsetC, NewLUT); - - if (LUT16.offsetA!= 0) - ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetA, NewLUT, 2); - - - // Convert to v2 PCS - - if (Icc ->PCS == icSigLabData) { - - switch (sig) { - - case icSigBToA0Tag: - case icSigBToA1Tag: - case icSigBToA2Tag: - case icSigGamutTag: - case icSigPreview0Tag: - case icSigPreview1Tag: - case icSigPreview2Tag: - - NewLUT ->wFlags |= LUT_V4_OUTPUT_EMULATE_V2; - break; - - default:; - } - } + Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag); - return TRUE; -} - -// CLUT main reader - -LPLUT LCMSEXPORT cmsReadICCLut(cmsHPROFILE hProfile, icTagSignature sig) -{ - - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - icTagTypeSignature BaseType; - int n; - size_t offset; - LPLUT NewLUT; - - n = _cmsSearchTag(Icc, sig, TRUE); - if (n < 0) - return NULL; - - - // If is in memory, the LUT is already there, so throw a copy - if (Icc -> TagPtrs[n]) { - if (!_cmsValidateLUT((LPLUT) Icc ->TagPtrs[n])) { - return NULL; - } - - return cmsDupLUT((LPLUT) Icc ->TagPtrs[n]); - } - - offset = Icc -> TagOffsets[n]; - - if (Icc -> Seek(Icc, offset)) - return NULL; - - BaseType = ReadBase(Icc); - - - NewLUT = cmsAllocLUT(); - if (!NewLUT) { - - cmsSignalError(LCMS_ERRC_ABORTED, "cmsAllocLUT() failed"); - return NULL; - } - - - switch (BaseType) { - - case icSigLut8Type: if (!ReadLUT8(Icc, NewLUT, sig)) { - cmsFreeLUT(NewLUT); - return NULL; - } - break; - - case icSigLut16Type: if (!ReadLUT16(Icc, NewLUT)) { - cmsFreeLUT(NewLUT); - return NULL; - } - break; - - case icSiglutAtoBType: if (!ReadLUT_A2B(Icc, NewLUT, offset, sig)) { - cmsFreeLUT(NewLUT); - return NULL; - } - break; - - case icSiglutBtoAType: if (!ReadLUT_B2A(Icc, NewLUT, offset, sig)) { - cmsFreeLUT(NewLUT); - return NULL; - } - break; - - default: cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType); - cmsFreeLUT(NewLUT); - return NULL; + if (Tag != NULL) { + *Dest = *Tag; + return TRUE; } - - return NewLUT; -} - - -// Sets the language & country preferences. Used only in ICC 4.0 profiles - -void LCMSEXPORT cmsSetLanguage(const char LanguageCode[4], const char CountryCode[4]) -{ - - int LanguageCodeInt = *(int *) LanguageCode; - int CountryCodeInt = *(int *) CountryCode; - - AdjustEndianess32((LPBYTE) &LanguageCodeInt); - AdjustEndianess32((LPBYTE) &CountryCodeInt); - - GlobalLanguageCode = LanguageCodeInt; - GlobalCountryCode = CountryCodeInt; -} - - - -// Some tags (e.g, 'pseq') can have text tags embedded. This function -// handles such special case. Returns -1 on error, or the number of bytes left on success. - -static -int ReadEmbeddedTextTag(LPLCMSICCPROFILE Icc, size_t size, char* Name, size_t size_max) -{ - icTagTypeSignature BaseType; - - - BaseType = ReadBase(Icc); - size -= sizeof(icTagBase); - - switch (BaseType) { - - case icSigTextDescriptionType: { - - icUInt32Number AsciiCount; - icUInt32Number i, UnicodeCode, UnicodeCount; - icUInt16Number ScriptCodeCode, Dummy; - icUInt8Number ScriptCodeCount; - - if (Icc ->Read(&AsciiCount, sizeof(icUInt32Number), 1, Icc) != 1) return -1; + // No CHAD available, default it to identity + _cmsMAT3identity(Dest); - if (size < sizeof(icUInt32Number)) return (int) size; - size -= sizeof(icUInt32Number); - - AdjustEndianess32((LPBYTE) &AsciiCount); - Icc ->Read(Name, 1, - (AsciiCount >= size_max) ? (size_max-1) : AsciiCount, Icc); - - if (size < AsciiCount) return (int) size; - size -= AsciiCount; - - // Skip Unicode code - - if (Icc ->Read(&UnicodeCode, sizeof(icUInt32Number), 1, Icc) != 1) return -1; - if (size < sizeof(icUInt32Number)) return (int) size; - size -= sizeof(icUInt32Number); - - if (Icc ->Read(&UnicodeCount, sizeof(icUInt32Number), 1, Icc) != 1) return -1; - if (size < sizeof(icUInt32Number)) return (int) size; - size -= sizeof(icUInt32Number); - - AdjustEndianess32((LPBYTE) &UnicodeCount); - - if (UnicodeCount > size) return (int) size; - - for (i=0; i < UnicodeCount; i++) { - size_t nread = Icc ->Read(&Dummy, sizeof(icUInt16Number), 1, Icc); - if (nread != 1) return (int) size; - size -= sizeof(icUInt16Number); - } - - // Skip ScriptCode code - - if (Icc ->Read(&ScriptCodeCode, sizeof(icUInt16Number), 1, Icc) != 1) return -1; - size -= sizeof(icUInt16Number); - if (Icc ->Read(&ScriptCodeCount, sizeof(icUInt8Number), 1, Icc) != 1) return -1; - size -= sizeof(icUInt8Number); - - // Should remain 67 bytes as filler - - if (size < 67) return (int) size; - - for (i=0; i < 67; i++) { - size_t nread = Icc ->Read(&Dummy, sizeof(icUInt8Number), 1, Icc); - if (nread != 1) return (int) size; - size --; - } - } - break; - + // V2 display profiles should give D50 + if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { - case icSigCopyrightTag: // Broken profiles from agfa does store copyright info in such type - case icSigTextType: - { - char Dummy; - size_t i, Missing = 0; - - if (size >= size_max) { - - Missing = size - size_max + 1; - size = size_max - 1; - } - - if (Icc -> Read(Name, 1, size, Icc) != size) return -1; - - for (i=0; i < Missing; i++) - Icc -> Read(&Dummy, 1, 1, Icc); - } - break; - - // MultiLocalizedUnicodeType, V4 only + if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { - case icSigMultiLocalizedUnicodeType: { - - icUInt32Number Count, RecLen; - icUInt16Number Language, Country; - icUInt32Number ThisLen, ThisOffset; - size_t Offset = 0; - size_t Len = 0; - size_t i; - wchar_t* wchar = L""; - - - if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return -1; - AdjustEndianess32((LPBYTE) &Count); - if (Icc ->Read(&RecLen, sizeof(icUInt32Number), 1, Icc) != 1) return -1; - AdjustEndianess32((LPBYTE) &RecLen); - - if (RecLen != 12) { - - cmsSignalError(LCMS_ERRC_ABORTED, "multiLocalizedUnicodeType of len != 12 is not supported."); - return -1; - } - - for (i=0; i < Count; i++) { + cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); - if (Icc ->Read(&Language, sizeof(icUInt16Number), 1, Icc) != 1) return -1; - AdjustEndianess16((LPBYTE) &Language); - if (Icc ->Read(&Country, sizeof(icUInt16Number), 1, Icc) != 1) return -1; - AdjustEndianess16((LPBYTE) &Country); - - if (Icc ->Read(&ThisLen, sizeof(icUInt32Number), 1, Icc) != 1) return -1; - AdjustEndianess32((LPBYTE) &ThisLen); - - if (Icc ->Read(&ThisOffset, sizeof(icUInt32Number), 1, Icc) != 1) return -1; - AdjustEndianess32((LPBYTE) &ThisOffset); - - if (Language == GlobalLanguageCode || Offset == 0) { - - Len = ThisLen; Offset = ThisOffset; - if (Country == GlobalCountryCode) - break; // Found - } - - } - - - if (Offset == 0) { - - strcpy(Name, "(no info)"); - break; - } + if (White == NULL) { - // Compute true offset - Offset -= 12 * Count + 8 + sizeof(icTagBase); - - // Skip unused bytes - for (i=0; i < Offset; i++) { - - char Discard; - if (Icc ->Read(&Discard, 1, 1, Icc) != 1) return -1; - } - - - // Bound len - if (Len < 0) Len = 0; - if (Len > 20*1024) Len = 20 * 1024; - - wchar = (wchar_t*) _cmsMalloc(Len*sizeof(wchar_t)+2); - if (!wchar) return -1; - - if (Icc ->Read(wchar, 1, Len, Icc) != Len) return -1; - AdjustEndianessArray16((LPWORD) wchar, Len / 2); - - wchar[Len / 2] = L'\0'; - i = wcstombs(Name, wchar, size_max ); - if (i == ((size_t) -1)) { - - Name[0] = 0; // Error + _cmsMAT3identity(Dest); + return TRUE; } - _cmsFree((void*) wchar); - } - break; - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType); - return -1; - } - - return (int) size; -} - - -// Take an ASCII item. Takes at most size_max bytes - -int LCMSEXPORT cmsReadICCTextEx(cmsHPROFILE hProfile, icTagSignature sig, char *Name, size_t size_max) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - size_t offset, size; - int n; - - n = _cmsSearchTag(Icc, sig, TRUE); - if (n < 0) - return -1; - - size = Icc -> TagSizes[n]; - - if (Icc -> TagPtrs[n]) { - - if (size > size_max) - size = size_max; - - CopyMemory(Name, Icc -> TagPtrs[n], size); - - return (int) Icc -> TagSizes[n]; - } - - offset = Icc -> TagOffsets[n]; - - - if (Icc -> Seek(Icc, offset)) - return -1; - - if (ReadEmbeddedTextTag(Icc, size, Name, size_max) < 0) return -1; - - return size; -} - -// Keep compatibility with older versions - -int LCMSEXPORT cmsReadICCText(cmsHPROFILE hProfile, icTagSignature sig, char *Text) -{ - return cmsReadICCTextEx(hProfile, sig, Text, LCMS_DESC_MAX); -} - - -// Take an XYZ item - -static -int ReadICCXYZ(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIEXYZ Value, LCMSBOOL lIsFatal) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - icTagTypeSignature BaseType; - size_t offset; - int n; - icXYZNumber XYZ; - - n = _cmsSearchTag(Icc, sig, FALSE); - if (n < 0) - return -1; - - if (Icc -> TagPtrs[n]) { - - CopyMemory(Value, Icc -> TagPtrs[n], Icc -> TagSizes[n]); - return (int) Icc -> TagSizes[n]; - } - - offset = Icc -> TagOffsets[n]; - - if (Icc -> Seek(Icc, offset)) - return -1; - - - BaseType = ReadBase(Icc); - - switch (BaseType) { - - - case 0x7c3b10cL: // Some apple broken embedded profiles does not have correct type - case icSigXYZType: - - Icc ->Read(&XYZ, sizeof(icXYZNumber), 1, Icc); - Value -> X = Convert15Fixed16(XYZ.X); - Value -> Y = Convert15Fixed16(XYZ.Y); - Value -> Z = Convert15Fixed16(XYZ.Z); - break; - - // Aug/21-2001 - Monaco 2 does have WRONG values. - - default: - if (lIsFatal) - cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType); - return -1; - } - - return 1; -} - - -// Read a icSigS15Fixed16ArrayType (currently only a 3x3 matrix) - -static -int ReadICCXYZArray(cmsHPROFILE hProfile, icTagSignature sig, LPMAT3 v) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - icTagTypeSignature BaseType; - size_t offset, sz; - int i, n; - icXYZNumber XYZ[3]; - cmsCIEXYZ XYZdbl[3]; - - - n = _cmsSearchTag(Icc, sig, FALSE); - if (n < 0) - return -1; // Not found - - if (Icc -> TagPtrs[n]) { - - CopyMemory(v, Icc -> TagPtrs[n], Icc -> TagSizes[n]); - return (int) Icc -> TagSizes[n]; - } - - offset = Icc -> TagOffsets[n]; - - if (Icc -> Seek(Icc, offset)) - return -1; - - BaseType = ReadBase(Icc); - - switch (BaseType) { - - case icSigS15Fixed16ArrayType: - - sz = Icc ->TagSizes[n] / sizeof(icXYZNumber); - - if (sz != 3) { - cmsSignalError(LCMS_ERRC_ABORTED, "Bad array size of %d entries.", sz); - return -1; - } - - Icc ->Read(XYZ, sizeof(icXYZNumber), 3, Icc); - - for (i=0; i < 3; i++) { - - XYZdbl[i].X = Convert15Fixed16(XYZ[i].X); - XYZdbl[i].Y = Convert15Fixed16(XYZ[i].Y); - XYZdbl[i].Z = Convert15Fixed16(XYZ[i].Z); - } - - CopyMemory(v, XYZdbl, 3*sizeof(cmsCIEXYZ)); - break; - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType); - return -1; - - } - - return sizeof(MAT3); -} - - - -// Primaries are to be in xyY notation - -LCMSBOOL LCMSEXPORT cmsTakeColorants(LPcmsCIEXYZTRIPLE Dest, cmsHPROFILE hProfile) -{ - if (ReadICCXYZ(hProfile, icSigRedColorantTag, &Dest -> Red, TRUE) < 0) return FALSE; - if (ReadICCXYZ(hProfile, icSigGreenColorantTag, &Dest -> Green, TRUE) < 0) return FALSE; - if (ReadICCXYZ(hProfile, icSigBlueColorantTag, &Dest -> Blue, TRUE) < 0) return FALSE; - - return TRUE; -} - - -LCMSBOOL cmsReadICCMatrixRGB2XYZ(LPMAT3 r, cmsHPROFILE hProfile) -{ - cmsCIEXYZTRIPLE Primaries; - - if (!cmsTakeColorants(&Primaries, hProfile)) return FALSE; - - VEC3init(&r -> v[0], Primaries.Red.X, Primaries.Green.X, Primaries.Blue.X); - VEC3init(&r -> v[1], Primaries.Red.Y, Primaries.Green.Y, Primaries.Blue.Y); - VEC3init(&r -> v[2], Primaries.Red.Z, Primaries.Green.Z, Primaries.Blue.Z); - - return TRUE; - -} - - -// Always return a suitable matrix - -LCMSBOOL cmsReadChromaticAdaptationMatrix(LPMAT3 r, cmsHPROFILE hProfile) -{ - - if (ReadICCXYZArray(hProfile, icSigChromaticAdaptationTag, r) < 0) { - - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - // For display profiles, revert to bradford. Else take identity. - - MAT3identity(r); - - // Emissive devices have non-identity chad - - if ((cmsGetDeviceClass(hProfile) == icSigDisplayClass) || - cmsTakeHeaderFlags(hProfile) & icTransparency) { - - // NULL for cone defaults to Bradford, from media to D50 - cmsAdaptationMatrix(r, NULL, &Icc ->MediaWhitePoint, &Icc ->Illuminant); + return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ()); } } @@ -1909,1850 +157,902 @@ } - -LPGAMMATABLE LCMSEXPORT cmsReadICCGamma(cmsHPROFILE hProfile, icTagSignature sig) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - size_t offset; - int n; - - - n = _cmsSearchTag(Icc, sig, TRUE); - if (n < 0) - return NULL; - - if (Icc -> TagPtrs[n]) { - - return cmsDupGamma((LPGAMMATABLE) Icc -> TagPtrs[n]); - } - - offset = Icc -> TagOffsets[n]; - - if (Icc -> Seek(Icc, offset)) - return NULL; - - return ReadCurve(Icc); - -} - - -// Some ways have analytical revese. This function accounts for that - -LPGAMMATABLE LCMSEXPORT cmsReadICCGammaReversed(cmsHPROFILE hProfile, icTagSignature sig) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - size_t offset; - int n; - - - n = _cmsSearchTag(Icc, sig, TRUE); - if (n < 0) - return NULL; - - if (Icc -> TagPtrs[n]) { - - return cmsReverseGamma(256, (LPGAMMATABLE) Icc -> TagPtrs[n]); - } - - offset = Icc -> TagOffsets[n]; - - if (Icc -> Seek(Icc, offset)) - return NULL; - - return ReadCurveReversed(Icc); -} - -// Check Named color header - +// Auxiliary, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper static -LCMSBOOL CheckHeader(LPcmsNAMEDCOLORLIST v, icNamedColor2* nc2) -{ - if (v ->Prefix[0] == 0 && v ->Suffix[0] == 0 && v ->ColorantCount == 0) return TRUE; - - if (stricmp(v ->Prefix, (const char*) nc2 ->prefix) != 0) return FALSE; - if (stricmp(v ->Suffix, (const char*) nc2 ->suffix) != 0) return FALSE; - - return ((int) v ->ColorantCount == (int) nc2 ->nDeviceCoords); -} - -// Read named color list - -int cmsReadICCnamedColorList(cmsHTRANSFORM xform, cmsHPROFILE hProfile, icTagSignature sig) +cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile) { - _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - int n; - icTagTypeSignature BaseType; - size_t offset; - - n = _cmsSearchTag(Icc, sig, TRUE); - if (n < 0) - return 0; - - if (Icc -> TagPtrs[n]) { - - // This replaces actual named color list. - size_t size = Icc -> TagSizes[n]; - - if (v ->NamedColorList) cmsFreeNamedColorList(v ->NamedColorList); - v -> NamedColorList = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size); - CopyMemory(v -> NamedColorList, Icc ->TagPtrs[n], size); - return v ->NamedColorList->nColors; - } - - offset = Icc -> TagOffsets[n]; - - if (Icc -> Seek(Icc, offset)) - return 0; - - BaseType = ReadBase(Icc); - - switch (BaseType) { - - // I never have seen one of these. Probably is not worth of implementing. - - case icSigNamedColorType: { - - cmsSignalError(LCMS_ERRC_WARNING, "Ancient named color profiles are not supported."); - return 0; - } - - // The named color struct - - case icSigNamedColor2Type: { - - icNamedColor2 nc2; - unsigned int i, j; - - if (Icc -> Read(&nc2, sizeof(icNamedColor2) - SIZEOF_UINT8_ALIGNED, 1, Icc) != 1) return 0; - AdjustEndianess32((LPBYTE) &nc2.vendorFlag); - AdjustEndianess32((LPBYTE) &nc2.count); - AdjustEndianess32((LPBYTE) &nc2.nDeviceCoords); - - if (!CheckHeader(v->NamedColorList, &nc2)) { - cmsSignalError(LCMS_ERRC_WARNING, "prefix/suffix/device for named color profiles mismatch."); - return 0; - } - - if (nc2.nDeviceCoords > MAXCHANNELS) { - cmsSignalError(LCMS_ERRC_WARNING, "Too many device coordinates."); - return 0; - } - - strncpy(v ->NamedColorList->Prefix, (const char*) nc2.prefix, 32); - strncpy(v ->NamedColorList->Suffix, (const char*) nc2.suffix, 32); - v ->NamedColorList->Prefix[32] = v->NamedColorList->Suffix[32] = 0; - - v ->NamedColorList ->ColorantCount = nc2.nDeviceCoords; - - for (i=0; i < nc2.count; i++) { - - WORD PCS[3]; - WORD Colorant[MAXCHANNELS]; - char Root[33]; - - ZeroMemory(Colorant, sizeof(WORD) * MAXCHANNELS); - Icc -> Read(Root, 1, 32, Icc); - Icc -> Read(PCS, 3, sizeof(WORD), Icc); - - for (j=0; j < 3; j++) - AdjustEndianess16((LPBYTE) &PCS[j]); - - Icc -> Read(Colorant, sizeof(WORD), nc2.nDeviceCoords, Icc); - - for (j=0; j < nc2.nDeviceCoords; j++) - AdjustEndianess16((LPBYTE) &Colorant[j]); - - cmsAppendNamedColor(v, Root, PCS, Colorant); - } - - return v ->NamedColorList->nColors; - } - break; - - default: - cmsSignalError(LCMS_ERRC_WARNING, "Bad tag signature '%lx' found.", BaseType); - return 0; - } - - // It would never reach here - // return 0; -} - - - -// Read colorant tables - -LPcmsNAMEDCOLORLIST LCMSEXPORT cmsReadColorantTable(cmsHPROFILE hProfile, icTagSignature sig) -{ - icInt32Number n; - icUInt32Number Count, i; - size_t offset; - icTagTypeSignature BaseType; - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - LPcmsNAMEDCOLORLIST List; - - n = _cmsSearchTag(Icc, sig, FALSE); - if (n < 0) - return NULL; // Not found - - if (Icc -> TagPtrs[n]) { - - size_t size = Icc -> TagSizes[n]; - void* v = _cmsMalloc(size); - - if (v == NULL) return NULL; - CopyMemory(v, Icc -> TagPtrs[n], size); - return (LPcmsNAMEDCOLORLIST) v; - } - - - offset = Icc -> TagOffsets[n]; - - if (Icc -> Seek(Icc, offset)) - return NULL; - - BaseType = ReadBase(Icc); - - if (BaseType != icSigColorantTableType) { - cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType); - return NULL; - } - - - if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL; - AdjustEndianess32((LPBYTE) &Count); - - if (Count > MAXCHANNELS) { - cmsSignalError(LCMS_ERRC_ABORTED, "Too many colorants '%lx'", Count); - return NULL; - } + cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue; - List = cmsAllocNamedColorList(Count); - for (i=0; i < Count; i++) { - - if (!Icc ->Read(List->List[i].Name, 1, 32 , Icc)) goto Error; - if (!Icc ->Read(List->List[i].PCS, sizeof(icUInt16Number), 3, Icc)) goto Error; - AdjustEndianessArray16(List->List[i].PCS, 3); - } - - return List; - -Error: - cmsFreeNamedColorList(List); - return NULL; - -} - - - -// Uncooked manufacturer - -const char* LCMSEXPORT cmsTakeManufacturer(cmsHPROFILE hProfile) -{ - - static char Manufacturer[LCMS_DESC_MAX] = ""; - - Manufacturer[0] = 0; - - if (cmsIsTag(hProfile, icSigDeviceMfgDescTag)) { - - cmsReadICCTextEx(hProfile, icSigDeviceMfgDescTag, Manufacturer, LCMS_DESC_MAX); - } - - return Manufacturer; -} - -// Uncooked model - -const char* LCMSEXPORT cmsTakeModel(cmsHPROFILE hProfile) -{ - - static char Model[LCMS_DESC_MAX] = ""; - - Model[0] = 0; - - if (cmsIsTag(hProfile, icSigDeviceModelDescTag)) { - - cmsReadICCTextEx(hProfile, icSigDeviceModelDescTag, Model, LCMS_DESC_MAX); - } - - return Model; -} - - -const char* LCMSEXPORT cmsTakeCopyright(cmsHPROFILE hProfile) -{ - - static char Copyright[LCMS_DESC_MAX] = ""; - - Copyright[0] = 0; - if (cmsIsTag(hProfile, icSigCopyrightTag)) { - - cmsReadICCTextEx(hProfile, icSigCopyrightTag, Copyright, LCMS_DESC_MAX); - } - - return Copyright; -} - - -// We compute name with model - manufacturer - -const char* LCMSEXPORT cmsTakeProductName(cmsHPROFILE hProfile) -{ - static char Name[LCMS_DESC_MAX*2+4]; - char Manufacturer[LCMS_DESC_MAX], Model[LCMS_DESC_MAX]; - - Name[0] = '\0'; - Manufacturer[0] = Model[0] = '\0'; - - if (cmsIsTag(hProfile, icSigDeviceMfgDescTag)) { - - cmsReadICCTextEx(hProfile, icSigDeviceMfgDescTag, Manufacturer, LCMS_DESC_MAX); - } - - if (cmsIsTag(hProfile, icSigDeviceModelDescTag)) { - - cmsReadICCTextEx(hProfile, icSigDeviceModelDescTag, Model, LCMS_DESC_MAX); - } - - if (!Manufacturer[0] && !Model[0]) { - - if (cmsIsTag(hProfile, icSigProfileDescriptionTag)) { - - cmsReadICCTextEx(hProfile, icSigProfileDescriptionTag, Name, LCMS_DESC_MAX); - return Name; - } - else return "{no name}"; - } - - - if (!Manufacturer[0] || - strncmp(Model, Manufacturer, 8) == 0 || strlen(Model) > 30) - strcpy(Name, Model); - else - sprintf(Name, "%s - %s", Model, Manufacturer); - - return Name; - -} + _cmsAssert(r != NULL); - -// We compute desc with manufacturer - model - -const char* LCMSEXPORT cmsTakeProductDesc(cmsHPROFILE hProfile) -{ - static char Name[2048]; - - if (cmsIsTag(hProfile, icSigProfileDescriptionTag)) { - - cmsReadICCText(hProfile, icSigProfileDescriptionTag, Name); - } - else return cmsTakeProductName(hProfile); - - if (strncmp(Name, "Copyrig", 7) == 0) - return cmsTakeProductName(hProfile); - - return Name; -} - - -const char* LCMSEXPORT cmsTakeProductInfo(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - static char Info[4096]; - - Info[0] = '\0'; - - if (cmsIsTag(hProfile, icSigProfileDescriptionTag)) - { - char Desc[1024]; - - cmsReadICCText(hProfile, icSigProfileDescriptionTag, Desc); - strcat(Info, Desc); - strcat(Info, "\r\n\r\n"); - } - - - if (cmsIsTag(hProfile, icSigCopyrightTag)) - { - char Copyright[LCMS_DESC_MAX]; - - cmsReadICCText(hProfile, icSigCopyrightTag, Copyright); - strcat(Info, Copyright); - strcat(Info, "\r\n\r\n"); - } - - - -// KODAK private tag... But very useful - -#define K007 (icTagSignature)0x4B303037 - - // MonCal + PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag); + PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag); + PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag); - if (cmsIsTag(hProfile, K007)) - { - char MonCal[LCMS_DESC_MAX]; - - cmsReadICCText(hProfile, K007, MonCal); - strcat(Info, MonCal); - strcat(Info, "\r\n\r\n"); - } - else - { - cmsCIEXYZ WhitePt; - char WhiteStr[1024]; - - cmsTakeMediaWhitePoint(&WhitePt, hProfile); - _cmsIdentifyWhitePoint(WhiteStr, &WhitePt); - strcat(WhiteStr, "\r\n\r\n"); - strcat(Info, WhiteStr); - } - - - if (Icc -> stream) { - strcat(Info, Icc -> PhysicalFile); - } - return Info; -} - -// Extract the target data as a big string. Does not signal if tag is not present. - -LCMSBOOL LCMSEXPORT cmsTakeCharTargetData(cmsHPROFILE hProfile, char** Data, size_t* len) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - int n; - - *Data = NULL; - *len = 0; - - n = _cmsSearchTag(Icc, icSigCharTargetTag, FALSE); - if (n < 0) return FALSE; - - - *len = Icc -> TagSizes[n]; - - // Make sure that is reasonable (600K) - if (*len > 600*1024) *len = 600*1024; - - *Data = (char*) _cmsMalloc(*len + 1); // Plus zero marker - - if (!*Data) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory allocating CharTarget space!"); - return FALSE; - } - - if (cmsReadICCTextEx(hProfile, icSigCharTargetTag, *Data, *len) < 0) + if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL) return FALSE; - (*Data)[*len] = 0; // Force a zero marker. Shouldn't be needed, but is - // here to simplify things. + _cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X); + _cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y); + _cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z); return TRUE; } - - -LCMSBOOL LCMSEXPORT cmsTakeCalibrationDateTime(struct tm *Dest, cmsHPROFILE hProfile) +// Gray input pipeline +static +cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile) { - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - int n; + cmsToneCurve *GrayTRC; + cmsPipeline* Lut; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); + if (GrayTRC == NULL) return NULL; - n = _cmsSearchTag(Icc, icSigCalibrationDateTimeTag, FALSE); - if (n < 0) return FALSE; + Lut = cmsPipelineAlloc(ContextID, 1, 3); + if (Lut == NULL) + goto Error; - if (Icc ->TagPtrs[n]) { + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + // In this case we implement the profile as an identity matrix plus 3 tone curves + cmsUInt16Number Zero[2] = { 0x8080, 0x8080 }; + cmsToneCurve* EmptyTab; + cmsToneCurve* LabCurves[3]; - CopyMemory(Dest, Icc ->TagPtrs[n], sizeof(struct tm)); - } - else - { - icDateTimeNumber timestamp; + EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); + + if (EmptyTab == NULL) + goto Error; + + LabCurves[0] = GrayTRC; + LabCurves[1] = EmptyTab; + LabCurves[2] = EmptyTab; - if (Icc -> Seek(Icc, Icc -> TagOffsets[n] + sizeof(icTagBase))) - return FALSE; + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) { + cmsFreeToneCurve(EmptyTab); + goto Error; + } - if (Icc ->Read(×tamp, 1, sizeof(icDateTimeNumber), Icc) != sizeof(icDateTimeNumber)) - return FALSE; + cmsFreeToneCurve(EmptyTab); - DecodeDateTimeNumber(×tamp, Dest); + } + else { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL))) + goto Error; } + return Lut; - return TRUE; +Error: + cmsFreeToneCurve(GrayTRC); + cmsPipelineFree(Lut); + return NULL; } - - -// PSEQ Tag, used in devicelink profiles - -LPcmsSEQ LCMSEXPORT cmsReadProfileSequenceDescription(cmsHPROFILE hProfile) +// RGB Matrix shaper +static +cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile) { - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - int n; - icUInt32Number i, Count; - icDescStruct DescStruct; - icTagTypeSignature BaseType; - size_t size, offset; - LPcmsSEQ OutSeq; + cmsPipeline* Lut; + cmsMAT3 Mat; + cmsToneCurve *Shapes[3]; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + int i, j; + + if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL; + + // XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so + // we need to adjust the output by a factor of (0x10000/0xffff) to put data in + // a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2) + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Mat.v[i].n[j] *= InpAdj; - n = _cmsSearchTag(Icc, icSigProfileSequenceDescTag, FALSE); - if (n < 0) return NULL; - - size = Icc -> TagSizes[n]; - if (size < 12) return NULL; - - if (Icc -> TagPtrs[n]) { + Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); + Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); + Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); - OutSeq = (LPcmsSEQ) _cmsMalloc(size); - if (OutSeq == NULL) return NULL; - CopyMemory(OutSeq, Icc ->TagPtrs[n], size); - return OutSeq; - } - - offset = Icc -> TagOffsets[n]; + if (!Shapes[0] || !Shapes[1] || !Shapes[2]) + return NULL; - if (Icc -> Seek(Icc, offset)) - return NULL; - - BaseType = ReadBase(Icc); - - if (BaseType != icSigProfileSequenceDescType) return NULL; - - Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc); - AdjustEndianess32((LPBYTE) &Count); + Lut = cmsPipelineAlloc(ContextID, 3, 3); + if (Lut != NULL) { - if (Count > 1000) { - return NULL; - } - - size = sizeof(int) + Count * sizeof(cmsPSEQDESC); - OutSeq = (LPcmsSEQ) _cmsMalloc(size); - if (OutSeq == NULL) return NULL; - - OutSeq ->n = Count; - - // Get structures as well - - for (i=0; i < Count; i++) { - - LPcmsPSEQDESC sec = &OutSeq -> seq[i]; + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL))) + goto Error; - Icc -> Read(&DescStruct, sizeof(icDescStruct) - SIZEOF_UINT8_ALIGNED, 1, Icc); - - AdjustEndianess32((LPBYTE) &DescStruct.deviceMfg); - AdjustEndianess32((LPBYTE) &DescStruct.deviceModel); - AdjustEndianess32((LPBYTE) &DescStruct.technology); - AdjustEndianess32((LPBYTE) &DescStruct.attributes[0]); - AdjustEndianess32((LPBYTE) &DescStruct.attributes[1]); + // Note that it is certainly possible a single profile would have a LUT based + // tag for output working in lab and a matrix-shaper for the fallback cases. + // This is not allowed by the spec, but this code is tolerant to those cases + if (cmsGetPCS(hProfile) == cmsSigLabData) { - sec ->attributes[0] = DescStruct.attributes[0]; - sec ->attributes[1] = DescStruct.attributes[1]; - sec ->deviceMfg = DescStruct.deviceMfg; - sec ->deviceModel = DescStruct.deviceModel; - sec ->technology = DescStruct.technology; - - if (ReadEmbeddedTextTag(Icc, size, sec ->Manufacturer, LCMS_DESC_MAX) < 0) return NULL; - if (ReadEmbeddedTextTag(Icc, size, sec ->Model, LCMS_DESC_MAX) < 0) return NULL; + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID))) + goto Error; + } } - return OutSeq; -} - + return Lut; -void LCMSEXPORT cmsFreeProfileSequenceDescription(LPcmsSEQ pseq) -{ - if (pseq) - _cmsFree(pseq); +Error: + cmsPipelineFree(Lut); + return NULL; } +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + + if (Lut == NULL) return NULL; + + // input and output of transform are in lcms 0..1 encoding. If XYZ or Lab spaces are used, + // these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0) + if ( spc == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else if (spc == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + if ( PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else if( PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} -// Read a few tags that are hardly required +// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc +// is adjusted here in order to create a LUT that takes care of all those details. +// We add intent = 0xffffffff as a way to read matrix shaper always, no matter of other LUT +cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + // On named color, take the appropriate tag + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + cmsPipeline* Lut; + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag); + + if (nc == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 0, 0); + if (Lut == NULL) { + cmsFreeNamedColorList(nc); + return NULL; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) { + cmsPipelineFree(Lut); + return NULL; + } + return Lut; + } + + // This is an attempt to reuse this function to retrieve the matrix-shaper as pipeline no + // matter other LUT are present and have precedence. Intent = 0xffffffff can be used for that. + if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) { + + tag16 = Device2PCS16[Intent]; + tagFloat = Device2PCSFloat[Intent]; + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V4, but the encoding range is no + // longer 0..1.0, so we need to add an stage depending on the color space + return _cmsReadFloatInputTag(hProfile, tagFloat); + } + + // Revert to perceptual if no tag is found + if (!cmsIsTag(hProfile, tag16)) { + tag16 = Device2PCS16[0]; + } + + if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // First read the tag + cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // After reading it, we have now info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + + // We need to adjust data only for Lab16 on output + if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) + return Lut; + + // If the input is Lab, add also a conversion at the begin + if (cmsGetColorSpace(hProfile) == cmsSigLabData && + !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + + // Add a matrix for conversion V2 to V4 Lab PCS + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; + } + } + + // Lut was not found, try to create a matrix-shaper + + // Check if this is a grayscale profile. + if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { + + // if so, build appropriate conversion tables. + // The tables are the PCS iluminant, scaled across GrayTRC + return BuildGrayInputMatrixPipeline(hProfile); + } + + // Not gray, create a normal matrix-shaper + return BuildRGBInputMatrixShaper(hProfile); +} + +// --------------------------------------------------------------------------------------------------------------- + +// Gray output pipeline. +// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be +// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve. +// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well. + +static +cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile) +{ + cmsToneCurve *GrayTRC, *RevGrayTRC; + cmsPipeline* Lut; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); + if (GrayTRC == NULL) return NULL; + + RevGrayTRC = cmsReverseToneCurve(GrayTRC); + if (RevGrayTRC == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 3, 1); + if (Lut == NULL) { + cmsFreeToneCurve(RevGrayTRC); + return NULL; + } + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL))) + goto Error; + } + else { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL))) + goto Error; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC))) + goto Error; + + cmsFreeToneCurve(RevGrayTRC); + return Lut; + +Error: + cmsFreeToneCurve(RevGrayTRC); + cmsPipelineFree(Lut); + return NULL; +} static -void ReadCriticalTags(LPLCMSICCPROFILE Icc) +cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile) { - cmsHPROFILE hProfile = (cmsHPROFILE) Icc; + cmsPipeline* Lut; + cmsToneCurve *Shapes[3], *InvShapes[3]; + cmsMAT3 Mat, Inv; + int i, j; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) + return NULL; + + if (!_cmsMAT3inverse(&Mat, &Inv)) + return NULL; - if (Icc ->Version >= 0x4000000) { + // XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so + // we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of + // (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0; - // v4 profiles + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Inv.v[i].n[j] *= OutpAdj; + + Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); + Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); + Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); + + if (!Shapes[0] || !Shapes[1] || !Shapes[2]) + return NULL; - MAT3 ChrmCanonical; + InvShapes[0] = cmsReverseToneCurve(Shapes[0]); + InvShapes[1] = cmsReverseToneCurve(Shapes[1]); + InvShapes[2] = cmsReverseToneCurve(Shapes[2]); + + if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) { + return NULL; + } + + Lut = cmsPipelineAlloc(ContextID, 3, 3); + if (Lut != NULL) { + + // Note that it is certainly possible a single profile would have a LUT based + // tag for output working in lab and a matrix-shaper for the fallback cases. + // This is not allowed by the spec, but this code is tolerant to those cases + if (cmsGetPCS(hProfile) == cmsSigLabData) { - if (ReadICCXYZ(hProfile, - icSigMediaWhitePointTag, - &Icc ->MediaWhitePoint, FALSE) < 0) { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID))) + goto Error; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes))) + goto Error; + } - Icc ->MediaWhitePoint = *cmsD50_XYZ(); - } + cmsFreeToneCurveTriple(InvShapes); + return Lut; +Error: + cmsFreeToneCurveTriple(InvShapes); + cmsPipelineFree(Lut); + return NULL; +} - // Read media black - if (ReadICCXYZ(hProfile, - icSigMediaBlackPointTag, - &Icc ->MediaBlackPoint, FALSE) < 0) { +// Change CLUT interpolation to trilinear +static +void ChangeInterpolationToTrilinear(cmsPipeline* Lut) +{ + cmsStage* Stage; + + for (Stage = cmsPipelineGetPtrToFirstStage(Lut); + Stage != NULL; + Stage = cmsStageNext(Stage)) { + + if (cmsStageType(Stage) == cmsSigCLutElemType) { + + _cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data; - Icc ->MediaBlackPoint.X = 0; - Icc ->MediaBlackPoint.Y = 0; - Icc ->MediaBlackPoint.X = 0; + CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR; + _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params); + } + } +} + - } +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile); + + if (Lut == NULL) return NULL; - NormalizeXYZ(&Icc ->MediaWhitePoint); - NormalizeXYZ(&Icc ->MediaBlackPoint); - - if (ReadICCXYZArray(hProfile, - icSigChromaticAdaptationTag, - &ChrmCanonical) > 0) { + // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding, + // and since the formatter has already accommodated to 0..1.0, we should undo this change + if ( PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else + if (PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } - MAT3inverse(&ChrmCanonical, &Icc ->ChromaticAdaptation); + // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline + if ( dataSpace == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else if (dataSpace == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } - } - else { + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} - MAT3identity(&Icc ->ChromaticAdaptation); - } +// Create an output MPE LUT from agiven profile. Version mismatches are handled here +cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); - // Convert media white, black to absolute under original illuminant + if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) { + + tag16 = PCS2Device16[Intent]; + tagFloat = PCS2DeviceFloat[Intent]; + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V4 + return _cmsReadFloatOutputTag(hProfile, tagFloat); + } + + // Revert to perceptual if no tag is found + if (!cmsIsTag(hProfile, tag16)) { + tag16 = PCS2Device16[0]; + } + + if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // First read the tag + cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // After reading it, we have info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + if (Lut == NULL) return NULL; + + // Now it is time for a controversial stuff. I found that for 3D LUTS using + // Lab used as indexer space, trilinear interpolation should be used + if (cmsGetPCS(hProfile) == cmsSigLabData) + ChangeInterpolationToTrilinear(Lut); + + // We need to adjust data only for Lab and Lut16 type + if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) + return Lut; + + // Add a matrix for conversion V4 to V2 Lab PCS + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + + // If the output is Lab, add also a conversion at the end + if (cmsGetColorSpace(hProfile) == cmsSigLabData) + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; + } + } + + // Lut not found, try to create a matrix-shaper + + // Check if this is a grayscale profile. + if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { - EvalCHRM(&Icc ->MediaWhitePoint, &Icc ->ChromaticAdaptation, &Icc ->MediaWhitePoint); - EvalCHRM(&Icc ->MediaBlackPoint, &Icc ->ChromaticAdaptation, &Icc ->MediaBlackPoint); + // if so, build appropriate conversion tables. + // The tables are the PCS iluminant, scaled across GrayTRC + return BuildGrayOutputPipeline(hProfile); + } + + // Not gray, create a normal matrix-shaper, which only operates in XYZ space + return BuildRGBOutputMatrixShaper(hProfile); +} + +// --------------------------------------------------------------------------------------------------------------- + +// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); + + if (Lut == NULL) return NULL; + + if (spc == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else + if (spc == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + if (PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else + if (PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The +// tag name here may default to AToB0 +cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsPipeline* Lut; + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); - } - else { + if (Intent > INTENT_ABSOLUTE_COLORIMETRIC) + return NULL; - // v2 profiles - - // Read media white + tag16 = Device2PCS16[Intent]; + tagFloat = Device2PCSFloat[Intent]; - if (ReadICCXYZ(hProfile, - icSigMediaWhitePointTag, - &Icc ->MediaWhitePoint, FALSE) < 0) { + // On named color, take the appropriate tag + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { - Icc ->MediaWhitePoint = *cmsD50_XYZ(); - } + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*)cmsReadTag(hProfile, cmsSigNamedColor2Tag); + + if (nc == NULL) return NULL; - // Read media black + Lut = cmsPipelineAlloc(ContextID, 0, 0); + if (Lut == NULL) + goto Error; - if (ReadICCXYZ(hProfile, - icSigMediaBlackPointTag, - &Icc ->MediaBlackPoint, FALSE) < 0) { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE))) + goto Error; - Icc ->MediaBlackPoint.X = 0; - Icc ->MediaBlackPoint.Y = 0; - Icc ->MediaBlackPoint.X = 0; + if (cmsGetColorSpace(hProfile) == cmsSigLabData) + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; - } - - NormalizeXYZ(&Icc ->MediaWhitePoint); - NormalizeXYZ(&Icc ->MediaBlackPoint); + return Lut; + Error: + cmsPipelineFree(Lut); + cmsFreeNamedColorList(nc); + return NULL; + } - // Take Bradford as default for Display profiles only. + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V + return _cmsReadFloatDevicelinkTag(hProfile, tagFloat); + } + + tagFloat = Device2PCSFloat[0]; + if (cmsIsTag(hProfile, tagFloat)) { + + return cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat)); + } + + if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + tag16 = Device2PCS16[0]; + if (!cmsIsTag(hProfile, tag16)) return NULL; + } + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // Read the tag + Lut = (cmsPipeline*)cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; - if (cmsGetDeviceClass(hProfile) == icSigDisplayClass) { + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + if (Lut == NULL) return NULL; + + // Now it is time for a controversial stuff. I found that for 3D LUTS using + // Lab used as indexer space, trilinear interpolation should be used + if (cmsGetPCS(hProfile) == cmsSigLabData) + ChangeInterpolationToTrilinear(Lut); + + // After reading it, we have info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + // We need to adjust data for Lab16 on output + if (OriginalType != cmsSigLut16Type) return Lut; + + // Here it is possible to get Lab on both sides + + if (cmsGetColorSpace(hProfile) == cmsSigLabData) { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error2; + } + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error2; + } + + return Lut; - cmsAdaptationMatrix(&Icc -> ChromaticAdaptation, - NULL, - &Icc -> Illuminant, - &Icc -> MediaWhitePoint); - } - else - MAT3identity(&Icc ->ChromaticAdaptation); +Error2: + cmsPipelineFree(Lut); + return NULL; +} + +// --------------------------------------------------------------------------------------------------------------- + +// Returns TRUE if the profile is implemented as matrix-shaper +cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile) +{ + switch (cmsGetColorSpace(hProfile)) { + + case cmsSigGrayData: + + return cmsIsTag(hProfile, cmsSigGrayTRCTag); + + case cmsSigRgbData: + + return (cmsIsTag(hProfile, cmsSigRedColorantTag) && + cmsIsTag(hProfile, cmsSigGreenColorantTag) && + cmsIsTag(hProfile, cmsSigBlueColorantTag) && + cmsIsTag(hProfile, cmsSigRedTRCTag) && + cmsIsTag(hProfile, cmsSigGreenTRCTag) && + cmsIsTag(hProfile, cmsSigBlueTRCTag)); + + default: + return FALSE; } +} + +// Returns TRUE if the intent is implemented as CLUT +cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection) +{ + const cmsTagSignature* TagTable; + + // For devicelinks, the supported intent is that one stated in the header + if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) { + return (cmsGetHeaderRenderingIntent(hProfile) == Intent); + } + + switch (UsedDirection) { + + case LCMS_USED_AS_INPUT: TagTable = Device2PCS16; break; + case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device16; break; + + // For proofing, we need rel. colorimetric in output. Let's do some recursion + case LCMS_USED_AS_PROOF: + return cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) && + cmsIsIntentSupported(hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT); + + default: + cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_RANGE, "Unexpected direction (%d)", UsedDirection); + return FALSE; + } + + return cmsIsTag(hProfile, TagTable[Intent]); } -// Create profile from disk file - -cmsHPROFILE LCMSEXPORT cmsOpenProfileFromFile(const char *lpFileName, const char *sAccess) +// Return info about supported intents +cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number UsedDirection) { - LPLCMSICCPROFILE NewIcc; - cmsHPROFILE hEmpty; - - - // Open for write means an empty profile - - if (*sAccess == 'W' || *sAccess == 'w') { - - hEmpty = _cmsCreateProfilePlaceholder(); - NewIcc = (LPLCMSICCPROFILE) (LPSTR) hEmpty; - NewIcc -> IsWrite = TRUE; - strncpy(NewIcc ->PhysicalFile, lpFileName, MAX_PATH-1); - NewIcc ->PhysicalFile[MAX_PATH-1] = 0; - - // Save LUT as 8 bit - sAccess++; - if (*sAccess == '8') NewIcc ->SaveAs8Bits = TRUE; - - return hEmpty; - } - - - // Open for read means a file placeholder - - NewIcc = _cmsCreateProfileFromFilePlaceholder(lpFileName); - if (!NewIcc) return NULL; - - if (!ReadHeader(NewIcc, FALSE)) return NULL; - - ReadCriticalTags(NewIcc); - - return (cmsHPROFILE) (LPSTR) NewIcc; -} - - - - -// Open from memory block + if (cmsIsCLUT(hProfile, Intent, UsedDirection)) return TRUE; -cmsHPROFILE LCMSEXPORT cmsOpenProfileFromMem(LPVOID MemPtr, DWORD dwSize) -{ - LPLCMSICCPROFILE NewIcc; - - - NewIcc = _cmsCreateProfileFromMemPlaceholder(MemPtr, dwSize); - if (!NewIcc) return NULL; - - if (!ReadHeader(NewIcc, TRUE)) return NULL; - - ReadCriticalTags(NewIcc); - - return (cmsHPROFILE) (LPSTR) NewIcc; - -} - - + // Is there any matrix-shaper? If so, the intent is supported. This is a bit odd, since V2 matrix shaper + // does not fully support relative colorimetric because they cannot deal with non-zero black points, but + // many profiles claims that, and this is certainly not true for V4 profiles. Lets answer "yes" no matter + // the accuracy would be less than optimal in rel.col and v2 case. -LCMSBOOL LCMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - LCMSBOOL rc = TRUE; - icInt32Number i; - - if (!Icc) return FALSE; - - // Was open in write mode? - if (Icc ->IsWrite) { - - Icc ->IsWrite = FALSE; // Assure no further writting - rc = _cmsSaveProfile(hProfile, Icc ->PhysicalFile); - } - - for (i=0; i < Icc -> TagCount; i++) { - - if (Icc -> TagPtrs[i]) - free(Icc -> TagPtrs[i]); - } - - if (Icc -> stream != NULL) { // Was a memory (i.e. not serialized) profile? - Icc -> Close(Icc); // No, close the stream - } - - free(Icc); // Free placeholder memory - - return rc; + return cmsIsMatrixShaper(hProfile); } - -// Write profile ------------------------------------------------------------ - +// --------------------------------------------------------------------------------------------------------------- - -static -LCMSBOOL SaveWordsTable(int nEntries, LPWORD Tab, LPLCMSICCPROFILE Icc) +// Read both, profile sequence description and profile sequence id if present. Then combine both to +// create qa unique structure holding both. Shame on ICC to store things in such complicated way. +cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile) { - size_t nTabSize = sizeof(WORD) * nEntries; - LPWORD PtrW = (LPWORD) _cmsMalloc(nTabSize); - LCMSBOOL rc; + cmsSEQ* ProfileSeq; + cmsSEQ* ProfileId; + cmsSEQ* NewSeq; + cmsUInt32Number i; + + // Take profile sequence description first + ProfileSeq = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceDescTag); + + // Take profile sequence ID + ProfileId = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceIdTag); + + if (ProfileSeq == NULL && ProfileId == NULL) return NULL; - if (!PtrW) return FALSE; - CopyMemory(PtrW, Tab, nTabSize); - AdjustEndianessArray16(PtrW, nEntries); - rc = Icc ->Write(Icc, nTabSize, PtrW); - free(PtrW); + if (ProfileSeq == NULL) return cmsDupProfileSequenceDescription(ProfileId); + if (ProfileId == NULL) return cmsDupProfileSequenceDescription(ProfileSeq); + + // We have to mix both together. For that they must agree + if (ProfileSeq ->n != ProfileId ->n) return cmsDupProfileSequenceDescription(ProfileSeq); + + NewSeq = cmsDupProfileSequenceDescription(ProfileSeq); - return rc; + // Ok, proceed to the mixing + if (NewSeq != NULL) { + for (i=0; i < ProfileSeq ->n; i++) { + + memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID)); + NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description); + } + } + return NewSeq; } - - -// Saves profile header - -static -LCMSBOOL SaveHeader(LPLCMSICCPROFILE Icc) +// Dump the contents of profile sequence in both tags (if v4 available) +cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq) { - icHeader Header; - time_t now = time(NULL); - - Header.size = TransportValue32((icInt32Number) Icc ->UsedSpace); - Header.cmmId = TransportValue32(lcmsSignature); - Header.version = TransportValue32((icInt32Number) 0x02300000); - Header.deviceClass = (icProfileClassSignature) TransportValue32(Icc -> DeviceClass); - Header.colorSpace = (icColorSpaceSignature) TransportValue32(Icc -> ColorSpace); - Header.pcs = (icColorSpaceSignature) TransportValue32(Icc -> PCS); - - // NOTE: in v4 Timestamp must be in UTC rather than in local time - EncodeDateTimeNumber(&Header.date, gmtime(&now)); - - Header.magic = TransportValue32(icMagicNumber); - -#ifdef NON_WINDOWS - Header.platform = (icPlatformSignature)TransportValue32(icSigMacintosh); -#else - Header.platform = (icPlatformSignature)TransportValue32(icSigMicrosoft); -#endif - - Header.flags = TransportValue32(Icc -> flags); - Header.manufacturer = TransportValue32(lcmsSignature); - Header.model = TransportValue32(0); - Header.attributes[0]= TransportValue32(Icc -> attributes); - Header.attributes[1]= TransportValue32(0); - - Header.renderingIntent = TransportValue32(Icc -> RenderingIntent); - - // Illuminant is D50 - - Header.illuminant.X = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.X)); - Header.illuminant.Y = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.Y)); - Header.illuminant.Z = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.Z)); - - Header.creator = TransportValue32(lcmsSignature); - - ZeroMemory(&Header.reserved, sizeof(Header.reserved)); - - // Set profile ID - CopyMemory(Header.reserved, Icc ->ProfileID, 16); - - - Icc ->UsedSpace = 0; // Mark as begin-of-file + if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE; - return Icc ->Write(Icc, sizeof(icHeader), &Header); -} - - - -// Setup base marker - -static -LCMSBOOL SetupBase(icTagTypeSignature sig, LPLCMSICCPROFILE Icc) -{ - icTagBase Base; - - Base.sig = (icTagTypeSignature) TransportValue32(sig); - ZeroMemory(&Base.reserved, sizeof(Base.reserved)); - return Icc -> Write(Icc, sizeof(icTagBase), &Base); -} - - -// Store a XYZ tag - -static -LCMSBOOL SaveXYZNumber(LPcmsCIEXYZ Value, LPLCMSICCPROFILE Icc) -{ - - icXYZNumber XYZ; - - if (!SetupBase(icSigXYZType, Icc)) return FALSE; + if (cmsGetEncodedICCversion(hProfile) >= 0x4000000) { - XYZ.X = TransportValue32(DOUBLE_TO_FIXED(Value -> X)); - XYZ.Y = TransportValue32(DOUBLE_TO_FIXED(Value -> Y)); - XYZ.Z = TransportValue32(DOUBLE_TO_FIXED(Value -> Z)); - - - return Icc -> Write(Icc, sizeof(icXYZNumber), &XYZ); -} - - -// Store a XYZ array. - -static -LCMSBOOL SaveXYZArray(int n, LPcmsCIEXYZ Value, LPLCMSICCPROFILE Icc) -{ - int i; - icXYZNumber XYZ; - - if (!SetupBase(icSigS15Fixed16ArrayType, Icc)) return FALSE; - - for (i=0; i < n; i++) { - - XYZ.X = TransportValue32(DOUBLE_TO_FIXED(Value -> X)); - XYZ.Y = TransportValue32(DOUBLE_TO_FIXED(Value -> Y)); - XYZ.Z = TransportValue32(DOUBLE_TO_FIXED(Value -> Z)); - - if (!Icc -> Write(Icc, sizeof(icXYZNumber), &XYZ)) return FALSE; - - Value++; + if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE; } return TRUE; } - -// Save a gamma structure as a table - +// Auxiliary, read and duplicate a MLU if found. static -LCMSBOOL SaveGammaTable(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc) -{ - icInt32Number Count; - - if (!SetupBase(icSigCurveType, Icc)) return FALSE; - - Count = TransportValue32(Gamma->nEntries); - - if (!Icc ->Write(Icc, sizeof(icInt32Number), &Count)) return FALSE; - - return SaveWordsTable(Gamma->nEntries, Gamma ->GammaTable, Icc); -} - - -// Save a gamma structure as a one-value - -static -LCMSBOOL SaveGammaOneValue(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc) +cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig) { - icInt32Number Count; - Fixed32 GammaFixed32; - WORD GammaFixed8; - - if (!SetupBase(icSigCurveType, Icc)) return FALSE; - - Count = TransportValue32(1); - if (!Icc ->Write(Icc, sizeof(icInt32Number), &Count)) return FALSE; - - GammaFixed32 = DOUBLE_TO_FIXED(Gamma ->Seed.Params[0]); - GammaFixed8 = (WORD) ((GammaFixed32 >> 8) & 0xFFFF); - GammaFixed8 = TransportValue16(GammaFixed8); - - return Icc ->Write(Icc, sizeof(icInt16Number), &GammaFixed8); -} - -// Save a gamma structure as a parametric gamma - -static -LCMSBOOL SaveGammaParametric(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc) -{ - icUInt16Number Type, Reserved; - int i, nParams; - int ParamsByType[] = { 1, 3, 4, 5, 7 }; + cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig); + if (mlu == NULL) return NULL; - if (!SetupBase(icSigParametricCurveType, Icc)) return FALSE; - - nParams = ParamsByType[Gamma -> Seed.Type]; - - Type = (icUInt16Number) TransportValue16((WORD) Gamma -> Seed. Type); - Reserved = (icUInt16Number) TransportValue16((WORD) 0); - - Icc -> Write(Icc, sizeof(icInt16Number), &Type); - Icc -> Write(Icc, sizeof(icUInt16Number), &Reserved); - - for (i=0; i < nParams; i++) { - - icInt32Number val = TransportValue32(DOUBLE_TO_FIXED(Gamma -> Seed.Params[i])); - Icc ->Write(Icc, sizeof(icInt32Number), &val); - } - - - return TRUE; - -} - - -// Save a gamma table - -static -LCMSBOOL SaveGamma(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc) -{ - // Is the gamma curve type supported by ICC format? - - if (Gamma -> Seed.Type < 0 || Gamma -> Seed.Type > 5 || - - // has been modified by user? - - _cmsCrc32OfGammaTable(Gamma) != Gamma -> Seed.Crc32) { - - return SaveGammaTable(Gamma, Icc); - } - - if (Gamma -> Seed.Type == 1) return SaveGammaOneValue(Gamma, Icc); - - // Only v4 profiles are allowed to hold parametric curves - - if (cmsGetProfileICCversion((cmsHPROFILE) Icc) >= 0x4000000) - return SaveGammaParametric(Gamma, Icc); - - // Defaults to save as table - - return SaveGammaTable(Gamma, Icc); - + return cmsMLUdup(mlu); } - - - -// Save an DESC Tag - -static -LCMSBOOL SaveDescription(const char *Text, LPLCMSICCPROFILE Icc) +// Create a sequence description out of an array of profiles +cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]) { + cmsUInt32Number i; + cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles); - icUInt32Number len, Count, TotalSize, AlignedSize; - char Filler[256]; + if (seq == NULL) return NULL; - len = (icUInt32Number) (strlen(Text) + 1); + for (i=0; i < nProfiles; i++) { - // * icInt8Number desc[count] * NULL terminated ascii string - // * icUInt32Number ucLangCode; * UniCode language code - // * icUInt32Number ucCount; * UniCode description length - // * icInt16Number ucDesc[ucCount];* The UniCode description - // * icUInt16Number scCode; * ScriptCode code - // * icUInt8Number scCount; * ScriptCode count - // * icInt8Number scDesc[67]; * ScriptCode Description + cmsPSEQDESC* ps = &seq ->seq[i]; + cmsHPROFILE h = hProfiles[i]; + cmsTechnologySignature* techpt; - TotalSize = sizeof(icTagBase) + sizeof(icUInt32Number) + len + - sizeof(icUInt32Number) + sizeof(icUInt32Number) + - sizeof(icUInt16Number) + sizeof(icUInt8Number) + 67; - - AlignedSize = TotalSize; // Can be unaligned!! - - if (!SetupBase(icSigTextDescriptionType, Icc)) return FALSE; - AlignedSize -= sizeof(icTagBase); - - Count = TransportValue32(len); - if (!Icc ->Write(Icc, sizeof(icUInt32Number), &Count)) return FALSE; - AlignedSize -= sizeof(icUInt32Number); + cmsGetHeaderAttributes(h, &ps ->attributes); + cmsGetHeaderProfileID(h, ps ->ProfileID.ID8); + ps ->deviceMfg = cmsGetHeaderManufacturer(h); + ps ->deviceModel = cmsGetHeaderModel(h); - if (!Icc ->Write(Icc, len, (LPVOID)Text)) return FALSE; - AlignedSize -= len; + techpt = (cmsTechnologySignature*) cmsReadTag(h, cmsSigTechnologyTag); + if (techpt == NULL) + ps ->technology = (cmsTechnologySignature) 0; + else + ps ->technology = *techpt; - if (AlignedSize < 0) - AlignedSize = 0; - if (AlignedSize > 255) - AlignedSize = 255; + ps ->Manufacturer = GetMLUFromProfile(h, cmsSigDeviceMfgDescTag); + ps ->Model = GetMLUFromProfile(h, cmsSigDeviceModelDescTag); + ps ->Description = GetMLUFromProfile(h, cmsSigProfileDescriptionTag); - ZeroMemory(Filler, AlignedSize); - if (!Icc ->Write(Icc, AlignedSize, Filler)) return FALSE; + } - return TRUE; + return seq; } -// Save an ASCII Tag - -static -LCMSBOOL SaveText(const char *Text, LPLCMSICCPROFILE Icc) -{ - size_t len = strlen(Text) + 1; - - if (!SetupBase(icSigTextType, Icc)) return FALSE; - if (!Icc ->Write(Icc, len, (LPVOID) Text)) return FALSE; - return TRUE; -} - - -// Save one of these new chromaticity values - -static -LCMSBOOL SaveOneChromaticity(double x, double y, LPLCMSICCPROFILE Icc) -{ - Fixed32 xf, yf; - - xf = TransportValue32(DOUBLE_TO_FIXED(x)); - yf = TransportValue32(DOUBLE_TO_FIXED(y)); - - if (!Icc ->Write(Icc, sizeof(Fixed32), &xf)) return FALSE; - if (!Icc ->Write(Icc, sizeof(Fixed32), &yf)) return FALSE; - - return TRUE; -} - - -// New tag added in Addendum II of old spec. - -static -LCMSBOOL SaveChromaticities(LPcmsCIExyYTRIPLE chrm, LPLCMSICCPROFILE Icc) -{ - WORD nChans, Table; - - if (!SetupBase(icSigChromaticityType, Icc)) return FALSE; - - nChans = TransportValue16(3); - if (!Icc ->Write(Icc, sizeof(WORD) , &nChans)) return FALSE; - Table = TransportValue16(0); - if (!Icc ->Write(Icc, sizeof(WORD) , &Table)) return FALSE; - - if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, Icc)) return FALSE; - if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, Icc)) return FALSE; - if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, Icc)) return FALSE; - - return TRUE; -} +// ------------------------------------------------------------------------------------------------------------------- static -LCMSBOOL SaveSequenceDescriptionTag(LPcmsSEQ seq, LPLCMSICCPROFILE Icc) +const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info) { - icUInt32Number nSeqs; - icDescStruct DescStruct; - int i, n = seq ->n; - LPcmsPSEQDESC pseq = seq ->seq; - - if (!SetupBase(icSigProfileSequenceDescType, Icc)) return FALSE; - - nSeqs = TransportValue32(n); - - if (!Icc ->Write(Icc, sizeof(icUInt32Number) , &nSeqs)) return FALSE; - - for (i=0; i < n; i++) { - - LPcmsPSEQDESC sec = pseq + i; - - - DescStruct.deviceMfg = (icTagTypeSignature) TransportValue32(sec ->deviceMfg); - DescStruct.deviceModel = (icTagTypeSignature) TransportValue32(sec ->deviceModel); - DescStruct.technology = (icTechnologySignature) TransportValue32(sec ->technology); - DescStruct.attributes[0]= TransportValue32(sec ->attributes[0]); - DescStruct.attributes[1]= TransportValue32(sec ->attributes[1]); + cmsTagSignature sig; - if (!Icc ->Write(Icc, sizeof(icDescStruct) - SIZEOF_UINT8_ALIGNED, &DescStruct)) return FALSE; - - if (!SaveDescription(sec ->Manufacturer, Icc)) return FALSE; - if (!SaveDescription(sec ->Model, Icc)) return FALSE; - } - - return TRUE; -} - - -// Saves a timestamp tag + switch (Info) { -static -LCMSBOOL SaveDateTimeNumber(const struct tm *DateTime, LPLCMSICCPROFILE Icc) -{ - icDateTimeNumber Dest; - - if (!SetupBase(icSigDateTimeType, Icc)) return FALSE; - EncodeDateTimeNumber(&Dest, DateTime); - if (!Icc ->Write(Icc, sizeof(icDateTimeNumber), &Dest)) return FALSE; - - return TRUE; -} - + case cmsInfoDescription: + sig = cmsSigProfileDescriptionTag; + break; -// Saves a named color list into a named color profile -static -LCMSBOOL SaveNamedColorList(LPcmsNAMEDCOLORLIST NamedColorList, LPLCMSICCPROFILE Icc) -{ - - icUInt32Number vendorFlag; // Bottom 16 bits for IC use - icUInt32Number count; // Count of named colors - icUInt32Number nDeviceCoords; // Num of device coordinates - char prefix[32]; // Prefix for each color name - char suffix[32]; // Suffix for each color name - int i; - - if (!SetupBase(icSigNamedColor2Type, Icc)) return FALSE; - - vendorFlag = TransportValue32(0); - count = TransportValue32(NamedColorList ->nColors); - nDeviceCoords = TransportValue32(NamedColorList ->ColorantCount); - - strncpy(prefix, (const char*) NamedColorList->Prefix, 31); - strncpy(suffix, (const char*) NamedColorList->Suffix, 31); - - suffix[31] = prefix[31] = 0; + case cmsInfoManufacturer: + sig = cmsSigDeviceMfgDescTag; + break; - if (!Icc ->Write(Icc, sizeof(icUInt32Number), &vendorFlag)) return FALSE; - if (!Icc ->Write(Icc, sizeof(icUInt32Number), &count)) return FALSE; - if (!Icc ->Write(Icc, sizeof(icUInt32Number), &nDeviceCoords)) return FALSE; - if (!Icc ->Write(Icc, 32 , prefix)) return FALSE; - if (!Icc ->Write(Icc, 32 , suffix)) return FALSE; - - for (i=0; i < NamedColorList ->nColors; i++) { - - icUInt16Number PCS[3]; - icUInt16Number Colorant[MAXCHANNELS]; - char root[32]; - LPcmsNAMEDCOLOR Color; - int j; - - Color = NamedColorList ->List + i; + case cmsInfoModel: + sig = cmsSigDeviceModelDescTag; + break; - strncpy(root, Color ->Name, 32); - Color ->Name[32] = 0; - - if (!Icc ->Write(Icc, 32 , root)) return FALSE; - - for (j=0; j < 3; j++) - PCS[j] = TransportValue16(Color ->PCS[j]); + case cmsInfoCopyright: + sig = cmsSigCopyrightTag; + break; - if (!Icc ->Write(Icc, 3 * sizeof(icUInt16Number), PCS)) return FALSE; - - for (j=0; j < NamedColorList ->ColorantCount; j++) - Colorant[j] = TransportValue16(Color ->DeviceColorant[j]); - - if (!Icc ->Write(Icc, - NamedColorList ->ColorantCount * sizeof(icUInt16Number), Colorant)) return FALSE; + default: return NULL; } - return TRUE; -} - - - -// Saves a colorant table. It is using the named color structure for simplicity sake - -static -LCMSBOOL SaveColorantTable(LPcmsNAMEDCOLORLIST NamedColorList, LPLCMSICCPROFILE Icc) -{ - icUInt32Number count; // Count of named colors - int i; - - if (!SetupBase(icSigColorantTableType, Icc)) return FALSE; - - count = TransportValue32(NamedColorList ->nColors); - - if (!Icc ->Write(Icc, sizeof(icUInt32Number), &count)) return FALSE; - - for (i=0; i < NamedColorList ->nColors; i++) { - - icUInt16Number PCS[3]; - icInt8Number root[33]; - LPcmsNAMEDCOLOR Color; - int j; - - Color = NamedColorList ->List + i; - - strncpy((char*) root, Color ->Name, 32); - root[32] = 0; - - if (!Icc ->Write(Icc, 32 , root)) return FALSE; - - for (j=0; j < 3; j++) - PCS[j] = TransportValue16(Color ->PCS[j]); - - if (!Icc ->Write(Icc, 3 * sizeof(icUInt16Number), PCS)) return FALSE; - - } - - - return TRUE; -} - -// Does serialization of LUT16 and writes it. - -static -LCMSBOOL SaveLUT(const LUT* NewLUT, LPLCMSICCPROFILE Icc) -{ - icLut16 LUT16; - unsigned int i; - size_t nTabSize; - WORD NullTbl[2] = { 0, 0xFFFFU}; - - - if (!SetupBase(icSigLut16Type, Icc)) return FALSE; - - LUT16.clutPoints = (icUInt8Number) NewLUT -> cLutPoints; - LUT16.inputChan = (icUInt8Number) NewLUT -> InputChan; - LUT16.outputChan = (icUInt8Number) NewLUT -> OutputChan; - - LUT16.inputEnt = TransportValue16((WORD) ((NewLUT -> wFlags & LUT_HASTL1) ? NewLUT -> InputEntries : 2)); - LUT16.outputEnt = TransportValue16((WORD) ((NewLUT -> wFlags & LUT_HASTL2) ? NewLUT -> OutputEntries : 2)); - - if (NewLUT -> wFlags & LUT_HASMATRIX) { - - LUT16.e00 = TransportValue32(NewLUT -> Matrix.v[0].n[0]); - LUT16.e01 = TransportValue32(NewLUT -> Matrix.v[0].n[1]); - LUT16.e02 = TransportValue32(NewLUT -> Matrix.v[0].n[2]); - LUT16.e10 = TransportValue32(NewLUT -> Matrix.v[1].n[0]); - LUT16.e11 = TransportValue32(NewLUT -> Matrix.v[1].n[1]); - LUT16.e12 = TransportValue32(NewLUT -> Matrix.v[1].n[2]); - LUT16.e20 = TransportValue32(NewLUT -> Matrix.v[2].n[0]); - LUT16.e21 = TransportValue32(NewLUT -> Matrix.v[2].n[1]); - LUT16.e22 = TransportValue32(NewLUT -> Matrix.v[2].n[2]); - } - else { - - LUT16.e00 = TransportValue32(DOUBLE_TO_FIXED(1)); - LUT16.e01 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT16.e02 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT16.e10 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT16.e11 = TransportValue32(DOUBLE_TO_FIXED(1)); - LUT16.e12 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT16.e20 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT16.e21 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT16.e22 = TransportValue32(DOUBLE_TO_FIXED(1)); - } - - - // Save header - - Icc -> Write(Icc, sizeof(icLut16)- SIZEOF_UINT16_ALIGNED, &LUT16); - - // The prelinearization table - - for (i=0; i < NewLUT -> InputChan; i++) { - - if (NewLUT -> wFlags & LUT_HASTL1) { - - if (!SaveWordsTable(NewLUT -> InputEntries, - NewLUT -> L1[i], Icc)) return FALSE; - - } - else Icc -> Write(Icc, sizeof(WORD)* 2, NullTbl); - } - - - nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints, - NewLUT->InputChan)); - // The 3D CLUT. - - if (nTabSize > 0) { - - if (!SaveWordsTable((int) nTabSize, NewLUT -> T, Icc)) return FALSE; - } - // The postlinearization table - - for (i=0; i < NewLUT -> OutputChan; i++) { - - if (NewLUT -> wFlags & LUT_HASTL2) { - - if (!SaveWordsTable(NewLUT -> OutputEntries, - NewLUT -> L2[i], Icc)) return FALSE; - } - else Icc -> Write(Icc, sizeof(WORD)* 2, NullTbl); - - } - - return TRUE; + return (cmsMLU*) cmsReadTag(hProfile, sig); } -// Does serialization of LUT8 and writes it - -static -LCMSBOOL SaveLUT8(const LUT* NewLUT, LPLCMSICCPROFILE Icc) +cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize) { - icLut8 LUT8; - unsigned int i, j; - size_t nTabSize; - BYTE val; - - // Sanity check - - if (NewLUT -> wFlags & LUT_HASTL1) { - - if (NewLUT -> InputEntries != 256) { - cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on prelinearization"); - return FALSE; - } - - } - - - if (NewLUT -> wFlags & LUT_HASTL2) { - - if (NewLUT -> OutputEntries != 256) { - cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on postlinearization"); - return FALSE; - } - } - - - - if (!SetupBase(icSigLut8Type, Icc)) return FALSE; - - LUT8.clutPoints = (icUInt8Number) NewLUT -> cLutPoints; - LUT8.inputChan = (icUInt8Number) NewLUT -> InputChan; - LUT8.outputChan = (icUInt8Number) NewLUT -> OutputChan; - - - if (NewLUT -> wFlags & LUT_HASMATRIX) { - - LUT8.e00 = TransportValue32(NewLUT -> Matrix.v[0].n[0]); - LUT8.e01 = TransportValue32(NewLUT -> Matrix.v[0].n[1]); - LUT8.e02 = TransportValue32(NewLUT -> Matrix.v[0].n[2]); - LUT8.e10 = TransportValue32(NewLUT -> Matrix.v[1].n[0]); - LUT8.e11 = TransportValue32(NewLUT -> Matrix.v[1].n[1]); - LUT8.e12 = TransportValue32(NewLUT -> Matrix.v[1].n[2]); - LUT8.e20 = TransportValue32(NewLUT -> Matrix.v[2].n[0]); - LUT8.e21 = TransportValue32(NewLUT -> Matrix.v[2].n[1]); - LUT8.e22 = TransportValue32(NewLUT -> Matrix.v[2].n[2]); - } - else { - - LUT8.e00 = TransportValue32(DOUBLE_TO_FIXED(1)); - LUT8.e01 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT8.e02 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT8.e10 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT8.e11 = TransportValue32(DOUBLE_TO_FIXED(1)); - LUT8.e12 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT8.e20 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT8.e21 = TransportValue32(DOUBLE_TO_FIXED(0)); - LUT8.e22 = TransportValue32(DOUBLE_TO_FIXED(1)); - } - - - // Save header - - Icc -> Write(Icc, sizeof(icLut8)- SIZEOF_UINT8_ALIGNED, &LUT8); - - // The prelinearization table - - for (i=0; i < NewLUT -> InputChan; i++) { - - for (j=0; j < 256; j++) { - - if (NewLUT -> wFlags & LUT_HASTL1) - val = (BYTE) floor(NewLUT ->L1[i][j] / 257.0 + .5); - else - val = (BYTE) j; + const cmsMLU* mlu = GetInfo(hProfile, Info); + if (mlu == NULL) return 0; - Icc ->Write(Icc, 1, &val); - } - - } - - - nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints, - NewLUT->InputChan)); - // The 3D CLUT. - - for (j=0; j < nTabSize; j++) { - - val = (BYTE) floor(NewLUT ->T[j] / 257.0 + .5); - Icc ->Write(Icc, 1, &val); - } - - // The postlinearization table - - for (i=0; i < NewLUT -> OutputChan; i++) { - - for (j=0; j < 256; j++) { - - if (NewLUT -> wFlags & LUT_HASTL2) - val = (BYTE) floor(NewLUT ->L2[i][j] / 257.0 + .5); - else - val = (BYTE) j; - - Icc ->Write(Icc, 1, &val); - } - - } - - return TRUE; -} - - - -// Set the LUT bitdepth to be saved - -void LCMSEXPORT _cmsSetLUTdepth(cmsHPROFILE hProfile, int depth) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - - switch (depth) { - - case 8: Icc ->SaveAs8Bits = TRUE; break; - case 16: Icc ->SaveAs8Bits = FALSE; break; - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "%d is an unsupported as bitdepth, use 8 or 16 only.", depth); - } -} - - -// Saves Tag directory - -static -LCMSBOOL SaveTagDirectory(LPLCMSICCPROFILE Icc) -{ - icInt32Number i; - icTag Tag; - icInt32Number Count = 0; - - // Get true count - for (i=0; i < Icc -> TagCount; i++) { - if (Icc ->TagNames[i] != 0) - Count++; - } - - Count = TransportValue32(Count); - if (!Icc ->Write(Icc, sizeof(icInt32Number) , &Count)) return FALSE; - - for (i=0; i < Icc -> TagCount; i++) { - - if (Icc ->TagNames[i] == 0) continue; - - Tag.sig = (icTagSignature)TransportValue32(Icc -> TagNames[i]); - Tag.offset = TransportValue32((icInt32Number) Icc -> TagOffsets[i]); - Tag.size = TransportValue32((icInt32Number) Icc -> TagSizes[i]); - - if (!Icc ->Write(Icc, sizeof(icTag), &Tag)) return FALSE; - } - - return TRUE; + return cmsMLUgetWide(mlu, LanguageCode, CountryCode, Buffer, BufferSize); } -// Dump tag contents - -static -LCMSBOOL SaveTags(LPLCMSICCPROFILE Icc, LPLCMSICCPROFILE FileOrig) +cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize) { - - LPBYTE Data; - icInt32Number i; - size_t Begin; - size_t AlignedSpace, FillerSize; - - - for (i=0; i < Icc -> TagCount; i++) { - - if (Icc ->TagNames[i] == 0) continue; - - // Align to DWORD boundary, following new spec. - - AlignedSpace = ALIGNLONG(Icc ->UsedSpace); - FillerSize = AlignedSpace - Icc ->UsedSpace; - if (FillerSize > 0) { - - BYTE Filler[20]; - - ZeroMemory(Filler, 16); - if (!Icc ->Write(Icc, FillerSize, Filler)) return FALSE; - } - - - Icc -> TagOffsets[i] = Begin = Icc ->UsedSpace; - Data = (LPBYTE) Icc -> TagPtrs[i]; - if (!Data) { - - // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. - // In this case a blind copy of the block data is performed - - if (Icc -> TagOffsets[i]) { - - size_t TagSize = FileOrig -> TagSizes[i]; - size_t TagOffset = FileOrig -> TagOffsets[i]; - void* Mem; - - if (FileOrig ->Seek(FileOrig, TagOffset)) return FALSE; - - Mem = _cmsMalloc(TagSize); - - if (FileOrig ->Read(Mem, TagSize, 1, FileOrig) != 1) return FALSE; - if (!Icc ->Write(Icc, TagSize, Mem)) return FALSE; - - Icc -> TagSizes[i] = (Icc ->UsedSpace - Begin); - free(Mem); - } - - continue; - } - - - switch (Icc -> TagNames[i]) { - - case icSigProfileDescriptionTag: - case icSigDeviceMfgDescTag: - case icSigDeviceModelDescTag: - if (!SaveDescription((const char *) Data, Icc)) return FALSE; - break; - - case icSigRedColorantTag: - case icSigGreenColorantTag: - case icSigBlueColorantTag: - case icSigMediaWhitePointTag: - case icSigMediaBlackPointTag: - if (!SaveXYZNumber((LPcmsCIEXYZ) Data, Icc)) return FALSE; - break; - - - case icSigRedTRCTag: - case icSigGreenTRCTag: - case icSigBlueTRCTag: - case icSigGrayTRCTag: - if (!SaveGamma((LPGAMMATABLE) Data, Icc)) return FALSE; - break; - - case icSigCharTargetTag: - case icSigCopyrightTag: - if (!SaveText((const char *) Data, Icc)) return FALSE; - break; - - case icSigChromaticityTag: - if (!SaveChromaticities((LPcmsCIExyYTRIPLE) Data, Icc)) return FALSE; - break; - - // Save LUT - - case icSigAToB0Tag: - case icSigAToB1Tag: - case icSigAToB2Tag: - case icSigBToA0Tag: - case icSigBToA1Tag: - case icSigBToA2Tag: - case icSigGamutTag: - case icSigPreview0Tag: - case icSigPreview1Tag: - case icSigPreview2Tag: - - if (Icc ->SaveAs8Bits) { - - if (!SaveLUT8((LPLUT) Data, Icc)) return FALSE; - } - else { - - if (!SaveLUT((LPLUT) Data, Icc)) return FALSE; - } - break; - - case icSigProfileSequenceDescTag: - if (!SaveSequenceDescriptionTag((LPcmsSEQ) Data, Icc)) return FALSE; - break; - - - case icSigNamedColor2Tag: - if (!SaveNamedColorList((LPcmsNAMEDCOLORLIST) Data, Icc)) return FALSE; - break; - - - case icSigCalibrationDateTimeTag: - if (!SaveDateTimeNumber((struct tm *) Data, Icc)) return FALSE; - break; - - - case icSigColorantTableTag: - case icSigColorantTableOutTag: - if (!SaveColorantTable((LPcmsNAMEDCOLORLIST) Data, Icc)) return FALSE; - break; - - - case icSigChromaticAdaptationTag: - if (!SaveXYZArray(3, (LPcmsCIEXYZ) Data, Icc)) return FALSE; - break; - - default: - return FALSE; - } - - Icc -> TagSizes[i] = (Icc ->UsedSpace - Begin); - } - - - - return TRUE; -} - - - -// Add tags to profile structure - -LCMSBOOL LCMSEXPORT cmsAddTag(cmsHPROFILE hProfile, icTagSignature sig, const void* Tag) -{ - LCMSBOOL rc; - - switch (sig) { - - case icSigCharTargetTag: - case icSigCopyrightTag: - case icSigProfileDescriptionTag: - case icSigDeviceMfgDescTag: - case icSigDeviceModelDescTag: - rc = _cmsAddTextTag(hProfile, sig, (const char*) Tag); - break; + const cmsMLU* mlu = GetInfo(hProfile, Info); + if (mlu == NULL) return 0; - case icSigRedColorantTag: - case icSigGreenColorantTag: - case icSigBlueColorantTag: - case icSigMediaWhitePointTag: - case icSigMediaBlackPointTag: - rc = _cmsAddXYZTag(hProfile, sig, (const cmsCIEXYZ*) Tag); - break; - - case icSigRedTRCTag: - case icSigGreenTRCTag: - case icSigBlueTRCTag: - case icSigGrayTRCTag: - rc = _cmsAddGammaTag(hProfile, sig, (LPGAMMATABLE) Tag); - break; - - case icSigAToB0Tag: - case icSigAToB1Tag: - case icSigAToB2Tag: - case icSigBToA0Tag: - case icSigBToA1Tag: - case icSigBToA2Tag: - case icSigGamutTag: - case icSigPreview0Tag: - case icSigPreview1Tag: - case icSigPreview2Tag: - rc = _cmsAddLUTTag(hProfile, sig, Tag); - break; - - case icSigChromaticityTag: - rc = _cmsAddChromaticityTag(hProfile, sig, (LPcmsCIExyYTRIPLE) Tag); - break; - - case icSigProfileSequenceDescTag: - rc = _cmsAddSequenceDescriptionTag(hProfile, sig, (LPcmsSEQ) Tag); - break; - - case icSigNamedColor2Tag: - rc = _cmsAddNamedColorTag(hProfile, sig, (LPcmsNAMEDCOLORLIST) Tag); - break; - - case icSigCalibrationDateTimeTag: - rc = _cmsAddDateTimeTag(hProfile, sig, (struct tm*) Tag); - break; - - case icSigColorantTableTag: - case icSigColorantTableOutTag: - rc = _cmsAddColorantTableTag(hProfile, sig, (LPcmsNAMEDCOLORLIST) Tag); - break; - - - case icSigChromaticAdaptationTag: - rc = _cmsAddChromaticAdaptationTag(hProfile, sig, (const cmsCIEXYZ*) Tag); - break; - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "cmsAddTag: Tag '%x' is unsupported", sig); - return FALSE; - } - - // Check for critical tags - - switch (sig) { - - case icSigMediaWhitePointTag: - case icSigMediaBlackPointTag: - case icSigChromaticAdaptationTag: - - ReadCriticalTags((LPLCMSICCPROFILE) hProfile); - break; - - default:; - } - - return rc; - + return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize); } - -// Low-level save to disk. It closes the profile on exit - -LCMSBOOL LCMSEXPORT _cmsSaveProfile(cmsHPROFILE hProfile, const char* FileName) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - LCMSICCPROFILE Keep; - LCMSBOOL rc; - - CopyMemory(&Keep, Icc, sizeof(LCMSICCPROFILE)); - _cmsSetSaveToDisk(Icc, NULL); - - // Pass #1 does compute offsets - - if (!SaveHeader(Icc)) return FALSE; - if (!SaveTagDirectory(Icc)) return FALSE; - if (!SaveTags(Icc, &Keep)) return FALSE; - - - _cmsSetSaveToDisk(Icc, FileName); - - - // Pass #2 does save to file - - if (!SaveHeader(Icc)) goto CleanUp; - if (!SaveTagDirectory(Icc)) goto CleanUp; - if (!SaveTags(Icc, &Keep)) goto CleanUp; - - rc = (Icc ->Close(Icc) == 0); - CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE)); - return rc; - - - CleanUp: - - Icc ->Close(Icc); - unlink(FileName); - CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE)); - return FALSE; -} - - -// Low-level save from open stream -LCMSBOOL LCMSEXPORT _cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, - size_t* BytesNeeded) -{ - LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile; - LCMSICCPROFILE Keep; - - - CopyMemory(&Keep, Icc, sizeof(LCMSICCPROFILE)); - - _cmsSetSaveToMemory(Icc, NULL, 0); - - // Pass #1 does compute offsets - - if (!SaveHeader(Icc)) return FALSE; - if (!SaveTagDirectory(Icc)) return FALSE; - if (!SaveTags(Icc, &Keep)) return FALSE; - - if (!MemPtr) { - - // update BytesSaved so caller knows how many bytes are needed for MemPtr - *BytesNeeded = Icc ->UsedSpace; - CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE)); - return TRUE; - } - - if (*BytesNeeded < Icc ->UsedSpace) { - - // need at least UsedSpace in MemPtr to continue - CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE)); - return FALSE; - } - - _cmsSetSaveToMemory(Icc, MemPtr, *BytesNeeded); - - - // Pass #2 does save to file into supplied stream - if (!SaveHeader(Icc)) goto CleanUp; - if (!SaveTagDirectory(Icc)) goto CleanUp; - if (!SaveTags(Icc, &Keep)) goto CleanUp; - - // update BytesSaved so caller knows how many bytes put into stream - *BytesNeeded = Icc ->UsedSpace; - - Icc ->Close(Icc); - CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE)); - return TRUE; - -CleanUp: - - Icc ->Close(Icc); - CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE)); - return FALSE; -} - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmslut.c --- a/src/share/native/sun/java2d/cmm/lcms/cmslut.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmslut.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,630 +49,1664 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include "lcms.h" - -// Pipeline of LUT. Enclosed by {} are new revision 4.0 of ICC spec. // -// [Mat] -> [L1] -> { [Mat3] -> [Ofs3] -> [L3] ->} [CLUT] { -> [L4] -> [Mat4] -> [Ofs4] } -> [L2] -// -// Some of these stages would be missing. This implements the totality of -// combinations of old and new LUT types as follows: -// -// Lut8 & Lut16 -// ============ -// [Mat] -> [L1] -> [CLUT] -> [L2] -// -// Mat2, Ofs2, L3, L3, Mat3, Ofs3 are missing -// -// LutAToB -// ======== -// -// [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2] -// -// Mat, Mat3, Ofs3, L3 are missing -// L1 = A curves -// L4 = M curves -// L2 = B curves -// -// LutBToA -// ======= -// -// [L1] -> [Mat3] -> [Ofs3] -> [L3] -> [CLUT] -> [L2] +//--------------------------------------------------------------------------------- // -// Mat, L4, Mat4, Ofs4 are missing -// L1 = B Curves -// L3 = M Curves -// L2 = A curves -// -// -// V2&3 emulation -// =============== -// -// For output, Mat is multiplied by -// -// -// | 0xff00 / 0xffff 0 0 | -// | 0 0xff00 / 0xffff 0 | -// | 0 0 0xff00 / 0xffff | -// -// -// For input, an additional matrix is needed at the very last end of the chain -// -// -// | 0xffff / 0xff00 0 0 | -// | 0 0xffff / 0xff00 0 | -// | 0 0 0xffff / 0xff00 | -// -// -// Which reduces to (val * 257) >> 8 + +#include "lcms2_internal.h" + + +// Allocates an empty multi profile element +cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, + cmsStageSignature Type, + cmsUInt32Number InputChannels, + cmsUInt32Number OutputChannels, + _cmsStageEvalFn EvalPtr, + _cmsStageDupElemFn DupElemPtr, + _cmsStageFreeElemFn FreePtr, + void* Data) +{ + cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage)); + + if (ph == NULL) return NULL; + + + ph ->ContextID = ContextID; + + ph ->Type = Type; + ph ->Implements = Type; // By default, no clue on what is implementing + + ph ->InputChannels = InputChannels; + ph ->OutputChannels = OutputChannels; + ph ->EvalPtr = EvalPtr; + ph ->DupElemPtr = DupElemPtr; + ph ->FreePtr = FreePtr; + ph ->Data = Data; + + return ph; +} + -// A couple of macros to convert between revisions +static +void EvaluateIdentity(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number)); +} + + +cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels) +{ + return _cmsStageAllocPlaceholder(ContextID, + cmsSigIdentityElemType, + nChannels, nChannels, + EvaluateIdentity, + NULL, + NULL, + NULL); + } -#define FROM_V2_TO_V4(x) (((((x)<<8)+(x))+0x80)>>8) // BY 65535 DIV 65280 ROUND -#define FROM_V4_TO_V2(x) ((((x)<<8)+0x80)/257) // BY 65280 DIV 65535 ROUND +// Conversion functions. From floating point to 16 bits +static +void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0); + } +} + +// From 16 bits to floating point +static +void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + Out[i] = (cmsFloat32Number) In[i] / 65535.0F; + } +} -// Lut Creation & Destruction +// This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements +// that conform the LUT. It should be called with the LUT, the number of expected elements and +// then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If +// the function founds a match with current pipeline, it fills the pointers and returns TRUE +// if not, returns FALSE without touching anything. Setting pointers to NULL does bypass +// the storage process. +cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...) +{ + va_list args; + cmsUInt32Number i; + cmsStage* mpe; + cmsStageSignature Type; + void** ElemPtr; + + // Make sure same number of elements + if (cmsPipelineStageCount(Lut) != n) return FALSE; + + va_start(args, n); + + // Iterate across asked types + mpe = Lut ->Elements; + for (i=0; i < n; i++) { -LPLUT LCMSEXPORT cmsAllocLUT(void) -{ - LPLUT NewLUT; + // Get asked type. cmsStageSignature is promoted to int by compiler + Type = (cmsStageSignature)va_arg(args, int); + if (mpe ->Type != Type) { + + va_end(args); // Mismatch. We are done. + return FALSE; + } + mpe = mpe ->Next; + } + + // Found a combination, fill pointers if not NULL + mpe = Lut ->Elements; + for (i=0; i < n; i++) { + + ElemPtr = va_arg(args, void**); + if (ElemPtr != NULL) + *ElemPtr = mpe; + + mpe = mpe ->Next; + } + + va_end(args); + return TRUE; +} + +// Below there are implementations for several types of elements. Each type may be implemented by a +// evaluation function, a duplication function, a function to free resources and a constructor. - NewLUT = (LPLUT) _cmsMalloc(sizeof(LUT)); - if (NewLUT) - ZeroMemory(NewLUT, sizeof(LUT)); +// ************************************************************************************************* +// Type cmsSigCurveSetElemType (curves) +// ************************************************************************************************* + +cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; + + return Data ->TheCurves; +} + +static +void EvaluateCurves(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + _cmsStageToneCurvesData* Data; + cmsUInt32Number i; + + _cmsAssert(mpe != NULL); + + Data = (_cmsStageToneCurvesData*) mpe ->Data; + if (Data == NULL) return; - return NewLUT; + if (Data ->TheCurves == NULL) return; + + for (i=0; i < Data ->nCurves; i++) { + Out[i] = cmsEvalToneCurveFloat(Data ->TheCurves[i], In[i]); + } +} + +static +void CurveSetElemTypeFree(cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data; + cmsUInt32Number i; + + _cmsAssert(mpe != NULL); + + Data = (_cmsStageToneCurvesData*) mpe ->Data; + if (Data == NULL) return; + + if (Data ->TheCurves != NULL) { + for (i=0; i < Data ->nCurves; i++) { + if (Data ->TheCurves[i] != NULL) + cmsFreeToneCurve(Data ->TheCurves[i]); + } + } + _cmsFree(mpe ->ContextID, Data ->TheCurves); + _cmsFree(mpe ->ContextID, Data); } -void LCMSEXPORT cmsFreeLUT(LPLUT Lut) + +static +void* CurveSetDup(cmsStage* mpe) { - unsigned int i; + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; + _cmsStageToneCurvesData* NewElem; + cmsUInt32Number i; + + NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageToneCurvesData)); + if (NewElem == NULL) return NULL; - if (!Lut) return; + NewElem ->nCurves = Data ->nCurves; + NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(mpe ->ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*)); + + if (NewElem ->TheCurves == NULL) goto Error; - if (Lut -> T) free(Lut -> T); + for (i=0; i < NewElem ->nCurves; i++) { + + // Duplicate each curve. It may fail. + NewElem ->TheCurves[i] = cmsDupToneCurve(Data ->TheCurves[i]); + if (NewElem ->TheCurves[i] == NULL) goto Error; + - for (i=0; i < Lut -> OutputChan; i++) - { - if (Lut -> L2[i]) free(Lut -> L2[i]); - } + } + return (void*) NewElem; + +Error: - for (i=0; i < Lut -> InputChan; i++) - { + if (NewElem ->TheCurves != NULL) { + for (i=0; i < NewElem ->nCurves; i++) { + if (NewElem ->TheCurves[i]) + cmsFreeToneCurve(NewElem ->TheCurves[i]); + } + } + _cmsFree(mpe ->ContextID, NewElem ->TheCurves); + _cmsFree(mpe ->ContextID, NewElem); + return NULL; +} - if (Lut -> L1[i]) free(Lut -> L1[i]); - } + +// Curves == NULL forces identity curves +cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]) +{ + cmsUInt32Number i; + _cmsStageToneCurvesData* NewElem; + cmsStage* NewMPE; - if (Lut ->wFlags & LUT_HASTL3) { + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels, + EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL ); + if (NewMPE == NULL) return NULL; + + NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } - for (i=0; i < Lut -> InputChan; i++) { + NewMPE ->Data = (void*) NewElem; + + NewElem ->nCurves = nChannels; + NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*)); + if (NewElem ->TheCurves == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + for (i=0; i < nChannels; i++) { - if (Lut -> L3[i]) free(Lut -> L3[i]); - } - } + if (Curves == NULL) { + NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0); + } + else { + NewElem ->TheCurves[i] = cmsDupToneCurve(Curves[i]); + } + + if (NewElem ->TheCurves[i] == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + } - if (Lut ->wFlags & LUT_HASTL4) { + return NewMPE; +} + + +// Create a bunch of identity curves +cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels) +{ + cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL); + + if (mpe == NULL) return NULL; + mpe ->Implements = cmsSigIdentityElemType; + return mpe; +} + - for (i=0; i < Lut -> OutputChan; i++) { +// ************************************************************************************************* +// Type cmsSigMatrixElemType (Matrices) +// ************************************************************************************************* + + +// Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used +static +void EvaluateMatrix(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + cmsUInt32Number i, j; + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + cmsFloat64Number Tmp; + + // Input is already in 0..1.0 notation + for (i=0; i < mpe ->OutputChannels; i++) { + + Tmp = 0; + for (j=0; j < mpe->InputChannels; j++) { + Tmp += In[j] * Data->Double[i*mpe->InputChannels + j]; + } + + if (Data ->Offset != NULL) + Tmp += Data->Offset[i]; - if (Lut -> L4[i]) free(Lut -> L4[i]); - } - } + Out[i] = (cmsFloat32Number) Tmp; + } + + + // Output in 0..1.0 domain +} + - if (Lut ->CLut16params.p8) - free(Lut ->CLut16params.p8); +// Duplicate a yet-existing matrix element +static +void* MatrixElemDup(cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + _cmsStageMatrixData* NewElem; + cmsUInt32Number sz; - free(Lut); + NewElem = (_cmsStageMatrixData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageMatrixData)); + if (NewElem == NULL) return NULL; + + sz = mpe ->InputChannels * mpe ->OutputChannels; + + NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ; + + if (Data ->Offset) + NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, + Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ; + + return (void*) NewElem; } static -LPVOID DupBlockTab(LPVOID Org, size_t size) +void MatrixElemTypeFree(cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + if (Data == NULL) + return; + if (Data ->Double) + _cmsFree(mpe ->ContextID, Data ->Double); + + if (Data ->Offset) + _cmsFree(mpe ->ContextID, Data ->Offset); + + _cmsFree(mpe ->ContextID, mpe ->Data); +} + + + +cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, + const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset) { - LPVOID mem = _cmsMalloc(size); - if (mem != NULL) - CopyMemory(mem, Org, size); + cmsUInt32Number i, n; + _cmsStageMatrixData* NewElem; + cmsStage* NewMPE; + + n = Rows * Cols; + + // Check for overflow + if (n == 0) return NULL; + if (n >= UINT_MAX / Cols) return NULL; + if (n >= UINT_MAX / Rows) return NULL; + if (n < Rows || n < Cols) return NULL; + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows, + EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL ); + if (NewMPE == NULL) return NULL; + + + NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData)); + if (NewElem == NULL) return NULL; + + + NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number)); - return mem; + if (NewElem->Double == NULL) { + MatrixElemTypeFree(NewMPE); + return NULL; + } + + for (i=0; i < n; i++) { + NewElem ->Double[i] = Matrix[i]; + } + + + if (Offset != NULL) { + + NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number)); + if (NewElem->Offset == NULL) { + MatrixElemTypeFree(NewMPE); + return NULL; + } + + for (i=0; i < Rows; i++) { + NewElem ->Offset[i] = Offset[i]; + } + + } + + NewMPE ->Data = (void*) NewElem; + return NewMPE; } -LPLUT LCMSEXPORT cmsDupLUT(LPLUT Orig) +// ************************************************************************************************* +// Type cmsSigCLutElemType +// ************************************************************************************************* + + +// Evaluate in true floating point +static +void EvaluateCLUTfloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + + Data -> Params ->Interpolation.LerpFloat(In, Out, Data->Params); +} + + +// Convert to 16 bits, evaluate, and back to floating point +static +void EvaluateCLUTfloatIn16(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) { - LPLUT NewLUT = cmsAllocLUT(); - unsigned int i; + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS]; + + _cmsAssert(mpe ->InputChannels <= MAX_STAGE_CHANNELS); + _cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS); + + FromFloatTo16(In, In16, mpe ->InputChannels); + Data -> Params ->Interpolation.Lerp16(In16, Out16, Data->Params); + From16ToFloat(Out16, Out, mpe ->OutputChannels); +} + - CopyMemory(NewLUT, Orig, sizeof(LUT)); +// Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes +static +cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b) +{ + cmsUInt32Number rv, dim; + + _cmsAssert(Dims != NULL); + + for (rv = 1; b > 0; b--) { + + dim = Dims[b-1]; + if (dim == 0) return 0; // Error + + rv *= dim; - for (i=0; i < Orig ->InputChan; i++) - NewLUT -> L1[i] = (LPWORD) DupBlockTab((LPVOID) Orig ->L1[i], - sizeof(WORD) * Orig ->In16params.nSamples); + // Check for overflow + if (rv > UINT_MAX / dim) return 0; + } + + return rv; +} + +static +void* CLUTElemDup(cmsStage* mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + _cmsStageCLutData* NewElem; + + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) return NULL; + + NewElem ->nEntries = Data ->nEntries; + NewElem ->HasFloatValues = Data ->HasFloatValues; + + if (Data ->Tab.T) { - for (i=0; i < Orig ->OutputChan; i++) - NewLUT -> L2[i] = (LPWORD) DupBlockTab((LPVOID) Orig ->L2[i], - sizeof(WORD) * Orig ->Out16params.nSamples); + if (Data ->HasFloatValues) { + NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number)); + if (NewElem ->Tab.TFloat == NULL) + goto Error; + } else { + NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number)); + if (NewElem ->Tab.T == NULL) + goto Error; + } + } - NewLUT -> T = (LPWORD) DupBlockTab((LPVOID) Orig ->T, Orig -> Tsize); - - return NewLUT; + NewElem ->Params = _cmsComputeInterpParamsEx(mpe ->ContextID, + Data ->Params ->nSamples, + Data ->Params ->nInputs, + Data ->Params ->nOutputs, + NewElem ->Tab.T, + Data ->Params ->dwFlags); + if (NewElem->Params != NULL) + return (void*) NewElem; + Error: + if (NewElem->Tab.T) + // This works for both types + _cmsFree(mpe ->ContextID, NewElem -> Tab.T); + _cmsFree(mpe ->ContextID, NewElem); + return NULL; } static -unsigned int UIpow(unsigned int a, unsigned int b) +void CLutElemTypeFree(cmsStage* mpe) +{ + + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + + // Already empty + if (Data == NULL) return; + + // This works for both types + if (Data -> Tab.T) + _cmsFree(mpe ->ContextID, Data -> Tab.T); + + _cmsFreeInterpParams(Data ->Params); + _cmsFree(mpe ->ContextID, mpe ->Data); +} + + +// Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different +// granularity on each dimension. +cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, + const cmsUInt32Number clutPoints[], + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsUInt16Number* Table) { - unsigned int rv = 1; + cmsUInt32Number i, n; + _cmsStageCLutData* NewElem; + cmsStage* NewMPE; + + _cmsAssert(clutPoints != NULL); + + if (inputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, + EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL ); + + if (NewMPE == NULL) return NULL; + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); + NewElem -> HasFloatValues = FALSE; - for (; b > 0; b--) - rv *= a; + if (n == 0) { + cmsStageFree(NewMPE); + return NULL; + } + + + NewElem ->Tab.T = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number)); + if (NewElem ->Tab.T == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + if (Table != NULL) { + for (i=0; i < n; i++) { + NewElem ->Tab.T[i] = Table[i]; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS); + if (NewElem ->Params == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + return NewMPE; +} - return rv; +cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, + cmsUInt32Number nGridPoints, + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsUInt16Number* Table) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + // Our resulting LUT would be same gridpoints on all dimensions + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = nGridPoints; + + return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table); +} + + +cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, + cmsUInt32Number nGridPoints, + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsFloat32Number* Table) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + // Our resulting LUT would be same gridpoints on all dimensions + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = nGridPoints; + + return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table); } -LCMSBOOL _cmsValidateLUT(LPLUT NewLUT) + +cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table) { - unsigned int calc = 1; - unsigned int oldCalc; - unsigned int power = NewLUT -> InputChan; + cmsUInt32Number i, n; + _cmsStageCLutData* NewElem; + cmsStage* NewMPE; + + _cmsAssert(clutPoints != NULL); + + if (inputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } - if (NewLUT -> cLutPoints > 100) return FALSE; - if (NewLUT -> InputChan > MAXCHANNELS) return FALSE; - if (NewLUT -> OutputChan > MAXCHANNELS) return FALSE; + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, + EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL); + if (NewMPE == NULL) return NULL; + + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } - if (NewLUT -> cLutPoints == 0) return TRUE; + NewMPE ->Data = (void*) NewElem; - for (; power > 0; power--) { + // There is a potential integer overflow on conputing n and nEntries. + NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); + NewElem -> HasFloatValues = TRUE; + + if (n == 0) { + cmsStageFree(NewMPE); + return NULL; + } - oldCalc = calc; - calc *= NewLUT -> cLutPoints; + NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number)); + if (NewElem ->Tab.TFloat == NULL) { + cmsStageFree(NewMPE); + return NULL; + } - if (calc / NewLUT -> cLutPoints != oldCalc) { - return FALSE; - } + if (Table != NULL) { + for (i=0; i < n; i++) { + NewElem ->Tab.TFloat[i] = Table[i]; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT); + if (NewElem ->Params == NULL) { + cmsStageFree(NewMPE); + return NULL; } - oldCalc = calc; - calc *= NewLUT -> OutputChan; - if (NewLUT -> OutputChan && calc / NewLUT -> OutputChan != oldCalc) { - return FALSE; + return NewMPE; +} + + +static +int IdentitySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo) +{ + int nChan = *(int*) Cargo; + int i; + + for (i=0; i < nChan; i++) + Out[i] = In[i]; + + return 1; +} + +// Creates an MPE that just copies input to output +cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + cmsStage* mpe ; + int i; + + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = 2; + + mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL); + if (mpe == NULL) return NULL; + + if (!cmsStageSampleCLut16bit(mpe, IdentitySampler, &nChan, 0)) { + cmsStageFree(mpe); + return NULL; + } + + mpe ->Implements = cmsSigIdentityElemType; + return mpe; +} + + + +// Quantize a value 0 <= i < MaxSamples to 0..0xffff +cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples) +{ + cmsFloat64Number x; + + x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1); + return _cmsQuickSaturateWord(x); +} + + +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. +cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags) +{ + int i, t, index, rest; + cmsUInt32Number nTotalPoints; + cmsUInt32Number nInputs, nOutputs; + cmsUInt32Number* nSamples; + cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; + _cmsStageCLutData* clut; + + if (mpe == NULL) return FALSE; + + clut = (_cmsStageCLutData*) mpe->Data; + + if (clut == NULL) return FALSE; + + nSamples = clut->Params ->nSamples; + nInputs = clut->Params ->nInputs; + nOutputs = clut->Params ->nOutputs; + + if (nInputs <= 0) return FALSE; + if (nOutputs <= 0) return FALSE; + if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; + if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; + + memset(In, 0, sizeof(In)); + memset(Out, 0, sizeof(Out)); + + nTotalPoints = CubeSize(nSamples, nInputs); + if (nTotalPoints == 0) return FALSE; + + index = 0; + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int)nInputs - 1; t >= 0; --t) { + + cmsUInt32Number Colorant = rest % nSamples[t]; + + rest /= nSamples[t]; + + In[t] = _cmsQuantizeVal(Colorant, nSamples[t]); + } + + if (clut ->Tab.T != NULL) { + for (t = 0; t < (int)nOutputs; t++) + Out[t] = clut->Tab.T[index + t]; + } + + if (!Sampler(In, Out, Cargo)) + return FALSE; + + if (!(dwFlags & SAMPLER_INSPECT)) { + + if (clut ->Tab.T != NULL) { + for (t=0; t < (int) nOutputs; t++) + clut->Tab.T[index + t] = Out[t]; + } + } + + index += nOutputs; } return TRUE; } -LPLUT LCMSEXPORT cmsAlloc3DGrid(LPLUT NewLUT, int clutPoints, int inputChan, int outputChan) +// Same as anterior, but for floating point +cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags) { - DWORD nTabSize; + int i, t, index, rest; + cmsUInt32Number nTotalPoints; + cmsUInt32Number nInputs, nOutputs; + cmsUInt32Number* nSamples; + cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; + _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data; + + nSamples = clut->Params ->nSamples; + nInputs = clut->Params ->nInputs; + nOutputs = clut->Params ->nOutputs; - NewLUT -> wFlags |= LUT_HAS3DGRID; - NewLUT -> cLutPoints = clutPoints; - NewLUT -> InputChan = inputChan; - NewLUT -> OutputChan = outputChan; + if (nInputs <= 0) return FALSE; + if (nOutputs <= 0) return FALSE; + if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; + if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; - if (!_cmsValidateLUT(NewLUT)) { - return NULL; - } + nTotalPoints = CubeSize(nSamples, nInputs); + if (nTotalPoints == 0) return FALSE; + + index = 0; + for (i = 0; i < (int)nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { - nTabSize = NewLUT -> OutputChan * UIpow(NewLUT->cLutPoints, - NewLUT->InputChan); + cmsUInt32Number Colorant = rest % nSamples[t]; + + rest /= nSamples[t]; - NewLUT -> T = (LPWORD) _cmsCalloc(sizeof(WORD), nTabSize); - nTabSize *= sizeof(WORD); - if (NewLUT -> T == NULL) return NULL; + In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0); + } + + if (clut ->Tab.TFloat != NULL) { + for (t=0; t < (int) nOutputs; t++) + Out[t] = clut->Tab.TFloat[index + t]; + } - ZeroMemory(NewLUT -> T, nTabSize); - NewLUT ->Tsize = nTabSize; + if (!Sampler(In, Out, Cargo)) + return FALSE; + if (!(dwFlags & SAMPLER_INSPECT)) { - cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan, - NewLUT -> OutputChan, - &NewLUT -> CLut16params); + if (clut ->Tab.TFloat != NULL) { + for (t=0; t < (int) nOutputs; t++) + clut->Tab.TFloat[index + t] = Out[t]; + } + } - return NewLUT; + index += nOutputs; + } + + return TRUE; } - -LPLUT LCMSEXPORT cmsAllocLinearTable(LPLUT NewLUT, LPGAMMATABLE Tables[], int nTable) +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. +cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLER16 Sampler, void * Cargo) { - unsigned int i; - LPWORD PtrW; + int i, t, rest; + cmsUInt32Number nTotalPoints; + cmsUInt16Number In[cmsMAXCHANNELS]; + + if (nInputs >= cmsMAXCHANNELS) return FALSE; + + nTotalPoints = CubeSize(clutPoints, nInputs); + if (nTotalPoints == 0) return FALSE; + + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % clutPoints[t]; + + rest /= clutPoints[t]; + In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]); + + } + + if (!Sampler(In, NULL, Cargo)) + return FALSE; + } + + return TRUE; +} - switch (nTable) { +cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLERFLOAT Sampler, void * Cargo) +{ + int i, t, rest; + cmsUInt32Number nTotalPoints; + cmsFloat32Number In[cmsMAXCHANNELS]; + + if (nInputs >= cmsMAXCHANNELS) return FALSE; + + nTotalPoints = CubeSize(clutPoints, nInputs); + if (nTotalPoints == 0) return FALSE; + + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % clutPoints[t]; + + rest /= clutPoints[t]; + In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0); + + } + + if (!Sampler(In, NULL, Cargo)) + return FALSE; + } + + return TRUE; +} + +// ******************************************************************************** +// Type cmsSigLab2XYZElemType +// ******************************************************************************** - case 1: NewLUT -> wFlags |= LUT_HASTL1; - cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> In16params); - NewLUT -> InputEntries = Tables[0] -> nEntries; +static +void EvaluateLab2XYZ(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + cmsCIELab Lab; + cmsCIEXYZ XYZ; + const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; + + // V4 rules + Lab.L = In[0] * 100.0; + Lab.a = In[1] * 255.0 - 128.0; + Lab.b = In[2] * 255.0 - 128.0; + + cmsLab2XYZ(NULL, &XYZ, &Lab); + + // From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff + // encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0) + + Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj); + Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj); + Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj); + return; + + cmsUNUSED_PARAMETER(mpe); +} - for (i=0; i < NewLUT -> InputChan; i++) { + +// No dup or free routines needed, as the structure has no pointers in it. +cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL); +} + +// ******************************************************************************** + +// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable +// number of gridpoints that would make exact match. However, a prelinearization +// of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot. +// Almost all what we need but unfortunately, the rest of entries should be scaled by +// (255*257/256) and this is not exact. + +cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID) +{ + cmsStage* mpe; + cmsToneCurve* LabTable[3]; + int i, j; + + LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + + for (j=0; j < 3; j++) { - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> InputEntries); - if (PtrW == NULL) return NULL; + if (LabTable[j] == NULL) { + cmsFreeToneCurveTriple(LabTable); + return NULL; + } + + // We need to map * (0xffff / 0xff00), that's same as (257 / 256) + // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256); + for (i=0; i < 257; i++) { + + LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8); + } + + LabTable[j] ->Table16[257] = 0xffff; + } + + mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable); + cmsFreeToneCurveTriple(LabTable); + + if (mpe == NULL) return NULL; + mpe ->Implements = cmsSigLabV2toV4; + return mpe; +} + +// ******************************************************************************** - NewLUT -> L1[i] = PtrW; - CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> InputEntries); - CopyMemory(&NewLUT -> LCurvesSeed[0][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS)); - } +// Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles +cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID) +{ + static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0, + 0, 65535.0/65280.0, 0, + 0, 0, 65535.0/65280.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLabV2toV4; + return mpe; +} + + +// Reverse direction +cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID) +{ + static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0, + 0, 65280.0/65535.0, 0, + 0, 0, 65280.0/65535.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLabV4toV2; + return mpe; +} - break; +// To Lab to float. Note that the MPE gives numbers in normal Lab range +// and we need 0..1.0 range for the formatters +// L* : 0...100 => 0...1.0 (L* / 100) +// ab* : -128..+127 to 0..1 ((ab* + 128) / 255) + +cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID) +{ + static const cmsFloat64Number a1[] = { + 1.0/100.0, 0, 0, + 0, 1.0/255.0, 0, + 0, 0, 1.0/255.0 + }; + + static const cmsFloat64Number o1[] = { + 0, + 128.0/255.0, + 128.0/255.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLab2FloatPCS; + return mpe; +} + +// Fom XYZ to floating point PCS +cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID) +{ +#define n (32768.0/65535.0) + static const cmsFloat64Number a1[] = { + n, 0, 0, + 0, n, 0, + 0, 0, n + }; +#undef n - case 2: NewLUT -> wFlags |= LUT_HASTL2; - cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> Out16params); - NewLUT -> OutputEntries = Tables[0] -> nEntries; - for (i=0; i < NewLUT -> OutputChan; i++) { + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigXYZ2FloatPCS; + return mpe; +} + +cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID) +{ + static const cmsFloat64Number a1[] = { + 100.0, 0, 0, + 0, 255.0, 0, + 0, 0, 255.0 + }; + + static const cmsFloat64Number o1[] = { + 0, + -128.0, + -128.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigFloatPCS2Lab; + return mpe; +} + +cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID) +{ +#define n (65535.0/32768.0) + + static const cmsFloat64Number a1[] = { + n, 0, 0, + 0, n, 0, + 0, 0, n + }; +#undef n - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> OutputEntries); - if (PtrW == NULL) return NULL; + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigFloatPCS2XYZ; + return mpe; +} + +// Clips values smaller than zero +static +void Clipper(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsUInt32Number i; + for (i = 0; i < mpe->InputChannels; i++) { + + cmsFloat32Number n = In[i]; + Out[i] = n < 0 ? 0 : n; + } +} + +cmsStage* _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigClipNegativesElemType, + nChannels, nChannels, Clipper, NULL, NULL, NULL); +} + +// ******************************************************************************** +// Type cmsSigXYZ2LabElemType +// ******************************************************************************** + +static +void EvaluateXYZ2Lab(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsCIELab Lab; + cmsCIEXYZ XYZ; + const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; + + // From 0..1.0 to XYZ + + XYZ.X = In[0] * XYZadj; + XYZ.Y = In[1] * XYZadj; + XYZ.Z = In[2] * XYZadj; - NewLUT -> L2[i] = PtrW; - CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> OutputEntries); - CopyMemory(&NewLUT -> LCurvesSeed[1][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS)); - } - break; + cmsXYZ2Lab(NULL, &Lab, &XYZ); + + // From V4 Lab to 0..1.0 + + Out[0] = (cmsFloat32Number) (Lab.L / 100.0); + Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0); + Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0); + return; + + cmsUNUSED_PARAMETER(mpe); +} + +cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL); + +} + +// ******************************************************************************** + +// For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray + +cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID) +{ + cmsToneCurve* LabTable[3]; + cmsFloat64Number Params[1] = {2.4} ; + + LabTable[0] = cmsBuildGamma(ContextID, 1.0); + LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params); + LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params); + + return cmsStageAllocToneCurves(ContextID, 3, LabTable); +} + + +// Free a single MPE +void CMSEXPORT cmsStageFree(cmsStage* mpe) +{ + if (mpe ->FreePtr) + mpe ->FreePtr(mpe); + + _cmsFree(mpe ->ContextID, mpe); +} - // 3 & 4 according ICC 4.0 spec +cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe) +{ + return mpe ->InputChannels; +} + +cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe) +{ + return mpe ->OutputChannels; +} + +cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe) +{ + return mpe -> Type; +} - case 3: - NewLUT -> wFlags |= LUT_HASTL3; - cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> L3params); - NewLUT -> L3Entries = Tables[0] -> nEntries; +void* CMSEXPORT cmsStageData(const cmsStage* mpe) +{ + return mpe -> Data; +} - for (i=0; i < NewLUT -> InputChan; i++) { +cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe) +{ + return mpe -> Next; +} - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> L3Entries); - if (PtrW == NULL) return NULL; + +// Duplicates an MPE +cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe) +{ + cmsStage* NewMPE; - NewLUT -> L3[i] = PtrW; - CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> L3Entries); - CopyMemory(&NewLUT -> LCurvesSeed[2][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS)); - } - break; + if (mpe == NULL) return NULL; + NewMPE = _cmsStageAllocPlaceholder(mpe ->ContextID, + mpe ->Type, + mpe ->InputChannels, + mpe ->OutputChannels, + mpe ->EvalPtr, + mpe ->DupElemPtr, + mpe ->FreePtr, + NULL); + if (NewMPE == NULL) return NULL; + + NewMPE ->Implements = mpe ->Implements; + + if (mpe ->DupElemPtr) { - case 4: - NewLUT -> wFlags |= LUT_HASTL4; - cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> L4params); - NewLUT -> L4Entries = Tables[0] -> nEntries; - for (i=0; i < NewLUT -> OutputChan; i++) { + NewMPE ->Data = mpe ->DupElemPtr(mpe); + + if (NewMPE->Data == NULL) { + + cmsStageFree(NewMPE); + return NULL; + } - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> L4Entries); - if (PtrW == NULL) return NULL; + } else { - NewLUT -> L4[i] = PtrW; - CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> L4Entries); - CopyMemory(&NewLUT -> LCurvesSeed[3][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS)); - } - break; + NewMPE ->Data = NULL; + } + + return NewMPE; +} - default:; +// *********************************************************************************************************** + +// This function sets up the channel count +static +cmsBool BlessLUT(cmsPipeline* lut) +{ + // We can set the input/output channels only if we have elements. + if (lut ->Elements != NULL) { + + cmsStage* prev; + cmsStage* next; + cmsStage* First; + cmsStage* Last; + + First = cmsPipelineGetPtrToFirstStage(lut); + Last = cmsPipelineGetPtrToLastStage(lut); + + if (First == NULL || Last == NULL) return FALSE; + + lut->InputChannels = First->InputChannels; + lut->OutputChannels = Last->OutputChannels; + + // Check chain consistency + prev = First; + next = prev->Next; + + while (next != NULL) + { + if (next->InputChannels != prev->OutputChannels) + return FALSE; + + next = next->Next; + prev = prev->Next; + } +} + + return TRUE; +} + + +// Default to evaluate the LUT on 16 bit-basis. Precision is retained. +static +void _LUTeval16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register const void* D) +{ + cmsPipeline* lut = (cmsPipeline*) D; + cmsStage *mpe; + cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS]; + int Phase = 0, NextPhase; + + From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels); + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NextPhase = Phase ^ 1; + mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); + Phase = NextPhase; + } + + + FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels); +} + + + +// Does evaluate the LUT on cmsFloat32Number-basis. +static +void _LUTevalFloat(register const cmsFloat32Number In[], register cmsFloat32Number Out[], const void* D) +{ + cmsPipeline* lut = (cmsPipeline*) D; + cmsStage *mpe; + cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS]; + int Phase = 0, NextPhase; + + memmove(&Storage[Phase][0], In, lut ->InputChannels * sizeof(cmsFloat32Number)); + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NextPhase = Phase ^ 1; + mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); + Phase = NextPhase; + } + + memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number)); +} + + +// LUT Creation & Destruction +cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) +{ + cmsPipeline* NewLUT; + + // A value of zero in channels is allowed as placeholder + if (InputChannels >= cmsMAXCHANNELS || + OutputChannels >= cmsMAXCHANNELS) return NULL; + + NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline)); + if (NewLUT == NULL) return NULL; + + NewLUT -> InputChannels = InputChannels; + NewLUT -> OutputChannels = OutputChannels; + + NewLUT ->Eval16Fn = _LUTeval16; + NewLUT ->EvalFloatFn = _LUTevalFloat; + NewLUT ->DupDataFn = NULL; + NewLUT ->FreeDataFn = NULL; + NewLUT ->Data = NewLUT; + NewLUT ->ContextID = ContextID; + + if (!BlessLUT(NewLUT)) + { + _cmsFree(ContextID, NewLUT); + return NULL; } return NewLUT; } +cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->ContextID; +} -// Set the LUT matrix +cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->InputChannels; +} -LPLUT LCMSEXPORT cmsSetMatrixLUT(LPLUT Lut, LPMAT3 M) +cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut) { - MAT3toFix(&Lut ->Matrix, M); + _cmsAssert(lut != NULL); + return lut ->OutputChannels; +} + +// Free a profile elements LUT +void CMSEXPORT cmsPipelineFree(cmsPipeline* lut) +{ + cmsStage *mpe, *Next; + + if (lut == NULL) return; - if (!MAT3isIdentity(&Lut->Matrix, 0.0001)) - Lut ->wFlags |= LUT_HASMATRIX; + for (mpe = lut ->Elements; + mpe != NULL; + mpe = Next) { - return Lut; + Next = mpe ->Next; + cmsStageFree(mpe); + } + + if (lut ->FreeDataFn) lut ->FreeDataFn(lut ->ContextID, lut ->Data); + + _cmsFree(lut ->ContextID, lut); } -// Set matrix & offset, v4 compatible - -LPLUT LCMSEXPORT cmsSetMatrixLUT4(LPLUT Lut, LPMAT3 M, LPVEC3 off, DWORD dwFlags) +// Default to evaluate the LUT on 16 bit-basis. +void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut) { - WMAT3 WMat; - WVEC3 Woff; - VEC3 Zero = {{0, 0, 0}}; - - MAT3toFix(&WMat, M); - - if (off == NULL) - off = &Zero; - - VEC3toFix(&Woff, off); - - // Nop if identity - if (MAT3isIdentity(&WMat, 0.0001) && - (Woff.n[VX] == 0 && Woff.n[VY] == 0 && Woff.n[VZ] == 0)) - return Lut; - - switch (dwFlags) { - - case LUT_HASMATRIX: - Lut ->Matrix = WMat; - Lut ->wFlags |= LUT_HASMATRIX; - break; - - case LUT_HASMATRIX3: - Lut ->Mat3 = WMat; - Lut ->Ofs3 = Woff; - Lut ->wFlags |= LUT_HASMATRIX3; - break; - - case LUT_HASMATRIX4: - Lut ->Mat4 = WMat; - Lut ->Ofs4 = Woff; - Lut ->wFlags |= LUT_HASMATRIX4; - break; + _cmsAssert(lut != NULL); + lut ->Eval16Fn(In, Out, lut->Data); +} - default:; - } - - return Lut; +// Does evaluate the LUT on cmsFloat32Number-basis. +void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + lut ->EvalFloatFn(In, Out, lut); } -// The full evaluator - -void LCMSEXPORT cmsEvalLUT(LPLUT Lut, WORD In[], WORD Out[]) +// Duplicates a LUT +cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut) { - register unsigned int i; - WORD StageABC[MAXCHANNELS], StageLMN[MAXCHANNELS]; - + cmsPipeline* NewLUT; + cmsStage *NewMPE, *Anterior = NULL, *mpe; + cmsBool First = TRUE; - // Try to speedup things on plain devicelinks - if (Lut ->wFlags == LUT_HAS3DGRID) { - - Lut ->CLut16params.Interp3D(In, Out, Lut -> T, &Lut -> CLut16params); - return; - } - - - // Nope, evaluate whole LUT + if (lut == NULL) return NULL; - for (i=0; i < Lut -> InputChan; i++) - StageABC[i] = In[i]; - - - if (Lut ->wFlags & LUT_V4_OUTPUT_EMULATE_V2) { - - // Clamp Lab to avoid overflow - if (StageABC[0] > 0xFF00) - StageABC[0] = 0xFF00; + NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels); + if (NewLUT == NULL) return NULL; - StageABC[0] = (WORD) FROM_V2_TO_V4(StageABC[0]); - StageABC[1] = (WORD) FROM_V2_TO_V4(StageABC[1]); - StageABC[2] = (WORD) FROM_V2_TO_V4(StageABC[2]); - - } + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { - if (Lut ->wFlags & LUT_V2_OUTPUT_EMULATE_V4) { - - StageABC[0] = (WORD) FROM_V4_TO_V2(StageABC[0]); - StageABC[1] = (WORD) FROM_V4_TO_V2(StageABC[1]); - StageABC[2] = (WORD) FROM_V4_TO_V2(StageABC[2]); - } + NewMPE = cmsStageDup(mpe); - - // Matrix handling. - - if (Lut -> wFlags & LUT_HASMATRIX) { - - WVEC3 InVect, OutVect; - - // In LUT8 here comes the special gray axis fixup - - if (Lut ->FixGrayAxes) { - - StageABC[1] = _cmsClampWord(StageABC[1] - 128); - StageABC[2] = _cmsClampWord(StageABC[2] - 128); - } - - // Matrix - - InVect.n[VX] = ToFixedDomain(StageABC[0]); - InVect.n[VY] = ToFixedDomain(StageABC[1]); - InVect.n[VZ] = ToFixedDomain(StageABC[2]); + if (NewMPE == NULL) { + cmsPipelineFree(NewLUT); + return NULL; + } - - MAT3evalW(&OutVect, &Lut -> Matrix, &InVect); - - // PCS in 1Fixed15 format, adjusting - - StageABC[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX])); - StageABC[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY])); - StageABC[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ])); - } + if (First) { + NewLUT ->Elements = NewMPE; + First = FALSE; + } + else { + if (Anterior != NULL) + Anterior ->Next = NewMPE; + } - - // First linearization + Anterior = NewMPE; + } - if (Lut -> wFlags & LUT_HASTL1) - { - for (i=0; i < Lut -> InputChan; i++) - StageABC[i] = cmsLinearInterpLUT16(StageABC[i], - Lut -> L1[i], - &Lut -> In16params); - } + NewLUT ->Eval16Fn = lut ->Eval16Fn; + NewLUT ->EvalFloatFn = lut ->EvalFloatFn; + NewLUT ->DupDataFn = lut ->DupDataFn; + NewLUT ->FreeDataFn = lut ->FreeDataFn; + + if (NewLUT ->DupDataFn != NULL) + NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data); - // Mat3, Ofs3, L3 processing - - if (Lut ->wFlags & LUT_HASMATRIX3) { - - WVEC3 InVect, OutVect; - - InVect.n[VX] = ToFixedDomain(StageABC[0]); - InVect.n[VY] = ToFixedDomain(StageABC[1]); - InVect.n[VZ] = ToFixedDomain(StageABC[2]); - - MAT3evalW(&OutVect, &Lut -> Mat3, &InVect); - - OutVect.n[VX] += Lut ->Ofs3.n[VX]; - OutVect.n[VY] += Lut ->Ofs3.n[VY]; - OutVect.n[VZ] += Lut ->Ofs3.n[VZ]; - - StageABC[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX])); - StageABC[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY])); - StageABC[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ])); - - } - - if (Lut ->wFlags & LUT_HASTL3) { - - for (i=0; i < Lut -> InputChan; i++) - StageABC[i] = cmsLinearInterpLUT16(StageABC[i], - Lut -> L3[i], - &Lut -> L3params); - - } - - - - if (Lut -> wFlags & LUT_HAS3DGRID) { - - Lut ->CLut16params.Interp3D(StageABC, StageLMN, Lut -> T, &Lut -> CLut16params); - - } - else - { - - for (i=0; i < Lut -> InputChan; i++) - StageLMN[i] = StageABC[i]; - - } - - - // Mat4, Ofs4, L4 processing - - if (Lut ->wFlags & LUT_HASTL4) { + NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits; - for (i=0; i < Lut -> OutputChan; i++) - StageLMN[i] = cmsLinearInterpLUT16(StageLMN[i], - Lut -> L4[i], - &Lut -> L4params); - } - - if (Lut ->wFlags & LUT_HASMATRIX4) { - - WVEC3 InVect, OutVect; - - InVect.n[VX] = ToFixedDomain(StageLMN[0]); - InVect.n[VY] = ToFixedDomain(StageLMN[1]); - InVect.n[VZ] = ToFixedDomain(StageLMN[2]); - - MAT3evalW(&OutVect, &Lut -> Mat4, &InVect); - - OutVect.n[VX] += Lut ->Ofs4.n[VX]; - OutVect.n[VY] += Lut ->Ofs4.n[VY]; - OutVect.n[VZ] += Lut ->Ofs4.n[VZ]; - - StageLMN[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX])); - StageLMN[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY])); - StageLMN[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ])); - - } - - // Last linearitzation + if (!BlessLUT(NewLUT)) + { + _cmsFree(lut->ContextID, NewLUT); + return NULL; + } - if (Lut -> wFlags & LUT_HASTL2) - { - for (i=0; i < Lut -> OutputChan; i++) - Out[i] = cmsLinearInterpLUT16(StageLMN[i], - Lut -> L2[i], - &Lut -> Out16params); - } - else - { - for (i=0; i < Lut -> OutputChan; i++) - Out[i] = StageLMN[i]; - } - - - - if (Lut ->wFlags & LUT_V4_INPUT_EMULATE_V2) { - - Out[0] = (WORD) FROM_V4_TO_V2(Out[0]); - Out[1] = (WORD) FROM_V4_TO_V2(Out[1]); - Out[2] = (WORD) FROM_V4_TO_V2(Out[2]); - - } - - if (Lut ->wFlags & LUT_V2_INPUT_EMULATE_V4) { - - Out[0] = (WORD) FROM_V2_TO_V4(Out[0]); - Out[1] = (WORD) FROM_V2_TO_V4(Out[1]); - Out[2] = (WORD) FROM_V2_TO_V4(Out[2]); - } + return NewLUT; } -// Precomputes tables for 8-bit on input devicelink. -// -LPLUT _cmsBlessLUT8(LPLUT Lut) +int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe) { - int i, j; - WORD StageABC[3]; - Fixed32 v1, v2, v3; - LPL8PARAMS p8; - LPL16PARAMS p = &Lut ->CLut16params; + cmsStage* Anterior = NULL, *pt; + + if (lut == NULL || mpe == NULL) + return FALSE; + + switch (loc) { + + case cmsAT_BEGIN: + mpe ->Next = lut ->Elements; + lut ->Elements = mpe; + break; + + case cmsAT_END: + + if (lut ->Elements == NULL) + lut ->Elements = mpe; + else { + + for (pt = lut ->Elements; + pt != NULL; + pt = pt -> Next) Anterior = pt; + + Anterior ->Next = mpe; + mpe ->Next = NULL; + } + break; + default:; + return FALSE; + } + + return BlessLUT(lut); +} + +// Unlink an element and return the pointer to it +void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe) +{ + cmsStage *Anterior, *pt, *Last; + cmsStage *Unlinked = NULL; - p8 = (LPL8PARAMS) _cmsMalloc(sizeof(L8PARAMS)); - if (p8 == NULL) return NULL; + // If empty LUT, there is nothing to remove + if (lut ->Elements == NULL) { + if (mpe) *mpe = NULL; + return; + } - // values comes * 257, so we can safely take first byte (x << 8 + x) - // if there are prelinearization, is already smelted in tables - - for (i=0; i < 256; i++) { - - StageABC[0] = StageABC[1] = StageABC[2] = RGB_8_TO_16(i); + // On depending on the strategy... + switch (loc) { - if (Lut ->wFlags & LUT_HASTL1) { + case cmsAT_BEGIN: + { + cmsStage* elem = lut ->Elements; - for (j=0; j < 3; j++) - StageABC[j] = cmsLinearInterpLUT16(StageABC[j], - Lut -> L1[j], - &Lut -> In16params); - Lut ->wFlags &= ~LUT_HASTL1; - } + lut ->Elements = elem -> Next; + elem ->Next = NULL; + Unlinked = elem; + } + break; - v1 = ToFixedDomain(StageABC[0] * p -> Domain); - v2 = ToFixedDomain(StageABC[1] * p -> Domain); - v3 = ToFixedDomain(StageABC[2] * p -> Domain); + case cmsAT_END: + Anterior = Last = NULL; + for (pt = lut ->Elements; + pt != NULL; + pt = pt -> Next) { + Anterior = Last; + Last = pt; + } - p8 ->X0[i] = p->opta3 * FIXED_TO_INT(v1); - p8 ->Y0[i] = p->opta2 * FIXED_TO_INT(v2); - p8 ->Z0[i] = p->opta1 * FIXED_TO_INT(v3); + Unlinked = Last; // Next already points to NULL - p8 ->rx[i] = (WORD) FIXED_REST_TO_INT(v1); - p8 ->ry[i] = (WORD) FIXED_REST_TO_INT(v2); - p8 ->rz[i] = (WORD) FIXED_REST_TO_INT(v3); - - } + // Truncate the chain + if (Anterior) + Anterior ->Next = NULL; + else + lut ->Elements = NULL; + break; + default:; + } - Lut -> CLut16params.p8 = p8; - Lut -> CLut16params.Interp3D = cmsTetrahedralInterp8; + if (mpe) + *mpe = Unlinked; + else + cmsStageFree(Unlinked); - return Lut; - + // May fail, but we ignore it + BlessLUT(lut); } +// Concatenate two LUT into a new single one +cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2) +{ + cmsStage* mpe; + + // If both LUTS does not have elements, we need to inherit + // the number of channels + if (l1 ->Elements == NULL && l2 ->Elements == NULL) { + l1 ->InputChannels = l2 ->InputChannels; + l1 ->OutputChannels = l2 ->OutputChannels; + } + + // Cat second + for (mpe = l2 ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + // We have to dup each element + if (!cmsPipelineInsertStage(l1, cmsAT_END, cmsStageDup(mpe))) + return FALSE; + } + + return BlessLUT(l1); +} + + +cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On) +{ + cmsBool Anterior = lut ->SaveAs8Bits; + + lut ->SaveAs8Bits = On; + return Anterior; +} + + +cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut) +{ + return lut ->Elements; +} + +cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut) +{ + cmsStage *mpe, *Anterior = NULL; + + for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) + Anterior = mpe; + + return Anterior; +} + +cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut) +{ + cmsStage *mpe; + cmsUInt32Number n; + + for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) + n++; + + return n; +} + +// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional +// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. +void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, + _cmsOPTeval16Fn Eval16, + void* PrivateData, + _cmsFreeUserDataFn FreePrivateDataFn, + _cmsDupUserDataFn DupPrivateDataFn) +{ + + Lut ->Eval16Fn = Eval16; + Lut ->DupDataFn = DupPrivateDataFn; + Lut ->FreeDataFn = FreePrivateDataFn; + Lut ->Data = PrivateData; +} // ----------------------------------------------------------- Reverse interpolation - - // Here's how it goes. The derivative Df(x) of the function f is the linear // transformation that best approximates f near the point x. It can be represented // by a matrix A whose entries are the partial derivatives of the components of f @@ -693,15 +1728,12 @@ // if you have a "good enough" initial guess. -#define JACOBIAN_EPSILON 0.001 +#define JACOBIAN_EPSILON 0.001f #define INVERSION_MAX_ITERATIONS 30 - - // Increment with reflexion on boundary - static -void IncDelta(double *Val) +void IncDelta(cmsFloat32Number *Val) { if (*Val < (1.0 - JACOBIAN_EPSILON)) @@ -714,159 +1746,127 @@ +// Euclidean distance between two vectors of n elements each one static -void ToEncoded(WORD Encoded[3], LPVEC3 Float) -{ - Encoded[0] = (WORD) floor(Float->n[0] * 65535.0 + 0.5); - Encoded[1] = (WORD) floor(Float->n[1] * 65535.0 + 0.5); - Encoded[2] = (WORD) floor(Float->n[2] * 65535.0 + 0.5); -} - -static -void FromEncoded(LPVEC3 Float, WORD Encoded[3]) +cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n) { - Float->n[0] = Encoded[0] / 65535.0; - Float->n[1] = Encoded[1] / 65535.0; - Float->n[2] = Encoded[2] / 65535.0; -} - -// Evaluates the CLUT part of a LUT (4 -> 3 only) -static -void EvalLUTdoubleKLab(LPLUT Lut, const VEC3* In, WORD FixedK, LPcmsCIELab Out) -{ - WORD wIn[4], wOut[3]; - - wIn[0] = (WORD) floor(In ->n[0] * 65535.0 + 0.5); - wIn[1] = (WORD) floor(In ->n[1] * 65535.0 + 0.5); - wIn[2] = (WORD) floor(In ->n[2] * 65535.0 + 0.5); - wIn[3] = FixedK; + cmsFloat32Number sum = 0; + int i; - cmsEvalLUT(Lut, wIn, wOut); - cmsLabEncoded2Float(Out, wOut); -} - -// Builds a Jacobian CMY->Lab - -static -void ComputeJacobianLab(LPLUT Lut, LPMAT3 Jacobian, const VEC3* Colorant, WORD K) -{ - VEC3 ColorantD; - cmsCIELab Lab, LabD; - int j; - - EvalLUTdoubleKLab(Lut, Colorant, K, &Lab); - + for (i=0; i < n; i++) { + cmsFloat32Number dif = b[i] - a[i]; + sum += dif * dif; + } - for (j = 0; j < 3; j++) { - - ColorantD.n[0] = Colorant ->n[0]; - ColorantD.n[1] = Colorant ->n[1]; - ColorantD.n[2] = Colorant ->n[2]; - - IncDelta(&ColorantD.n[j]); - - EvalLUTdoubleKLab(Lut, &ColorantD, K, &LabD); - - Jacobian->v[0].n[j] = ((LabD.L - Lab.L) / JACOBIAN_EPSILON); - Jacobian->v[1].n[j] = ((LabD.a - Lab.a) / JACOBIAN_EPSILON); - Jacobian->v[2].n[j] = ((LabD.b - Lab.b) / JACOBIAN_EPSILON); - - } + return sqrtf(sum); } -// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT, but It -// can be used on CMYK -> Lab LUT to obtain black preservation. -// Target holds LabK in this case +// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method +// +// x1 <- x - [J(x)]^-1 * f(x) +// +// lut: The LUT on where to do the search +// Target: LabK, 3 values of Lab plus destination K which is fixed +// Result: The obtained CMYK +// Hint: Location where begin the search -// x1 <- x - [J(x)]^-1 * f(x) - - -LCMSAPI double LCMSEXPORT cmsEvalLUTreverse(LPLUT Lut, WORD Target[], WORD Result[], LPWORD Hint) +cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], + cmsFloat32Number Result[], + cmsFloat32Number Hint[], + const cmsPipeline* lut) { - int i; - double error, LastError = 1E20; - cmsCIELab fx, Goal; - VEC3 tmp, tmp2, x; - MAT3 Jacobian; - WORD FixedK; - WORD LastResult[4]; - + cmsUInt32Number i, j; + cmsFloat64Number error, LastError = 1E20; + cmsFloat32Number fx[4], x[4], xd[4], fxd[4]; + cmsVEC3 tmp, tmp2; + cmsMAT3 Jacobian; - // This is our Lab goal - cmsLabEncoded2Float(&Goal, Target); - - // Special case for CMYK->Lab - - if (Lut ->InputChan == 4) - FixedK = Target[3]; - else - FixedK = 0; - + // Only 3->3 and 4->3 are supported + if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE; + if (lut ->OutputChannels != 3) return FALSE; // Take the hint as starting point if specified - if (Hint == NULL) { - // Begin at any point, we choose 1/3 of neutral CMY gray - - x.n[0] = x.n[1] = x.n[2] = 0.3; - + // Begin at any point, we choose 1/3 of CMY axis + x[0] = x[1] = x[2] = 0.3f; } else { - FromEncoded(&x, Hint); + // Only copy 3 channels from hint... + for (j=0; j < 3; j++) + x[j] = Hint[j]; } + // If Lut is 4-dimensions, then grab target[3], which is fixed + if (lut ->InputChannels == 4) { + x[3] = Target[3]; + } + else x[3] = 0; // To keep lint happy + // Iterate - for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) { // Get beginning fx - EvalLUTdoubleKLab(Lut, &x, FixedK, &fx); + cmsPipelineEvalFloat(x, fx, lut); // Compute error - error = cmsDeltaE(&fx, &Goal); + error = EuclideanDistance(fx, Target, 3); // If not convergent, return last safe value if (error >= LastError) break; // Keep latest values - LastError = error; - - ToEncoded(LastResult, &x); - LastResult[3] = FixedK; + LastError = error; + for (j=0; j < lut ->InputChannels; j++) + Result[j] = x[j]; - // Obtain slope - ComputeJacobianLab(Lut, &Jacobian, &x, FixedK); - - // Solve system - tmp2.n[0] = fx.L - Goal.L; - tmp2.n[1] = fx.a - Goal.a; - tmp2.n[2] = fx.b - Goal.b; - - if (!MAT3solve(&tmp, &Jacobian, &tmp2)) + // Found an exact match? + if (error <= 0) break; + // Obtain slope (the Jacobian) + for (j = 0; j < 3; j++) { + + xd[0] = x[0]; + xd[1] = x[1]; + xd[2] = x[2]; + xd[3] = x[3]; // Keep fixed channel + + IncDelta(&xd[j]); + + cmsPipelineEvalFloat(xd, fxd, lut); + + Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON); + Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON); + Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON); + } + + // Solve system + tmp2.n[0] = fx[0] - Target[0]; + tmp2.n[1] = fx[1] - Target[1]; + tmp2.n[2] = fx[2] - Target[2]; + + if (!_cmsMAT3solve(&tmp, &Jacobian, &tmp2)) + return FALSE; + // Move our guess - x.n[0] -= tmp.n[0]; - x.n[1] -= tmp.n[1]; - x.n[2] -= tmp.n[2]; + x[0] -= (cmsFloat32Number) tmp.n[0]; + x[1] -= (cmsFloat32Number) tmp.n[1]; + x[2] -= (cmsFloat32Number) tmp.n[2]; // Some clipping.... - VEC3saturate(&x); + for (j=0; j < 3; j++) { + if (x[j] < 0) x[j] = 0; + else + if (x[j] > 1.0) x[j] = 1.0; + } } - Result[0] = LastResult[0]; - Result[1] = LastResult[1]; - Result[2] = LastResult[2]; - Result[3] = LastResult[3]; - - return LastError; - + return TRUE; } - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsmatsh.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsmatsh.c Wed Jan 31 22:55:12 2018 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,411 +0,0 @@ -/* - * 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -// This file is available under and governed by the GNU General Public -// License version 2 only, as published by the Free Software Foundation. -// However, the following notice accompanied the original version of this -// file: -// -// -// Little cms -// Copyright (C) 1998-2007 Marti Maria -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "lcms.h" - - -// Shaper/Matrix handling -// This routines handles the matrix-shaper method. A note about domain -// is here required. If the shaper-matrix is invoked on INPUT profiles, -// after the shaper process, we have a value between 0 and 0xFFFF. Thus, -// for proper matrix handling, we must convert it to 15fix16, so -// ToFixedDomain might be called. But cmsLinearInterpFixed() returns -// data yet in fixed point, so no additional process is required. -// Then, we obtain data on 15.16, so we need to shift >> by 1 to -// obtain 1.15 PCS format. - -// On OUTPUT profiles, things are inverse, we must first expand 1 bit -// by shifting left, and then convert result between 0 and 1.000 to -// RGB, so FromFixedDomain() must be called before pass values to -// shaper. Trickly, there is a situation where this shifts works -// little different. Sometimes, lcms smelts input/output -// matrices into a single, one shaper, process. In such cases, since -// input is encoded from 0 to 0xffff, we must first use the shaper and -// then the matrix, an additional FromFixedDomain() must be used to -// accomodate output values. - -// For a sake of simplicity, I will handle this three behaviours -// with different routines, so the flags MATSHAPER_INPUT and MATSHAPER_OUTPUT -// can be conbined to signal smelted matrix-shapers - - - -static -int ComputeTables(LPGAMMATABLE Table[3], LPWORD Out[3], LPL16PARAMS p16) -{ - int i, AllLinear; - - cmsCalcL16Params(Table[0] -> nEntries, p16); - - AllLinear = 0; - for (i=0; i < 3; i++) - { - LPWORD PtrW; - - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * p16 -> nSamples); - - if (PtrW == NULL) return -1; // Signal error - - CopyMemory(PtrW, Table[i] -> GammaTable, sizeof(WORD) * Table[i] -> nEntries); - - Out[i] = PtrW; // Set table pointer - - // Linear after all? - - AllLinear += cmsIsLinear(PtrW, p16 -> nSamples); - } - - // If is all linear, then supress table interpolation (this - // will speed greately some trivial operations. - // Return 1 if present, 0 if all linear - - - if (AllLinear != 3) return 1; - - return 0; - -} - - -LPMATSHAPER cmsAllocMatShaper2(LPMAT3 Matrix, LPGAMMATABLE In[], LPGAMMATABLE Out[], DWORD Behaviour) -{ - LPMATSHAPER NewMatShaper; - int rc; - - NewMatShaper = (LPMATSHAPER) _cmsMalloc(sizeof(MATSHAPER)); - if (NewMatShaper) - ZeroMemory(NewMatShaper, sizeof(MATSHAPER)); - - NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED); - - // Fill matrix part - - MAT3toFix(&NewMatShaper -> Matrix, Matrix); - - // Reality check - - if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001)) - NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX; - - // Now, on the table characteristics - - if (Out) { - - rc = ComputeTables(Out, NewMatShaper ->L, &NewMatShaper ->p16); - if (rc < 0) { - cmsFreeMatShaper(NewMatShaper); - return NULL; - } - if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER; - } - - - if (In) { - - rc = ComputeTables(In, NewMatShaper ->L2, &NewMatShaper ->p2_16); - if (rc < 0) { - cmsFreeMatShaper(NewMatShaper); - return NULL; - } - if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASINPSHAPER; - } - - - return NewMatShaper; - -} - - - -// Creation & Destruction - -LPMATSHAPER cmsAllocMatShaper(LPMAT3 Matrix, LPGAMMATABLE Tables[], DWORD Behaviour) -{ - LPMATSHAPER NewMatShaper; - int i, AllLinear; - - if (Matrix == NULL) return NULL; - for (i=0; i < 3; i++) { - - if (Tables[i] == NULL) return NULL; - } - - NewMatShaper = (LPMATSHAPER) _cmsMalloc(sizeof(MATSHAPER)); - if (NewMatShaper) - ZeroMemory(NewMatShaper, sizeof(MATSHAPER)); - - NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED); - - // Fill matrix part - - MAT3toFix(&NewMatShaper -> Matrix, Matrix); - - // Reality check - - if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001)) - NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX; - - // Now, on the table characteristics - cmsCalcL16Params(Tables[0] -> nEntries, &NewMatShaper -> p16); - - // Copy tables - - AllLinear = 0; - for (i=0; i < 3; i++) { - - LPWORD PtrW; - - PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewMatShaper -> p16.nSamples); - - if (PtrW == NULL) { - cmsFreeMatShaper(NewMatShaper); - return NULL; - } - - CopyMemory(PtrW, Tables[i] -> GammaTable, - sizeof(WORD) * Tables[i] -> nEntries); - - NewMatShaper -> L[i] = PtrW; // Set table pointer - - // Linear after all? - - AllLinear += cmsIsLinear(PtrW, NewMatShaper -> p16.nSamples); - } - - // If is all linear, then supress table interpolation (this - // will speed greately some trivial operations - - if (AllLinear != 3) - NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER; - - return NewMatShaper; -} - - - -// Free associated memory - -void cmsFreeMatShaper(LPMATSHAPER MatShaper) -{ - int i; - - if (!MatShaper) return; - - for (i=0; i < 3; i++) - { - if (MatShaper -> L[i]) _cmsFree(MatShaper ->L[i]); - if (MatShaper -> L2[i]) _cmsFree(MatShaper ->L2[i]); - } - - _cmsFree(MatShaper); -} - - -// All smelted must postpose gamma to last stage. - -static -void AllSmeltedBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[]) -{ - - WORD tmp[3]; - WVEC3 InVect, OutVect; - - if (MatShaper -> dwFlags & MATSHAPER_HASINPSHAPER) - { - InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L2[0], &MatShaper -> p2_16); - InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L2[1], &MatShaper -> p2_16); - InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L2[2], &MatShaper -> p2_16); - } - else - { - InVect.n[VX] = ToFixedDomain(In[0]); - InVect.n[VY] = ToFixedDomain(In[1]); - InVect.n[VZ] = ToFixedDomain(In[2]); - } - - - if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX) - { - - MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect); - } - else { - - OutVect.n[VX] = InVect.n[VX]; - OutVect.n[VY] = InVect.n[VY]; - OutVect.n[VZ] = InVect.n[VZ]; - } - - - tmp[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX])); - tmp[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY])); - tmp[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ])); - - - - if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER) - { - Out[0] = cmsLinearInterpLUT16(tmp[0], MatShaper -> L[0], &MatShaper -> p16); - Out[1] = cmsLinearInterpLUT16(tmp[1], MatShaper -> L[1], &MatShaper -> p16); - Out[2] = cmsLinearInterpLUT16(tmp[2], MatShaper -> L[2], &MatShaper -> p16); - } - else - { - Out[0] = tmp[0]; - Out[1] = tmp[1]; - Out[2] = tmp[2]; - } - -} - - -static -void InputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[]) -{ - WVEC3 InVect, OutVect; - - - if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER) - { - InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L[0], &MatShaper -> p16); - InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L[1], &MatShaper -> p16); - InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L[2], &MatShaper -> p16); - } - else - { - InVect.n[VX] = ToFixedDomain(In[0]); - InVect.n[VY] = ToFixedDomain(In[1]); - InVect.n[VZ] = ToFixedDomain(In[2]); - } - - if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX) - { - MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect); - } - else - { - OutVect = InVect; - } - - // PCS in 1Fixed15 format, adjusting - - Out[0] = _cmsClampWord((OutVect.n[VX]) >> 1); - Out[1] = _cmsClampWord((OutVect.n[VY]) >> 1); - Out[2] = _cmsClampWord((OutVect.n[VZ]) >> 1); - -} - - -static -void OutputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[]) -{ - WVEC3 InVect, OutVect; - int i; - - // We need to convert from XYZ to RGB, here we must - // shift << 1 to pass between 1.15 to 15.16 formats - - InVect.n[VX] = (Fixed32) In[0] << 1; - InVect.n[VY] = (Fixed32) In[1] << 1; - InVect.n[VZ] = (Fixed32) In[2] << 1; - - if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX) - { - MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect); - } - else - { - OutVect = InVect; - } - - - if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER) - { - for (i=0; i < 3; i++) - { - - Out[i] = cmsLinearInterpLUT16( - _cmsClampWord(FromFixedDomain(OutVect.n[i])), - MatShaper -> L[i], - &MatShaper ->p16); - } - } - else - { - // Result from fixed domain to RGB - - Out[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX])); - Out[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY])); - Out[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ])); - } - -} - - -// Master on evaluating shapers, 3 different behaviours - -void cmsEvalMatShaper(LPMATSHAPER MatShaper, WORD In[], WORD Out[]) -{ - - if ((MatShaper -> dwFlags & MATSHAPER_ALLSMELTED) == MATSHAPER_ALLSMELTED) - { - AllSmeltedBehaviour(MatShaper, In, Out); - return; - } - if (MatShaper -> dwFlags & MATSHAPER_INPUT) - { - InputBehaviour(MatShaper, In, Out); - return; - } - - OutputBehaviour(MatShaper, In, Out); -} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsmd5.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsmd5.c Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,346 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- + + +#include "lcms2_internal.h" + +#ifdef CMS_USE_BIG_ENDIAN + +static +void byteReverse(cmsUInt8Number * buf, cmsUInt32Number longs) +{ + do { + + cmsUInt32Number t = _cmsAdjustEndianess32(*(cmsUInt32Number *) buf); + *(cmsUInt32Number *) buf = t; + buf += sizeof(cmsUInt32Number); + + } while (--longs); + +} + +#else +#define byteReverse(buf, len) +#endif + + +typedef struct { + + cmsUInt32Number buf[4]; + cmsUInt32Number bits[2]; + cmsUInt8Number in[64]; + cmsContext ContextID; + +} _cmsMD5; + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + + +static +void MD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16]) + +{ + register cmsUInt32Number a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + + +// Create a MD5 object +static +cmsHANDLE MD5alloc(cmsContext ContextID) +{ + _cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5)); + if (ctx == NULL) return NULL; + + ctx ->ContextID = ContextID; + + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; + + return (cmsHANDLE) ctx; +} + + +static +void MD5add(cmsHANDLE Handle, cmsUInt8Number* buf, cmsUInt32Number len) +{ + _cmsMD5* ctx = (_cmsMD5*) Handle; + cmsUInt32Number t; + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + (len << 3)) < t) + ctx->bits[1]++; + + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; + + if (t) { + + cmsUInt8Number *p = (cmsUInt8Number *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memmove(p, buf, len); + return; + } + + memmove(p, buf, t); + byteReverse(ctx->in, 16); + + MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + buf += t; + len -= t; + } + + while (len >= 64) { + memmove(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + buf += 64; + len -= 64; + } + + memmove(ctx->in, buf, len); +} + +// Destroy the object and return the checksum +static +void MD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle) +{ + _cmsMD5* ctx = (_cmsMD5*) Handle; + cmsUInt32Number count; + cmsUInt8Number *p; + + count = (ctx->bits[0] >> 3) & 0x3F; + + p = ctx->in + count; + *p++ = 0x80; + + count = 64 - 1 - count; + + if (count < 8) { + + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + + memset(ctx->in, 0, 56); + } else { + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + ((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0]; + ((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1]; + + MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + + byteReverse((cmsUInt8Number *) ctx->buf, 4); + memmove(ProfileID ->ID8, ctx->buf, 16); + + _cmsFree(ctx ->ContextID, ctx); +} + + + +// Assuming io points to an ICC profile, compute and store MD5 checksum +// In the header, rendering intentent, attributes and ID should be set to zero +// before computing MD5 checksum (per 6.1.13 in ICC spec) + +cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile) +{ + cmsContext ContextID; + cmsUInt32Number BytesNeeded; + cmsUInt8Number* Mem = NULL; + cmsHANDLE MD5 = NULL; + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + _cmsICCPROFILE Keep; + + _cmsAssert(hProfile != NULL); + + ContextID = cmsGetProfileContextID(hProfile); + + // Save a copy of the profile header + memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); + + // Set RI, attributes and ID + memset(&Icc ->attributes, 0, sizeof(Icc ->attributes)); + Icc ->RenderingIntent = 0; + memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID)); + + // Compute needed storage + if (!cmsSaveProfileToMem(hProfile, NULL, &BytesNeeded)) goto Error; + + // Allocate memory + Mem = (cmsUInt8Number*) _cmsMalloc(ContextID, BytesNeeded); + if (Mem == NULL) goto Error; + + // Save to temporary storage + if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error; + + // Create MD5 object + MD5 = MD5alloc(ContextID); + if (MD5 == NULL) goto Error; + + // Add all bytes + MD5add(MD5, Mem, BytesNeeded); + + // Temp storage is no longer needed + _cmsFree(ContextID, Mem); + + // Restore header + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + + // And store the ID + MD5finish(&Icc ->ProfileID, MD5); + return TRUE; + +Error: + + // Free resources as something went wrong + // "MD5" cannot be other than NULL here, so no need to free it + if (Mem != NULL) _cmsFree(ContextID, Mem); + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + return FALSE; +} + diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsmtrx.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsmtrx.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsmtrx.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,447 +49,105 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Vector & Matrix stuff - -#include "lcms.h" - - -void cdecl VEC3init(LPVEC3 r, double x, double y, double z); -void cdecl VEC3initF(LPWVEC3 r, double x, double y, double z); -void cdecl VEC3toFix(LPWVEC3 r, LPVEC3 v); -void cdecl VEC3scaleFix(LPWORD r, LPWVEC3 Scale); -void cdecl VEC3swap(LPVEC3 a, LPVEC3 b); -void cdecl VEC3divK(LPVEC3 r, LPVEC3 v, double d); -void cdecl VEC3perK(LPVEC3 r, LPVEC3 v, double d); -void cdecl VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 b); -void cdecl VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 b); -void cdecl VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d); -void cdecl VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 v); -void cdecl VEC3saturate(LPVEC3 v); - -double cdecl VEC3length(LPVEC3 a); -double cdecl VEC3distance(LPVEC3 a, LPVEC3 b); - - -void cdecl MAT3identity(LPMAT3 a); -void cdecl MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b); -int cdecl MAT3inverse(LPMAT3 a, LPMAT3 b); -LCMSBOOL cdecl MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b); -double cdecl MAT3det(LPMAT3 m); -void cdecl MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 v); -void cdecl MAT3toFix(LPWMAT3 r, LPMAT3 v); -void cdecl MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v); -void cdecl MAT3perK(LPMAT3 r, LPMAT3 v, double d); -void cdecl MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d); +// +//--------------------------------------------------------------------------------- +// -// --------------------- Implementation ---------------------------- - -#define DSWAP(x, y) {double tmp = (x); (x)=(y); (y)=tmp;} - - - -#ifdef USE_ASSEMBLER - - -#ifdef _MSC_VER -#pragma warning(disable : 4033) -#pragma warning(disable : 4035) -#endif - - - -Fixed32 FixedMul(Fixed32 a, Fixed32 b) -{ - ASM { - - mov eax, ss:a - mov edx, ss:b - imul edx - add eax, 0x8000 - adc edx, 0 - shrd eax, edx, 16 - - } - - RET(_EAX); -} - - +#include "lcms2_internal.h" -Fixed32 FixedSquare(Fixed32 a) -{ - ASM { - pushf - push edx - mov eax, ss:a - imul eax - add eax, 0x8000 - adc edx, 0 - shrd eax, edx, 16 - sar eax, 16 - pop edx - popf - } - - RET(_EAX); -} - - - - -// Linear intERPolation -// a * (h - l) >> 16 + l - -Fixed32 FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h) -{ - ASM { - mov eax, dword ptr ss:h - mov edx, dword ptr ss:l - push edx - mov ecx, dword ptr ss:a - sub eax, edx - imul ecx - add eax, 0x8000 - adc edx, 0 - shrd eax, edx, 16 - pop edx - add eax, edx - } - - RET(_EAX); -} - - -// a as word is scaled by s as float - -WORD FixedScale(WORD a, Fixed32 s) -{ - ASM { - - xor eax,eax - mov ax, ss:a // This is faster that movzx eax, ss:a - sal eax, 16 - mov edx, ss:s - mul edx - add eax, 0x8000 - adc edx, 0 - mov eax, edx - } - - RET(_EAX); -} - -#ifdef _MSC_VER -#pragma warning(default : 4033) -#pragma warning(default : 4035) -#endif - -#else +#define DSWAP(x, y) {cmsFloat64Number tmp = (x); (x)=(y); (y)=tmp;} -// These are floating point versions for compilers that doesn't -// support asm at all. Use with care, since this will slow down -// all operations - - -Fixed32 FixedMul(Fixed32 a, Fixed32 b) -{ -#ifdef USE_INT64 - LCMSULONGLONG l = (LCMSULONGLONG) (LCMSSLONGLONG) a * (LCMSULONGLONG) (LCMSSLONGLONG) b + (LCMSULONGLONG) 0x8000; - l >>= 16; - return (Fixed32) l; -#else - return DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(a) * FIXED_TO_DOUBLE(b)); -#endif -} - -Fixed32 FixedSquare(Fixed32 a) -{ - return FixedMul(a, a); -} - - -Fixed32 FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h) +// Initiate a vector +void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z) { -#ifdef USE_INT64 - - LCMSULONGLONG dif = (LCMSULONGLONG) (h - l) * a + 0x8000; - dif = (dif >> 16) + l; - return (Fixed32) (dif); -#else - double dif = h - l; - - dif *= a; - dif /= 65536.0; - dif += l; - - return (Fixed32) (dif + 0.5); -#endif - -} - - -WORD FixedScale(WORD a, Fixed32 s) -{ - return (WORD) (a * FIXED_TO_DOUBLE(s)); -} - -#endif - - -#ifndef USE_INLINE - -Fixed32 ToFixedDomain(int a) -{ - return a + ((a + 0x7fff) / 0xffff); -} - - -int FromFixedDomain(Fixed32 a) -{ - return a - ((a + 0x7fff) >> 16); + r -> n[VX] = x; + r -> n[VY] = y; + r -> n[VZ] = z; } -#endif - - - -// Initiate a vector (double version) - - -void VEC3init(LPVEC3 r, double x, double y, double z) -{ - r -> n[VX] = x; - r -> n[VY] = y; - r -> n[VZ] = z; -} - -// Init a vector (fixed version) - -void VEC3initF(LPWVEC3 r, double x, double y, double z) -{ - r -> n[VX] = DOUBLE_TO_FIXED(x); - r -> n[VY] = DOUBLE_TO_FIXED(y); - r -> n[VZ] = DOUBLE_TO_FIXED(z); -} - - -// Convert to fixed point encoding is 1.0 = 0xFFFF - -void VEC3toFix(LPWVEC3 r, LPVEC3 v) -{ - r -> n[VX] = DOUBLE_TO_FIXED(v -> n[VX]); - r -> n[VY] = DOUBLE_TO_FIXED(v -> n[VY]); - r -> n[VZ] = DOUBLE_TO_FIXED(v -> n[VZ]); -} - -// Convert from fixed point - -void VEC3fromFix(LPVEC3 r, LPWVEC3 v) -{ - r -> n[VX] = FIXED_TO_DOUBLE(v -> n[VX]); - r -> n[VY] = FIXED_TO_DOUBLE(v -> n[VY]); - r -> n[VZ] = FIXED_TO_DOUBLE(v -> n[VZ]); -} - - -// Swap two double vectors - -void VEC3swap(LPVEC3 a, LPVEC3 b) -{ - DSWAP(a-> n[VX], b-> n[VX]); - DSWAP(a-> n[VY], b-> n[VY]); - DSWAP(a-> n[VZ], b-> n[VZ]); -} - -// Divide a vector by a constant - -void VEC3divK(LPVEC3 r, LPVEC3 v, double d) -{ - double d_inv = 1./d; - - r -> n[VX] = v -> n[VX] * d_inv; - r -> n[VY] = v -> n[VY] * d_inv; - r -> n[VZ] = v -> n[VZ] * d_inv; -} - -// Multiply by a constant - -void VEC3perK(LPVEC3 r, LPVEC3 v, double d ) -{ - r -> n[VX] = v -> n[VX] * d; - r -> n[VY] = v -> n[VY] * d; - r -> n[VZ] = v -> n[VZ] * d; -} - - -void VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 b) -{ - r -> n[VX] = a->n[VX]*b->n[VX]; - r -> n[VY] = a->n[VY]*b->n[VY]; - r -> n[VZ] = a->n[VZ]*b->n[VZ]; -} - -// Minus - - -void VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 b) +// Vector subtraction +void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b) { r -> n[VX] = a -> n[VX] - b -> n[VX]; r -> n[VY] = a -> n[VY] - b -> n[VY]; r -> n[VZ] = a -> n[VZ] - b -> n[VZ]; } - -// Check id two vectors are the same, allowing tolerance - -static -LCMSBOOL RangeCheck(double l, double h, double v) -{ - return (v >= l && v <= h); -} - - -LCMSBOOL VEC3equal(LPWVEC3 a, LPWVEC3 b, double Tolerance) -{ - int i; - double c; - - for (i=0; i < 3; i++) - { - c = FIXED_TO_DOUBLE(a -> n[i]); - if (!RangeCheck(c - Tolerance, - c + Tolerance, - FIXED_TO_DOUBLE(b->n[i]))) return FALSE; - } - - return TRUE; -} - -LCMSBOOL VEC3equalF(LPVEC3 a, LPVEC3 b, double Tolerance) +// Vector cross product +void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v) { - int i; - double c; - - for (i=0; i < 3; i++) - { - c = a -> n[i]; - if (!RangeCheck(c - Tolerance, - c + Tolerance, - b->n[i])) return FALSE; - } - - return TRUE; -} - - -void VEC3scaleFix(LPWORD r, LPWVEC3 Scale) -{ - if (Scale -> n[VX] == 0x00010000L && - Scale -> n[VY] == 0x00010000L && - Scale -> n[VZ] == 0x00010000L) return; - - r[0] = (WORD) FixedScale(r[0], Scale -> n[VX]); - r[1] = (WORD) FixedScale(r[1], Scale -> n[VY]); - r[2] = (WORD) FixedScale(r[2], Scale -> n[VZ]); - -} - - - -// Vector cross product - -void VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 v) -{ - r ->n[VX] = u->n[VY] * v->n[VZ] - v->n[VY] * u->n[VZ]; r ->n[VY] = u->n[VZ] * v->n[VX] - v->n[VZ] * u->n[VX]; r ->n[VZ] = u->n[VX] * v->n[VY] - v->n[VX] * u->n[VY]; } - +// Vector dot product +cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v) +{ + return u->n[VX] * v->n[VX] + u->n[VY] * v->n[VY] + u->n[VZ] * v->n[VZ]; +} -// The vector size - -double VEC3length(LPVEC3 a) +// Euclidean length +cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a) { return sqrt(a ->n[VX] * a ->n[VX] + a ->n[VY] * a ->n[VY] + a ->n[VZ] * a ->n[VZ]); } - -// Saturate a vector into 0..1.0 range - -void VEC3saturate(LPVEC3 v) +// Euclidean distance +cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b) { - int i; - for (i=0; i < 3; i++) { - if (v ->n[i] < 0) - v ->n[i] = 0; - else - if (v ->n[i] > 1.0) - v ->n[i] = 1.0; - } -} - - -// Euclidean distance - -double VEC3distance(LPVEC3 a, LPVEC3 b) -{ - double d1 = a ->n[VX] - b ->n[VX]; - double d2 = a ->n[VY] - b ->n[VY]; - double d3 = a ->n[VZ] - b ->n[VZ]; + cmsFloat64Number d1 = a ->n[VX] - b ->n[VX]; + cmsFloat64Number d2 = a ->n[VY] - b ->n[VY]; + cmsFloat64Number d3 = a ->n[VZ] - b ->n[VZ]; return sqrt(d1*d1 + d2*d2 + d3*d3); } -// Identity - -void MAT3identity(LPMAT3 a) +// 3x3 Identity +void CMSEXPORT _cmsMAT3identity(cmsMAT3* a) { - VEC3init(&a-> v[0], 1.0, 0.0, 0.0); - VEC3init(&a-> v[1], 0.0, 1.0, 0.0); - VEC3init(&a-> v[2], 0.0, 0.0, 1.0); + _cmsVEC3init(&a-> v[0], 1.0, 0.0, 0.0); + _cmsVEC3init(&a-> v[1], 0.0, 1.0, 0.0); + _cmsVEC3init(&a-> v[2], 0.0, 0.0, 1.0); +} + +static +cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b) +{ + return fabs(b - a) < (1.0 / 65535.0); } +cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a) +{ + cmsMAT3 Identity; + int i, j; + + _cmsMAT3identity(&Identity); + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + if (!CloseEnough(a ->v[i].n[j], Identity.v[i].n[j])) return FALSE; + + return TRUE; +} -// Check if matrix is Identity. Allow a tolerance as % - -LCMSBOOL MAT3isIdentity(LPWMAT3 a, double Tolerance) -{ - int i; - MAT3 Idd; - WMAT3 Idf; - - MAT3identity(&Idd); - MAT3toFix(&Idf, &Idd); - - for (i=0; i < 3; i++) - if (!VEC3equal(&a -> v[i], &Idf.v[i], Tolerance)) return FALSE; - - return TRUE; - -} - // Multiply two matrices - - -void MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b) +void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b) { #define ROWCOL(i, j) \ a->v[i].n[0]*b->v[0].n[j] + a->v[i].n[1]*b->v[1].n[j] + a->v[i].n[2]*b->v[2].n[j] - VEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2)); - VEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2)); - VEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2)); + _cmsVEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2)); + _cmsVEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2)); + _cmsVEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2)); #undef ROWCOL //(i, j) } @@ -496,92 +155,47 @@ // Inverse of a matrix b = a^(-1) -// Gauss-Jordan elimination with partial pivoting - -int MAT3inverse(LPMAT3 a, LPMAT3 b) +cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b) { - register int i, j, max; - - MAT3identity(b); + cmsFloat64Number det, c0, c1, c2; - // Loop over cols of a from left to right, eliminating above and below diag - for (j=0; j<3; j++) { // Find largest pivot in column j among rows j..2 + c0 = a -> v[1].n[1]*a -> v[2].n[2] - a -> v[1].n[2]*a -> v[2].n[1]; + c1 = -a -> v[1].n[0]*a -> v[2].n[2] + a -> v[1].n[2]*a -> v[2].n[0]; + c2 = a -> v[1].n[0]*a -> v[2].n[1] - a -> v[1].n[1]*a -> v[2].n[0]; - max = j; // Row with largest pivot candidate - for (i=j+1; i<3; i++) - if (fabs(a -> v[i].n[j]) > fabs(a -> v[max].n[j])) - max = i; + det = a -> v[0].n[0]*c0 + a -> v[0].n[1]*c1 + a -> v[0].n[2]*c2; - // Swap rows max and j in a and b to put pivot on diagonal - - VEC3swap(&a -> v[max], &a -> v[j]); - VEC3swap(&b -> v[max], &b -> v[j]); + if (fabs(det) < MATRIX_DET_TOLERANCE) return FALSE; // singular matrix; can't invert - // Scale row j to have a unit diagonal - - if (a -> v[j].n[j]==0.) - return -1; // singular matrix; can't invert - - VEC3divK(&b-> v[j], &b -> v[j], a->v[j].n[j]); - VEC3divK(&a-> v[j], &a -> v[j], a->v[j].n[j]); - - // Eliminate off-diagonal elems in col j of a, doing identical ops to b - for (i=0; i<3; i++) + b -> v[0].n[0] = c0/det; + b -> v[0].n[1] = (a -> v[0].n[2]*a -> v[2].n[1] - a -> v[0].n[1]*a -> v[2].n[2])/det; + b -> v[0].n[2] = (a -> v[0].n[1]*a -> v[1].n[2] - a -> v[0].n[2]*a -> v[1].n[1])/det; + b -> v[1].n[0] = c1/det; + b -> v[1].n[1] = (a -> v[0].n[0]*a -> v[2].n[2] - a -> v[0].n[2]*a -> v[2].n[0])/det; + b -> v[1].n[2] = (a -> v[0].n[2]*a -> v[1].n[0] - a -> v[0].n[0]*a -> v[1].n[2])/det; + b -> v[2].n[0] = c2/det; + b -> v[2].n[1] = (a -> v[0].n[1]*a -> v[2].n[0] - a -> v[0].n[0]*a -> v[2].n[1])/det; + b -> v[2].n[2] = (a -> v[0].n[0]*a -> v[1].n[1] - a -> v[0].n[1]*a -> v[1].n[0])/det; - if (i !=j) { - VEC3 temp; - - VEC3perK(&temp, &b -> v[j], a -> v[i].n[j]); - VEC3minus(&b -> v[i], &b -> v[i], &temp); - - VEC3perK(&temp, &a -> v[j], a -> v[i].n[j]); - VEC3minus(&a -> v[i], &a -> v[i], &temp); - } - } - - return 1; + return TRUE; } // Solve a system in the form Ax = b - -LCMSBOOL MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b) +cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b) { - MAT3 m, a_1; + cmsMAT3 m, a_1; - CopyMemory(&m, a, sizeof(MAT3)); + memmove(&m, a, sizeof(cmsMAT3)); - if (!MAT3inverse(&m, &a_1)) return FALSE; // Singular matrix + if (!_cmsMAT3inverse(&m, &a_1)) return FALSE; // Singular matrix - MAT3eval(x, &a_1, b); + _cmsMAT3eval(x, &a_1, b); return TRUE; } - -// The determinant - -double MAT3det(LPMAT3 m) -{ - - double a1 = m ->v[VX].n[VX]; - double a2 = m ->v[VX].n[VY]; - double a3 = m ->v[VX].n[VZ]; - double b1 = m ->v[VY].n[VX]; - double b2 = m ->v[VY].n[VY]; - double b3 = m ->v[VY].n[VZ]; - double c1 = m ->v[VZ].n[VX]; - double c2 = m ->v[VZ].n[VY]; - double c3 = m ->v[VZ].n[VZ]; - - - return a1*b2*c3 - a1*b3*c2 + a2*b3*c1 - a2*b1*c3 - a3*b1*c2 - a3*b2*c1; -} - - -// linear transform - - -void MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 v) +// Evaluate a vector across a matrix +void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v) { r->n[VX] = a->v[0].n[VX]*v->n[VX] + a->v[0].n[VY]*v->n[VY] + a->v[0].n[VZ]*v->n[VZ]; r->n[VY] = a->v[1].n[VX]*v->n[VX] + a->v[1].n[VY]*v->n[VY] + a->v[1].n[VZ]*v->n[VZ]; @@ -589,257 +203,3 @@ } -// Ok, this is another bottleneck of performance. - - -#ifdef USE_ASSEMBLER - -// ecx:ebx is result in 64 bits format -// edi points to matrix, esi points to input vector -// since only 3 accesses are in output, this is a stack variable - - -void MAT3evalW(LPWVEC3 r_, LPWMAT3 a_, LPWVEC3 v_) -{ - - ASM { - - - mov esi, dword ptr ss:v_ - mov edi, dword ptr ss:a_ - - // r->n[VX] = FixedMul(a->v[0].n[0], v->n[0]) + - - mov eax,dword ptr [esi] - mov edx,dword ptr [edi] - imul edx - mov ecx, eax - mov ebx, edx - - // FixedMul(a->v[0].n[1], v->n[1]) + - - mov eax,dword ptr [esi+4] - mov edx,dword ptr [edi+4] - imul edx - add ecx, eax - adc ebx, edx - - // FixedMul(a->v[0].n[2], v->n[2]); - - mov eax,dword ptr [esi+8] - mov edx,dword ptr [edi+8] - imul edx - add ecx, eax - adc ebx, edx - - // Back to Fixed 15.16 - - add ecx, 0x8000 - adc ebx, 0 - shrd ecx, ebx, 16 - - push edi - mov edi, dword ptr ss:r_ - mov dword ptr [edi], ecx // r -> n[VX] - pop edi - - - - // 2nd row *************************** - - // FixedMul(a->v[1].n[0], v->n[0]) - - mov eax,dword ptr [esi] - mov edx,dword ptr [edi+12] - imul edx - mov ecx, eax - mov ebx, edx - - // FixedMul(a->v[1].n[1], v->n[1]) + - - mov eax,dword ptr [esi+4] - mov edx,dword ptr [edi+16] - imul edx - add ecx, eax - adc ebx, edx - - // FixedMul(a->v[1].n[2], v->n[2]); - - mov eax,dword ptr [esi+8] - mov edx,dword ptr [edi+20] - imul edx - add ecx, eax - adc ebx, edx - - add ecx, 0x8000 - adc ebx, 0 - shrd ecx, ebx, 16 - - push edi - mov edi, dword ptr ss:r_ - mov dword ptr [edi+4], ecx // r -> n[VY] - pop edi - -// 3d row ************************** - - // r->n[VZ] = FixedMul(a->v[2].n[0], v->n[0]) + - - mov eax,dword ptr [esi] - mov edx,dword ptr [edi+24] - imul edx - mov ecx, eax - mov ebx, edx - - // FixedMul(a->v[2].n[1], v->n[1]) + - - mov eax,dword ptr [esi+4] - mov edx,dword ptr [edi+28] - imul edx - add ecx, eax - adc ebx, edx - - // FixedMul(a->v[2].n[2], v->n[2]); - - mov eax,dword ptr [esi+8] - mov edx,dword ptr [edi+32] - imul edx - add ecx, eax - adc ebx, edx - - add ecx, 0x8000 - adc ebx, 0 - shrd ecx, ebx, 16 - - mov edi, dword ptr ss:r_ - mov dword ptr [edi+8], ecx // r -> n[VZ] - } -} - - -#else - - -#ifdef USE_FLOAT - -void MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v) -{ - r->n[VX] = DOUBLE_TO_FIXED( - FIXED_TO_DOUBLE(a->v[0].n[0]) * FIXED_TO_DOUBLE(v->n[0]) + - FIXED_TO_DOUBLE(a->v[0].n[1]) * FIXED_TO_DOUBLE(v->n[1]) + - FIXED_TO_DOUBLE(a->v[0].n[2]) * FIXED_TO_DOUBLE(v->n[2]) - ); - - r->n[VY] = DOUBLE_TO_FIXED( - FIXED_TO_DOUBLE(a->v[1].n[0]) * FIXED_TO_DOUBLE(v->n[0]) + - FIXED_TO_DOUBLE(a->v[1].n[1]) * FIXED_TO_DOUBLE(v->n[1]) + - FIXED_TO_DOUBLE(a->v[1].n[2]) * FIXED_TO_DOUBLE(v->n[2]) - ); - - r->n[VZ] = DOUBLE_TO_FIXED( - FIXED_TO_DOUBLE(a->v[2].n[0]) * FIXED_TO_DOUBLE(v->n[0]) + - FIXED_TO_DOUBLE(a->v[2].n[1]) * FIXED_TO_DOUBLE(v->n[1]) + - FIXED_TO_DOUBLE(a->v[2].n[2]) * FIXED_TO_DOUBLE(v->n[2]) - ); -} - - -#else - -void MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v) -{ - -#ifdef USE_INT64 - - LCMSULONGLONG l1 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[0] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] + - (LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[1] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] + - (LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[2] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000; - - LCMSULONGLONG l2 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[0] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] + - (LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[1] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] + - (LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[2] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000; - - LCMSULONGLONG l3 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[0] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] + - (LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[1] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] + - (LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[2] * - (LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000; - l1 >>= 16; - l2 >>= 16; - l3 >>= 16; - - r->n[VX] = (Fixed32) l1; - r->n[VY] = (Fixed32) l2; - r->n[VZ] = (Fixed32) l3; - -#else - - // FIXME: Rounding should be done at very last stage. There is 1-Contone rounding error! - - r->n[VX] = FixedMul(a->v[0].n[0], v->n[0]) + - FixedMul(a->v[0].n[1], v->n[1]) + - FixedMul(a->v[0].n[2], v->n[2]); - - r->n[VY] = FixedMul(a->v[1].n[0], v->n[0]) + - FixedMul(a->v[1].n[1], v->n[1]) + - FixedMul(a->v[1].n[2], v->n[2]); - - r->n[VZ] = FixedMul(a->v[2].n[0], v->n[0]) + - FixedMul(a->v[2].n[1], v->n[1]) + - FixedMul(a->v[2].n[2], v->n[2]); -#endif -} - -#endif -#endif - - -void MAT3perK(LPMAT3 r, LPMAT3 v, double d) -{ - VEC3perK(&r -> v[0], &v -> v[0], d); - VEC3perK(&r -> v[1], &v -> v[1], d); - VEC3perK(&r -> v[2], &v -> v[2], d); -} - - -void MAT3toFix(LPWMAT3 r, LPMAT3 v) -{ - VEC3toFix(&r -> v[0], &v -> v[0]); - VEC3toFix(&r -> v[1], &v -> v[1]); - VEC3toFix(&r -> v[2], &v -> v[2]); -} - -void MAT3fromFix(LPMAT3 r, LPWMAT3 v) -{ - VEC3fromFix(&r -> v[0], &v -> v[0]); - VEC3fromFix(&r -> v[1], &v -> v[1]); - VEC3fromFix(&r -> v[2], &v -> v[2]); -} - - - -// Scale v by d and store it in r giving INTEGER - -void VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d) -{ - r -> n[VX] = (int) floor(v -> n[VX] * d + .5); - r -> n[VY] = (int) floor(v -> n[VY] * d + .5); - r -> n[VZ] = (int) floor(v -> n[VZ] * d + .5); -} - -void MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d) -{ - VEC3scaleAndCut(&r -> v[0], &v -> v[0], d); - VEC3scaleAndCut(&r -> v[1], &v -> v[1], d); - VEC3scaleAndCut(&r -> v[2], &v -> v[2], d); -} - - - - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsnamed.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsnamed.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsnamed.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,153 +49,943 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Multilocalized unicode objects. That is an attempt to encapsulate i18n. -// Named color support +// Allocates an empty multi localizad unicode object +cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems) +{ + cmsMLU* mlu; + + // nItems should be positive if given + if (nItems <= 0) nItems = 2; + + // Create the container + mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU)); + if (mlu == NULL) return NULL; + + mlu ->ContextID = ContextID; -#include "lcms.h" + // Create entry array + mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry)); + if (mlu ->Entries == NULL) { + _cmsFree(ContextID, mlu); + return NULL; + } + // Ok, keep indexes up to date + mlu ->AllocatedEntries = nItems; + mlu ->UsedEntries = 0; + + return mlu; +} +// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two. static -LPcmsNAMEDCOLORLIST GrowNamedColorList(LPcmsNAMEDCOLORLIST v, int ByElements) +cmsBool GrowMLUpool(cmsMLU* mlu) { - if (ByElements > v ->Allocated) { + cmsUInt32Number size; + void *NewPtr; - LPcmsNAMEDCOLORLIST TheNewList; - int NewElements; - size_t size; + // Sanity check + if (mlu == NULL) return FALSE; - if (v ->Allocated == 0) - NewElements = 64; // Initial guess - else - NewElements = v ->Allocated; + if (mlu ->PoolSize == 0) + size = 256; + else + size = mlu ->PoolSize * 2; - while (ByElements > NewElements) - NewElements *= 2; + // Check for overflow + if (size < mlu ->PoolSize) return FALSE; - size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * NewElements); - TheNewList = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size); + // Reallocate the pool + NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size); + if (NewPtr == NULL) return FALSE; - if (TheNewList == NULL) { - cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory reallocating named color list"); - return NULL; - } - else { - ZeroMemory(TheNewList, size); - CopyMemory(TheNewList, v, sizeof(cmsNAMEDCOLORLIST) + (v ->nColors - 1) * sizeof(cmsNAMEDCOLOR)); - TheNewList -> Allocated = NewElements; + mlu ->MemPool = NewPtr; + mlu ->PoolSize = size; + + return TRUE; +} + + +// Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two. +static +cmsBool GrowMLUtable(cmsMLU* mlu) +{ + cmsUInt32Number AllocatedEntries; + _cmsMLUentry *NewPtr; - _cmsFree(v); - return TheNewList; - } - } + // Sanity check + if (mlu == NULL) return FALSE; + + AllocatedEntries = mlu ->AllocatedEntries * 2; + + // Check for overflow + if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE; - return v; + // Reallocate the memory + NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry)); + if (NewPtr == NULL) return FALSE; + + mlu ->Entries = NewPtr; + mlu ->AllocatedEntries = AllocatedEntries; + + return TRUE; } -LPcmsNAMEDCOLORLIST cmsAllocNamedColorList(int n) +// Search for a specific entry in the structure. Language and Country are used. +static +int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) { - size_t size = sizeof(cmsNAMEDCOLORLIST) + (n - 1) * sizeof(cmsNAMEDCOLOR); + cmsUInt32Number i; + + // Sanity check + if (mlu == NULL) return -1; + + // Iterate whole table + for (i=0; i < mlu ->UsedEntries; i++) { + + if (mlu ->Entries[i].Country == CountryCode && + mlu ->Entries[i].Language == LanguageCode) return (int) i; + } + + // Not found + return -1; +} - LPcmsNAMEDCOLORLIST v = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size); +// Add a block of characters to the intended MLU. Language and country are specified. +// Only one entry for Language/country pair is allowed. +static +cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block, + cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) +{ + cmsUInt32Number Offset; + cmsUInt8Number* Ptr; + // Sanity check + if (mlu == NULL) return FALSE; - if (v == NULL) { - cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory creating named color list"); - return NULL; + // Is there any room available? + if (mlu ->UsedEntries >= mlu ->AllocatedEntries) { + if (!GrowMLUtable(mlu)) return FALSE; + } + + // Only one ASCII string + if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed! + + // Check for size + while ((mlu ->PoolSize - mlu ->PoolUsed) < size) { + + if (!GrowMLUpool(mlu)) return FALSE; } - ZeroMemory(v, size); + Offset = mlu ->PoolUsed; + + Ptr = (cmsUInt8Number*) mlu ->MemPool; + if (Ptr == NULL) return FALSE; + + // Set the entry + memmove(Ptr + Offset, Block, size); + mlu ->PoolUsed += size; + + mlu ->Entries[mlu ->UsedEntries].StrW = Offset; + mlu ->Entries[mlu ->UsedEntries].Len = size; + mlu ->Entries[mlu ->UsedEntries].Country = CountryCode; + mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode; + mlu ->UsedEntries++; + + return TRUE; +} + +// Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some +// compilers don't properly align beginning of strings + +static +cmsUInt16Number strTo16(const char str[3]) +{ + const cmsUInt8Number* ptr8 = (const cmsUInt8Number*)str; + cmsUInt16Number n = (cmsUInt16Number)(((cmsUInt16Number)ptr8[0] << 8) | ptr8[1]); + + return n; +} + +static +void strFrom16(char str[3], cmsUInt16Number n) +{ + str[0] = (char)(n >> 8); + str[1] = (char)n; + str[2] = (char)0; + +} - v ->nColors = n; - v ->Allocated = n; - v ->Prefix[0] = 0; - v ->Suffix[0] = 0; +// Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61) +cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString) +{ + cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString); + wchar_t* WStr; + cmsBool rc; + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + if (mlu == NULL) return FALSE; + + WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t)); + if (WStr == NULL) return FALSE; + + for (i=0; i < len; i++) + WStr[i] = (wchar_t) ASCIIString[i]; + + rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry); + + _cmsFree(mlu ->ContextID, WStr); + return rc; + +} - return v; +// We don't need any wcs support library +static +cmsUInt32Number mywcslen(const wchar_t *s) +{ + const wchar_t *p; + + p = s; + while (*p) + p++; + + return (cmsUInt32Number)(p - s); +} + +// Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61) +cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString) +{ + cmsUInt16Number Lang = strTo16(Language); + cmsUInt16Number Cntry = strTo16(Country); + cmsUInt32Number len; + + if (mlu == NULL) return FALSE; + if (WideString == NULL) return FALSE; + + len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t); + return AddMLUBlock(mlu, len, WideString, Lang, Cntry); } -void cmsFreeNamedColorList(LPcmsNAMEDCOLORLIST v) +// Duplicating a MLU is as easy as copying all members +cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu) { - if (v == NULL) { - cmsSignalError(LCMS_ERRC_RECOVERABLE, "Couldn't free a NULL named color list"); - return; + cmsMLU* NewMlu = NULL; + + // Duplicating a NULL obtains a NULL + if (mlu == NULL) return NULL; + + NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries); + if (NewMlu == NULL) return NULL; + + // Should never happen + if (NewMlu ->AllocatedEntries < mlu ->UsedEntries) + goto Error; + + // Sanitize... + if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error; + + memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry)); + NewMlu ->UsedEntries = mlu ->UsedEntries; + + // The MLU may be empty + if (mlu ->PoolUsed == 0) { + NewMlu ->MemPool = NULL; + } + else { + // It is not empty + NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed); + if (NewMlu ->MemPool == NULL) goto Error; } - _cmsFree(v); + NewMlu ->PoolSize = mlu ->PoolUsed; + + if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error; + + memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed); + NewMlu ->PoolUsed = mlu ->PoolUsed; + + return NewMlu; + +Error: + + if (NewMlu != NULL) cmsMLUfree(NewMlu); + return NULL; +} + +// Free any used memory +void CMSEXPORT cmsMLUfree(cmsMLU* mlu) +{ + if (mlu) { + + if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries); + if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool); + + _cmsFree(mlu ->ContextID, mlu); + } } -LCMSBOOL cmsAppendNamedColor(cmsHTRANSFORM xform, const char* Name, WORD PCS[3], WORD Colorant[MAXCHANNELS]) + +// The algorithm first searches for an exact match of country and language, if not found it uses +// the Language. If none is found, first entry is used instead. +static +const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu, + cmsUInt32Number *len, + cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode, + cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode) { - _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; - LPcmsNAMEDCOLORLIST List; - int i; + cmsUInt32Number i; + int Best = -1; + _cmsMLUentry* v; + + if (mlu == NULL) return NULL; + + if (mlu -> AllocatedEntries <= 0) return NULL; + + for (i=0; i < mlu ->UsedEntries; i++) { + + v = mlu ->Entries + i; + + if (v -> Language == LanguageCode) { + + if (Best == -1) Best = (int) i; + + if (v -> Country == CountryCode) { + + if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; + if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; + + if (len != NULL) *len = v ->Len; + + return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match + } + } + } + + // No string found. Return First one + if (Best == -1) + Best = 0; - if (v ->NamedColorList == NULL) return FALSE; + v = mlu ->Entries + Best; + + if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; + if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; + + if (len != NULL) *len = v ->Len; + + return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW); +} + - v ->NamedColorList = GrowNamedColorList(v ->NamedColorList, v->NamedColorList ->nColors + 1); +// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len +cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize) +{ + const wchar_t *Wide; + cmsUInt32Number StrLen = 0; + cmsUInt32Number ASCIIlen, i; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + // Sanitize + if (mlu == NULL) return 0; + + // Get WideChar + Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); + if (Wide == NULL) return 0; + + ASCIIlen = StrLen / sizeof(wchar_t); + + // Maybe we want only to know the len? + if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end - List = v ->NamedColorList; + // No buffer size means no data + if (BufferSize <= 0) return 0; + + // Some clipping may be required + if (BufferSize < ASCIIlen + 1) + ASCIIlen = BufferSize - 1; + + // Precess each character + for (i=0; i < ASCIIlen; i++) { + + if (Wide[i] == 0) + Buffer[i] = 0; + else + Buffer[i] = (char) Wide[i]; + } - for (i=0; i < MAXCHANNELS; i++) - List ->List[List ->nColors].DeviceColorant[i] = Colorant[i]; + // We put a termination "\0" + Buffer[ASCIIlen] = 0; + return ASCIIlen + 1; +} + +// Obtain a wide representation of the MLU, on depending on current locale settings +cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize) +{ + const wchar_t *Wide; + cmsUInt32Number StrLen = 0; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + // Sanitize + if (mlu == NULL) return 0; + + Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); + if (Wide == NULL) return 0; - for (i=0; i < 3; i++) - List ->List[List ->nColors].PCS[i] = PCS[i]; + // Maybe we want only to know the len? + if (Buffer == NULL) return StrLen + sizeof(wchar_t); + + // No buffer size means no data + if (BufferSize <= 0) return 0; + + // Some clipping may be required + if (BufferSize < StrLen + sizeof(wchar_t)) + StrLen = BufferSize - + sizeof(wchar_t); + + memmove(Buffer, Wide, StrLen); + Buffer[StrLen / sizeof(wchar_t)] = 0; + + return StrLen + sizeof(wchar_t); +} + - strncpy(List ->List[List ->nColors].Name, Name, MAX_PATH-1); - List ->List[List ->nColors].Name[MAX_PATH-1] = 0; +// Get also the language and country +CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char ObtainedLanguage[3], char ObtainedCountry[3]) +{ + const wchar_t *Wide; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + cmsUInt16Number ObtLang, ObtCode; - List ->nColors++; + // Sanitize + if (mlu == NULL) return FALSE; + + Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode); + if (Wide == NULL) return FALSE; + + // Get used language and code + strFrom16(ObtainedLanguage, ObtLang); + strFrom16(ObtainedCountry, ObtCode); + return TRUE; } -// Returns named color count +// Get the number of translations in the MLU object +cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu) +{ + if (mlu == NULL) return 0; + return mlu->UsedEntries; +} + +// Get the language and country codes for a specific MLU index +cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, + cmsUInt32Number idx, + char LanguageCode[3], + char CountryCode[3]) +{ + _cmsMLUentry *entry; + + if (mlu == NULL) return FALSE; -int LCMSEXPORT cmsNamedColorCount(cmsHTRANSFORM xform) + if (idx >= mlu->UsedEntries) return FALSE; + + entry = &mlu->Entries[idx]; + + strFrom16(LanguageCode, entry->Language); + strFrom16(CountryCode, entry->Country); + + return TRUE; +} + + +// Named color lists -------------------------------------------------------------------------------------------- + +// Grow the list to keep at least NumElements +static +cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v) { - _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; + cmsUInt32Number size; + _cmsNAMEDCOLOR * NewPtr; + + if (v == NULL) return FALSE; + + if (v ->Allocated == 0) + size = 64; // Initial guess + else + size = v ->Allocated * 2; + + // Keep a maximum color lists can grow, 100K entries seems reasonable + if (size > 1024 * 100) { + _cmsFree(v->ContextID, (void*) v->List); + v->List = NULL; + return FALSE; + } + + NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR)); + if (NewPtr == NULL) + return FALSE; + + v ->List = NewPtr; + v ->Allocated = size; + return TRUE; +} + +// Allocate a list for n elements +cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix) +{ + cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST)); + + if (v == NULL) return NULL; + + v ->List = NULL; + v ->nColors = 0; + v ->ContextID = ContextID; - if (v ->NamedColorList == NULL) return 0; - return v ->NamedColorList ->nColors; + while (v -> Allocated < n) { + if (!GrowNamedColorList(v)) { + _cmsFree(ContextID, (void*) v); + return NULL; + } + } + + strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1); + strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1); + v->Prefix[32] = v->Suffix[32] = 0; + + v -> ColorantCount = ColorantCount; + + return v; +} + +// Free a list +void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v) +{ + if (v == NULL) return; + if (v ->List) _cmsFree(v ->ContextID, v ->List); + _cmsFree(v ->ContextID, v); +} + +cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v) +{ + cmsNAMEDCOLORLIST* NewNC; + + if (v == NULL) return NULL; + + NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix); + if (NewNC == NULL) return NULL; + + // For really large tables we need this + while (NewNC ->Allocated < v ->Allocated){ + if (!GrowNamedColorList(NewNC)) return NULL; + } + + memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix)); + memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix)); + NewNC ->ColorantCount = v ->ColorantCount; + memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR)); + NewNC ->nColors = v ->nColors; + return NewNC; } -LCMSBOOL LCMSEXPORT cmsNamedColorInfo(cmsHTRANSFORM xform, int nColor, char* Name, char* Prefix, char* Suffix) +// Append a color to a list. List pointer may change if reallocated +cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList, + const char* Name, + cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS]) +{ + cmsUInt32Number i; + + if (NamedColorList == NULL) return FALSE; + + if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) { + if (!GrowNamedColorList(NamedColorList)) return FALSE; + } + + for (i=0; i < NamedColorList ->ColorantCount; i++) + NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i]; + + for (i=0; i < 3; i++) + NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i]; + + if (Name != NULL) { + + strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1); + NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0; + + } + else + NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0; + + + NamedColorList ->nColors++; + return TRUE; +} + +// Returns number of elements +cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList) { - _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; + if (NamedColorList == NULL) return 0; + return NamedColorList ->nColors; +} + +// Info aboout a given color +cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, + char* Name, + char* Prefix, + char* Suffix, + cmsUInt16Number* PCS, + cmsUInt16Number* Colorant) +{ + if (NamedColorList == NULL) return FALSE; + + if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE; - if (v ->NamedColorList == NULL) return FALSE; + // strcpy instead of strncpy because many apps are using small buffers + if (Name) strcpy(Name, NamedColorList->List[nColor].Name); + if (Prefix) strcpy(Prefix, NamedColorList->Prefix); + if (Suffix) strcpy(Suffix, NamedColorList->Suffix); + if (PCS) + memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number)); + + if (Colorant) + memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant, + sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount); + + + return TRUE; +} + +// Search for a given color name (no prefix or suffix) +cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name) +{ + cmsUInt32Number i; + cmsUInt32Number n; + + if (NamedColorList == NULL) return -1; + n = cmsNamedColorCount(NamedColorList); + for (i=0; i < n; i++) { + if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0) + return (cmsInt32Number) i; + } - if (nColor < 0 || nColor >= cmsNamedColorCount(xform)) return FALSE; + return -1; +} + +// MPE support ----------------------------------------------------------------------------------------------------------------- + +static +void FreeNamedColorList(cmsStage* mpe) +{ + cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsFreeNamedColorList(List); +} + +static +void* DupNamedColorList(cmsStage* mpe) +{ + cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; + return cmsDupNamedColorList(List); +} + +static +void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); + + if (index >= NamedColorList-> nColors) { + cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index); + Out[0] = Out[1] = Out[2] = 0.0f; + } + else { + + // Named color always uses Lab + Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0); + Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0); + Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0); + } +} - if (Name) { strncpy(Name, v ->NamedColorList->List[nColor].Name, 31); Name[31] = 0; } - if (Prefix) { strncpy(Prefix, v ->NamedColorList->Prefix, 31); Prefix[31] = 0; } - if (Suffix) { strncpy(Suffix, v ->NamedColorList->Suffix, 31); Suffix[31] = 0; } +static +void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); + cmsUInt32Number j; + + if (index >= NamedColorList-> nColors) { + cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index); + for (j = 0; j < NamedColorList->ColorantCount; j++) + Out[j] = 0.0f; + + } + else { + for (j=0; j < NamedColorList ->ColorantCount; j++) + Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0); + } +} + - return TRUE; +// Named color lookup element +cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS) +{ + return _cmsStageAllocPlaceholder(NamedColorList ->ContextID, + cmsSigNamedColorElemType, + 1, UsePCS ? 3 : NamedColorList ->ColorantCount, + UsePCS ? EvalNamedColorPCS : EvalNamedColor, + DupNamedColorList, + FreeNamedColorList, + cmsDupNamedColorList(NamedColorList)); + +} + + +// Retrieve the named color list from a transform. Should be first element in the LUT +cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform) +{ + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + cmsStage* mpe = v ->Lut->Elements; + + if (mpe ->Type != cmsSigNamedColorElemType) return NULL; + return (cmsNAMEDCOLORLIST*) mpe ->Data; } -int LCMSEXPORT cmsNamedColorIndex(cmsHTRANSFORM xform, const char* Name) +// Profile sequence description routines ------------------------------------------------------------------------------------- + +cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n) { - _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; - int i, n; + cmsSEQ* Seq; + cmsUInt32Number i; + + if (n == 0) return NULL; + + // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked + // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door! + if (n > 255) return NULL; + + Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ)); + if (Seq == NULL) return NULL; + + Seq -> ContextID = ContextID; + Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC)); + Seq -> n = n; + + if (Seq -> seq == NULL) { + _cmsFree(ContextID, Seq); + return NULL; + } + + for (i=0; i < n; i++) { + Seq -> seq[i].Manufacturer = NULL; + Seq -> seq[i].Model = NULL; + Seq -> seq[i].Description = NULL; + } + + return Seq; +} - if (v ->NamedColorList == NULL) return -1; +void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq) +{ + cmsUInt32Number i; + + for (i=0; i < pseq ->n; i++) { + if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer); + if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model); + if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description); + } + + if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq); + _cmsFree(pseq -> ContextID, pseq); +} + +cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq) +{ + cmsSEQ *NewSeq; + cmsUInt32Number i; + + if (pseq == NULL) + return NULL; + + NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ)); + if (NewSeq == NULL) return NULL; + + + NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC)); + if (NewSeq ->seq == NULL) goto Error; + + NewSeq -> ContextID = pseq ->ContextID; + NewSeq -> n = pseq ->n; + + for (i=0; i < pseq->n; i++) { - n = cmsNamedColorCount(xform); - for (i=0; i < n; i++) { - if (stricmp(Name, v ->NamedColorList->List[i].Name) == 0) - return i; - } + memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number)); + + NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg; + NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel; + memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID)); + NewSeq ->seq[i].technology = pseq ->seq[i].technology; + + NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer); + NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model); + NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description); + + } + + return NewSeq; + +Error: + + cmsFreeProfileSequenceDescription(NewSeq); + return NULL; +} + +// Dictionaries -------------------------------------------------------------------------------------------------------- + +// Dictionaries are just very simple linked lists + + +typedef struct _cmsDICT_struct { + cmsDICTentry* head; + cmsContext ContextID; +} _cmsDICT; + - return -1; +// Allocate an empty dictionary +cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID) +{ + _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT)); + if (dict == NULL) return NULL; + + dict ->ContextID = ContextID; + return (cmsHANDLE) dict; + +} + +// Dispose resources +void CMSEXPORT cmsDictFree(cmsHANDLE hDict) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + cmsDICTentry *entry, *next; + + _cmsAssert(dict != NULL); + + // Walk the list freeing all nodes + entry = dict ->head; + while (entry != NULL) { + + if (entry ->DisplayName != NULL) cmsMLUfree(entry ->DisplayName); + if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue); + if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name); + if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value); + + // Don't fall in the habitual trap... + next = entry ->Next; + _cmsFree(dict ->ContextID, entry); + + entry = next; + } + + _cmsFree(dict ->ContextID, dict); } +// Duplicate a wide char string +static +wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr) +{ + if (ptr == NULL) return NULL; + return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t)); +} + +// Add a new entry to the linked list +cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + cmsDICTentry *entry; + + _cmsAssert(dict != NULL); + _cmsAssert(Name != NULL); + + entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry)); + if (entry == NULL) return FALSE; + + entry ->DisplayName = cmsMLUdup(DisplayName); + entry ->DisplayValue = cmsMLUdup(DisplayValue); + entry ->Name = DupWcs(dict ->ContextID, Name); + entry ->Value = DupWcs(dict ->ContextID, Value); + + entry ->Next = dict ->head; + dict ->head = entry; + + return TRUE; +} + + +// Duplicates an existing dictionary +cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict) +{ + _cmsDICT* old_dict = (_cmsDICT*) hDict; + cmsHANDLE hNew; + cmsDICTentry *entry; + + _cmsAssert(old_dict != NULL); + + hNew = cmsDictAlloc(old_dict ->ContextID); + if (hNew == NULL) return NULL; + + // Walk the list freeing all nodes + entry = old_dict ->head; + while (entry != NULL) { + + if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) { + + cmsDictFree(hNew); + return NULL; + } + + entry = entry -> Next; + } + + return hNew; +} + +// Get a pointer to the linked list +const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + + if (dict == NULL) return NULL; + return dict ->head; +} + +// Helper For external languages +const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e) +{ + if (e == NULL) return NULL; + return e ->Next; +} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsopt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsopt.c Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,1996 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +//---------------------------------------------------------------------------------- + +// Optimization for 8 bits, Shaper-CLUT (3 inputs only) +typedef struct { + + cmsContext ContextID; + + const cmsInterpParams* p; // Tetrahedrical interpolation parameters. This is a not-owned pointer. + + cmsUInt16Number rx[256], ry[256], rz[256]; + cmsUInt32Number X0[256], Y0[256], Z0[256]; // Precomputed nodes and offsets for 8-bit input data + + +} Prelin8Data; + + +// Generic optimization for 16 bits Shaper-CLUT-Shaper (any inputs) +typedef struct { + + cmsContext ContextID; + + // Number of channels + cmsUInt32Number nInputs; + cmsUInt32Number nOutputs; + + _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance + cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS]; + + _cmsInterpFn16 EvalCLUT; // The evaluator for 3D grid + const cmsInterpParams* CLUTparams; // (not-owned pointer) + + + _cmsInterpFn16* EvalCurveOut16; // Points to an array of curve evaluators in 16 bits (not-owned pointer) + cmsInterpParams** ParamsCurveOut16; // Points to an array of references to interpolation params (not-owned pointer) + + +} Prelin16Data; + + +// Optimization for matrix-shaper in 8 bits. Numbers are operated in n.14 signed, tables are stored in 1.14 fixed + +typedef cmsInt32Number cmsS1Fixed14Number; // Note that this may hold more than 16 bits! + +#define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5)) + +typedef struct { + + cmsContext ContextID; + + cmsS1Fixed14Number Shaper1R[256]; // from 0..255 to 1.14 (0.0...1.0) + cmsS1Fixed14Number Shaper1G[256]; + cmsS1Fixed14Number Shaper1B[256]; + + cmsS1Fixed14Number Mat[3][3]; // n.14 to n.14 (needs a saturation after that) + cmsS1Fixed14Number Off[3]; + + cmsUInt16Number Shaper2R[16385]; // 1.14 to 0..255 + cmsUInt16Number Shaper2G[16385]; + cmsUInt16Number Shaper2B[16385]; + +} MatShaper8Data; + +// Curves, optimization is shared between 8 and 16 bits +typedef struct { + + cmsContext ContextID; + + cmsUInt32Number nCurves; // Number of curves + cmsUInt32Number nElements; // Elements in curves + cmsUInt16Number** Curves; // Points to a dynamically allocated array + +} Curves16Data; + + +// Simple optimizations ---------------------------------------------------------------------------------------------------------- + + +// Remove an element in linked chain +static +void _RemoveElement(cmsStage** head) +{ + cmsStage* mpe = *head; + cmsStage* next = mpe ->Next; + *head = next; + cmsStageFree(mpe); +} + +// Remove all identities in chain. Note that pt actually is a double pointer to the element that holds the pointer. +static +cmsBool _Remove1Op(cmsPipeline* Lut, cmsStageSignature UnaryOp) +{ + cmsStage** pt = &Lut ->Elements; + cmsBool AnyOpt = FALSE; + + while (*pt != NULL) { + + if ((*pt) ->Implements == UnaryOp) { + _RemoveElement(pt); + AnyOpt = TRUE; + } + else + pt = &((*pt) -> Next); + } + + return AnyOpt; +} + +// Same, but only if two adjacent elements are found +static +cmsBool _Remove2Op(cmsPipeline* Lut, cmsStageSignature Op1, cmsStageSignature Op2) +{ + cmsStage** pt1; + cmsStage** pt2; + cmsBool AnyOpt = FALSE; + + pt1 = &Lut ->Elements; + if (*pt1 == NULL) return AnyOpt; + + while (*pt1 != NULL) { + + pt2 = &((*pt1) -> Next); + if (*pt2 == NULL) return AnyOpt; + + if ((*pt1) ->Implements == Op1 && (*pt2) ->Implements == Op2) { + _RemoveElement(pt2); + _RemoveElement(pt1); + AnyOpt = TRUE; + } + else + pt1 = &((*pt1) -> Next); + } + + return AnyOpt; +} + + +static +cmsBool CloseEnoughFloat(cmsFloat64Number a, cmsFloat64Number b) +{ + return fabs(b - a) < 0.00001f; +} + +static +cmsBool isFloatMatrixIdentity(const cmsMAT3* a) +{ + cmsMAT3 Identity; + int i, j; + + _cmsMAT3identity(&Identity); + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + if (!CloseEnoughFloat(a->v[i].n[j], Identity.v[i].n[j])) return FALSE; + + return TRUE; +} +// if two adjacent matrices are found, multiply them. +static +cmsBool _MultiplyMatrix(cmsPipeline* Lut) +{ + cmsStage** pt1; + cmsStage** pt2; + cmsStage* chain; + cmsBool AnyOpt = FALSE; + + pt1 = &Lut->Elements; + if (*pt1 == NULL) return AnyOpt; + + while (*pt1 != NULL) { + + pt2 = &((*pt1)->Next); + if (*pt2 == NULL) return AnyOpt; + + if ((*pt1)->Implements == cmsSigMatrixElemType && (*pt2)->Implements == cmsSigMatrixElemType) { + + // Get both matrices + _cmsStageMatrixData* m1 = (_cmsStageMatrixData*) cmsStageData(*pt1); + _cmsStageMatrixData* m2 = (_cmsStageMatrixData*) cmsStageData(*pt2); + cmsMAT3 res; + + // Input offset and output offset should be zero to use this optimization + if (m1->Offset != NULL || m2 ->Offset != NULL || + cmsStageInputChannels(*pt1) != 3 || cmsStageOutputChannels(*pt1) != 3 || + cmsStageInputChannels(*pt2) != 3 || cmsStageOutputChannels(*pt2) != 3) + return FALSE; + + // Multiply both matrices to get the result + _cmsMAT3per(&res, (cmsMAT3*)m2->Double, (cmsMAT3*)m1->Double); + + // Get the next in chain after the matrices + chain = (*pt2)->Next; + + // Remove both matrices + _RemoveElement(pt2); + _RemoveElement(pt1); + + // Now what if the result is a plain identity? + if (!isFloatMatrixIdentity(&res)) { + + // We can not get rid of full matrix + cmsStage* Multmat = cmsStageAllocMatrix(Lut->ContextID, 3, 3, (const cmsFloat64Number*) &res, NULL); + if (Multmat == NULL) return FALSE; // Should never happen + + // Recover the chain + Multmat->Next = chain; + *pt1 = Multmat; + } + + AnyOpt = TRUE; + } + else + pt1 = &((*pt1)->Next); + } + + return AnyOpt; +} + + +// Preoptimize just gets rif of no-ops coming paired. Conversion from v2 to v4 followed +// by a v4 to v2 and vice-versa. The elements are then discarded. +static +cmsBool PreOptimize(cmsPipeline* Lut) +{ + cmsBool AnyOpt = FALSE, Opt; + + do { + + Opt = FALSE; + + // Remove all identities + Opt |= _Remove1Op(Lut, cmsSigIdentityElemType); + + // Remove XYZ2Lab followed by Lab2XYZ + Opt |= _Remove2Op(Lut, cmsSigXYZ2LabElemType, cmsSigLab2XYZElemType); + + // Remove Lab2XYZ followed by XYZ2Lab + Opt |= _Remove2Op(Lut, cmsSigLab2XYZElemType, cmsSigXYZ2LabElemType); + + // Remove V4 to V2 followed by V2 to V4 + Opt |= _Remove2Op(Lut, cmsSigLabV4toV2, cmsSigLabV2toV4); + + // Remove V2 to V4 followed by V4 to V2 + Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2); + + // Remove float pcs Lab conversions + Opt |= _Remove2Op(Lut, cmsSigLab2FloatPCS, cmsSigFloatPCS2Lab); + + // Remove float pcs Lab conversions + Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ); + + // Simplify matrix. + Opt |= _MultiplyMatrix(Lut); + + if (Opt) AnyOpt = TRUE; + + } while (Opt); + + return AnyOpt; +} + +static +void Eval16nop1D(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const struct _cms_interp_struc* p) +{ + Output[0] = Input[0]; + + cmsUNUSED_PARAMETER(p); +} + +static +void PrelinEval16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const void* D) +{ + Prelin16Data* p16 = (Prelin16Data*) D; + cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS]; + cmsUInt16Number StageDEF[cmsMAXCHANNELS]; + cmsUInt32Number i; + + for (i=0; i < p16 ->nInputs; i++) { + + p16 ->EvalCurveIn16[i](&Input[i], &StageABC[i], p16 ->ParamsCurveIn16[i]); + } + + p16 ->EvalCLUT(StageABC, StageDEF, p16 ->CLUTparams); + + for (i=0; i < p16 ->nOutputs; i++) { + + p16 ->EvalCurveOut16[i](&StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]); + } +} + + +static +void PrelinOpt16free(cmsContext ContextID, void* ptr) +{ + Prelin16Data* p16 = (Prelin16Data*) ptr; + + _cmsFree(ContextID, p16 ->EvalCurveOut16); + _cmsFree(ContextID, p16 ->ParamsCurveOut16); + + _cmsFree(ContextID, p16); +} + +static +void* Prelin16dup(cmsContext ContextID, const void* ptr) +{ + Prelin16Data* p16 = (Prelin16Data*) ptr; + Prelin16Data* Duped = (Prelin16Data*) _cmsDupMem(ContextID, p16, sizeof(Prelin16Data)); + + if (Duped == NULL) return NULL; + + Duped->EvalCurveOut16 = (_cmsInterpFn16*) _cmsDupMem(ContextID, p16->EvalCurveOut16, p16->nOutputs * sizeof(_cmsInterpFn16)); + Duped->ParamsCurveOut16 = (cmsInterpParams**)_cmsDupMem(ContextID, p16->ParamsCurveOut16, p16->nOutputs * sizeof(cmsInterpParams*)); + + return Duped; +} + + +static +Prelin16Data* PrelinOpt16alloc(cmsContext ContextID, + const cmsInterpParams* ColorMap, + cmsUInt32Number nInputs, cmsToneCurve** In, + cmsUInt32Number nOutputs, cmsToneCurve** Out ) +{ + cmsUInt32Number i; + Prelin16Data* p16 = (Prelin16Data*)_cmsMallocZero(ContextID, sizeof(Prelin16Data)); + if (p16 == NULL) return NULL; + + p16 ->nInputs = nInputs; + p16 ->nOutputs = nOutputs; + + + for (i=0; i < nInputs; i++) { + + if (In == NULL) { + p16 -> ParamsCurveIn16[i] = NULL; + p16 -> EvalCurveIn16[i] = Eval16nop1D; + + } + else { + p16 -> ParamsCurveIn16[i] = In[i] ->InterpParams; + p16 -> EvalCurveIn16[i] = p16 ->ParamsCurveIn16[i]->Interpolation.Lerp16; + } + } + + p16 ->CLUTparams = ColorMap; + p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16; + + + p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16)); + p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* )); + + for (i=0; i < nOutputs; i++) { + + if (Out == NULL) { + p16 ->ParamsCurveOut16[i] = NULL; + p16 -> EvalCurveOut16[i] = Eval16nop1D; + } + else { + + p16 ->ParamsCurveOut16[i] = Out[i] ->InterpParams; + p16 -> EvalCurveOut16[i] = p16 ->ParamsCurveOut16[i]->Interpolation.Lerp16; + } + } + + return p16; +} + + + +// Resampling --------------------------------------------------------------------------------- + +#define PRELINEARIZATION_POINTS 4096 + +// Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for +// almost any transform. We use floating point precision and then convert from floating point to 16 bits. +static +cmsInt32Number XFormSampler16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + cmsPipeline* Lut = (cmsPipeline*) Cargo; + cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; + cmsUInt32Number i; + + _cmsAssert(Lut -> InputChannels < cmsMAXCHANNELS); + _cmsAssert(Lut -> OutputChannels < cmsMAXCHANNELS); + + // From 16 bit to floating point + for (i=0; i < Lut ->InputChannels; i++) + InFloat[i] = (cmsFloat32Number) (In[i] / 65535.0); + + // Evaluate in floating point + cmsPipelineEvalFloat(InFloat, OutFloat, Lut); + + // Back to 16 bits representation + for (i=0; i < Lut ->OutputChannels; i++) + Out[i] = _cmsQuickSaturateWord(OutFloat[i] * 65535.0); + + // Always succeed + return TRUE; +} + +// Try to see if the curves of a given MPE are linear +static +cmsBool AllCurvesAreLinear(cmsStage* mpe) +{ + cmsToneCurve** Curves; + cmsUInt32Number i, n; + + Curves = _cmsStageGetPtrToCurveSet(mpe); + if (Curves == NULL) return FALSE; + + n = cmsStageOutputChannels(mpe); + + for (i=0; i < n; i++) { + if (!cmsIsToneCurveLinear(Curves[i])) return FALSE; + } + + return TRUE; +} + +// This function replaces a specific node placed in "At" by the "Value" numbers. Its purpose +// is to fix scum dot on broken profiles/transforms. Works on 1, 3 and 4 channels +static +cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[], + cmsUInt32Number nChannelsOut, cmsUInt32Number nChannelsIn) +{ + _cmsStageCLutData* Grid = (_cmsStageCLutData*) CLUT ->Data; + cmsInterpParams* p16 = Grid ->Params; + cmsFloat64Number px, py, pz, pw; + int x0, y0, z0, w0; + int i, index; + + if (CLUT -> Type != cmsSigCLutElemType) { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut stage"); + return FALSE; + } + + if (nChannelsIn == 4) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; + pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; + pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + w0 = (int) floor(pw); + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0) || + ((pw - w0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[3] * x0 + + (int) p16 -> opta[2] * y0 + + (int) p16 -> opta[1] * z0 + + (int) p16 -> opta[0] * w0; + } + else + if (nChannelsIn == 3) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; + pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[2] * x0 + + (int) p16 -> opta[1] * y0 + + (int) p16 -> opta[0] * z0; + } + else + if (nChannelsIn == 1) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + + x0 = (int) floor(px); + + if (((px - x0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[0] * x0; + } + else { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); + return FALSE; + } + + for (i = 0; i < (int) nChannelsOut; i++) + Grid->Tab.T[index + i] = Value[i]; + + return TRUE; +} + +// Auxiliary, to see if two values are equal or very different +static +cmsBool WhitesAreEqual(cmsUInt32Number n, cmsUInt16Number White1[], cmsUInt16Number White2[] ) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + + if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremely different that the fixup should be avoided + if (White1[i] != White2[i]) return FALSE; + } + return TRUE; +} + + +// Locate the node for the white point and fix it to pure white in order to avoid scum dot. +static +cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColorSpace, cmsColorSpaceSignature ExitColorSpace) +{ + cmsUInt16Number *WhitePointIn, *WhitePointOut; + cmsUInt16Number WhiteIn[cmsMAXCHANNELS], WhiteOut[cmsMAXCHANNELS], ObtainedOut[cmsMAXCHANNELS]; + cmsUInt32Number i, nOuts, nIns; + cmsStage *PreLin = NULL, *CLUT = NULL, *PostLin = NULL; + + if (!_cmsEndPointsBySpace(EntryColorSpace, + &WhitePointIn, NULL, &nIns)) return FALSE; + + if (!_cmsEndPointsBySpace(ExitColorSpace, + &WhitePointOut, NULL, &nOuts)) return FALSE; + + // It needs to be fixed? + if (Lut ->InputChannels != nIns) return FALSE; + if (Lut ->OutputChannels != nOuts) return FALSE; + + cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut); + + if (WhitesAreEqual(nOuts, WhitePointOut, ObtainedOut)) return TRUE; // whites already match + + // Check if the LUT comes as Prelin, CLUT or Postlin. We allow all combinations + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &PreLin, &CLUT, &PostLin)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCurveSetElemType, cmsSigCLutElemType, &PreLin, &CLUT)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCLutElemType, cmsSigCurveSetElemType, &CLUT, &PostLin)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCLutElemType, &CLUT)) + return FALSE; + + // We need to interpolate white points of both, pre and post curves + if (PreLin) { + + cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PreLin); + + for (i=0; i < nIns; i++) { + WhiteIn[i] = cmsEvalToneCurve16(Curves[i], WhitePointIn[i]); + } + } + else { + for (i=0; i < nIns; i++) + WhiteIn[i] = WhitePointIn[i]; + } + + // If any post-linearization, we need to find how is represented white before the curve, do + // a reverse interpolation in this case. + if (PostLin) { + + cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PostLin); + + for (i=0; i < nOuts; i++) { + + cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]); + if (InversePostLin == NULL) { + WhiteOut[i] = WhitePointOut[i]; + + } else { + + WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); + cmsFreeToneCurve(InversePostLin); + } + } + } + else { + for (i=0; i < nOuts; i++) + WhiteOut[i] = WhitePointOut[i]; + } + + // Ok, proceed with patching. May fail and we don't care if it fails + PatchLUT(CLUT, WhiteIn, WhiteOut, nOuts, nIns); + + return TRUE; +} + +// ----------------------------------------------------------------------------------------------------------------------------------------------- +// This function creates simple LUT from complex ones. The generated LUT has an optional set of +// prelinearization curves, a CLUT of nGridPoints and optional postlinearization tables. +// These curves have to exist in the original LUT in order to be used in the simplified output. +// Caller may also use the flags to allow this feature. +// LUTS with all curves will be simplified to a single curve. Parametric curves are lost. +// This function should be used on 16-bits LUTS only, as floating point losses precision when simplified +// ----------------------------------------------------------------------------------------------------------------------------------------------- + +static +cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsPipeline* Src = NULL; + cmsPipeline* Dest = NULL; + cmsStage* mpe; + cmsStage* CLUT; + cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; + cmsUInt32Number nGridPoints; + cmsColorSpaceSignature ColorSpace, OutputColorSpace; + cmsStage *NewPreLin = NULL; + cmsStage *NewPostLin = NULL; + _cmsStageCLutData* DataCLUT; + cmsToneCurve** DataSetIn; + cmsToneCurve** DataSetOut; + Prelin16Data* p16; + + // This is a loosy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat)); + OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat)); + + // Color space must be specified + if (ColorSpace == (cmsColorSpaceSignature)0 || + OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE; + + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + + // For empty LUTs, 2 points are enough + if (cmsPipelineStageCount(*Lut) == 0) + nGridPoints = 2; + + Src = *Lut; + + // Named color pipelines cannot be optimized either + for (mpe = cmsPipelineGetPtrToFirstStage(Src); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (!Dest) return FALSE; + + // Prelinearization tables are kept unless indicated by flags + if (*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION) { + + // Get a pointer to the prelinearization element + cmsStage* PreLin = cmsPipelineGetPtrToFirstStage(Src); + + // Check if suitable + if (PreLin && PreLin ->Type == cmsSigCurveSetElemType) { + + // Maybe this is a linear tram, so we can avoid the whole stuff + if (!AllCurvesAreLinear(PreLin)) { + + // All seems ok, proceed. + NewPreLin = cmsStageDup(PreLin); + if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin)) + goto Error; + + // Remove prelinearization. Since we have duplicated the curve + // in destination LUT, the sampling should be applied after this stage. + cmsPipelineUnlinkStage(Src, cmsAT_BEGIN, &KeepPreLin); + } + } + } + + // Allocate the CLUT + CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL); + if (CLUT == NULL) goto Error; + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) { + goto Error; + } + + // Postlinearization tables are kept unless indicated by flags + if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) { + + // Get a pointer to the postlinearization if present + cmsStage* PostLin = cmsPipelineGetPtrToLastStage(Src); + + // Check if suitable + if (PostLin && cmsStageType(PostLin) == cmsSigCurveSetElemType) { + + // Maybe this is a linear tram, so we can avoid the whole stuff + if (!AllCurvesAreLinear(PostLin)) { + + // All seems ok, proceed. + NewPostLin = cmsStageDup(PostLin); + if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin)) + goto Error; + + // In destination LUT, the sampling should be applied after this stage. + cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin); + } + } + } + + // Now its time to do the sampling. We have to ignore pre/post linearization + // The source LUT without pre/post curves is passed as parameter. + if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) { +Error: + // Ops, something went wrong, Restore stages + if (KeepPreLin != NULL) { + if (!cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin)) { + _cmsAssert(0); // This never happens + } + } + if (KeepPostLin != NULL) { + if (!cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin)) { + _cmsAssert(0); // This never happens + } + } + cmsPipelineFree(Dest); + return FALSE; + } + + // Done. + + if (KeepPreLin != NULL) cmsStageFree(KeepPreLin); + if (KeepPostLin != NULL) cmsStageFree(KeepPostLin); + cmsPipelineFree(Src); + + DataCLUT = (_cmsStageCLutData*) CLUT ->Data; + + if (NewPreLin == NULL) DataSetIn = NULL; + else DataSetIn = ((_cmsStageToneCurvesData*) NewPreLin ->Data) ->TheCurves; + + if (NewPostLin == NULL) DataSetOut = NULL; + else DataSetOut = ((_cmsStageToneCurvesData*) NewPostLin ->Data) ->TheCurves; + + + if (DataSetIn == NULL && DataSetOut == NULL) { + + _cmsPipelineSetOptimizationParameters(Dest, (_cmsOPTeval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL); + } + else { + + p16 = PrelinOpt16alloc(Dest ->ContextID, + DataCLUT ->Params, + Dest ->InputChannels, + DataSetIn, + Dest ->OutputChannels, + DataSetOut); + + _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); + } + + + // Don't fix white on absolute colorimetric + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + + if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { + + FixWhiteMisalignment(Dest, ColorSpace, OutputColorSpace); + } + + *Lut = Dest; + return TRUE; + + cmsUNUSED_PARAMETER(Intent); +} + + +// ----------------------------------------------------------------------------------------------------------------------------------------------- +// Fixes the gamma balancing of transform. This is described in my paper "Prelinearization Stages on +// Color-Management Application-Specific Integrated Circuits (ASICs)" presented at NIP24. It only works +// for RGB transforms. See the paper for more details +// ----------------------------------------------------------------------------------------------------------------------------------------------- + + +// Normalize endpoints by slope limiting max and min. This assures endpoints as well. +// Descending curves are handled as well. +static +void SlopeLimiting(cmsToneCurve* g) +{ + int BeginVal, EndVal; + int AtBegin = (int) floor((cmsFloat64Number) g ->nEntries * 0.02 + 0.5); // Cutoff at 2% + int AtEnd = (int) g ->nEntries - AtBegin - 1; // And 98% + cmsFloat64Number Val, Slope, beta; + int i; + + if (cmsIsToneCurveDescending(g)) { + BeginVal = 0xffff; EndVal = 0; + } + else { + BeginVal = 0; EndVal = 0xffff; + } + + // Compute slope and offset for begin of curve + Val = g ->Table16[AtBegin]; + Slope = (Val - BeginVal) / AtBegin; + beta = Val - Slope * AtBegin; + + for (i=0; i < AtBegin; i++) + g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); + + // Compute slope and offset for the end + Val = g ->Table16[AtEnd]; + Slope = (EndVal - Val) / AtBegin; // AtBegin holds the X interval, which is same in both cases + beta = Val - Slope * AtEnd; + + for (i = AtEnd; i < (int) g ->nEntries; i++) + g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); +} + + +// Precomputes tables for 8-bit on input devicelink. +static +Prelin8Data* PrelinOpt8alloc(cmsContext ContextID, const cmsInterpParams* p, cmsToneCurve* G[3]) +{ + int i; + cmsUInt16Number Input[3]; + cmsS15Fixed16Number v1, v2, v3; + Prelin8Data* p8; + + p8 = (Prelin8Data*)_cmsMallocZero(ContextID, sizeof(Prelin8Data)); + if (p8 == NULL) return NULL; + + // Since this only works for 8 bit input, values comes always as x * 257, + // we can safely take msb byte (x << 8 + x) + + for (i=0; i < 256; i++) { + + if (G != NULL) { + + // Get 16-bit representation + Input[0] = cmsEvalToneCurve16(G[0], FROM_8_TO_16(i)); + Input[1] = cmsEvalToneCurve16(G[1], FROM_8_TO_16(i)); + Input[2] = cmsEvalToneCurve16(G[2], FROM_8_TO_16(i)); + } + else { + Input[0] = FROM_8_TO_16(i); + Input[1] = FROM_8_TO_16(i); + Input[2] = FROM_8_TO_16(i); + } + + + // Move to 0..1.0 in fixed domain + v1 = _cmsToFixedDomain((int) (Input[0] * p -> Domain[0])); + v2 = _cmsToFixedDomain((int) (Input[1] * p -> Domain[1])); + v3 = _cmsToFixedDomain((int) (Input[2] * p -> Domain[2])); + + // Store the precalculated table of nodes + p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1)); + p8 ->Y0[i] = (p->opta[1] * FIXED_TO_INT(v2)); + p8 ->Z0[i] = (p->opta[0] * FIXED_TO_INT(v3)); + + // Store the precalculated table of offsets + p8 ->rx[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v1); + p8 ->ry[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v2); + p8 ->rz[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v3); + } + + p8 ->ContextID = ContextID; + p8 ->p = p; + + return p8; +} + +static +void Prelin8free(cmsContext ContextID, void* ptr) +{ + _cmsFree(ContextID, ptr); +} + +static +void* Prelin8dup(cmsContext ContextID, const void* ptr) +{ + return _cmsDupMem(ContextID, ptr, sizeof(Prelin8Data)); +} + + + +// A optimized interpolation for 8-bit input. +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static +void PrelinEval8(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const void* D) +{ + + cmsUInt8Number r, g, b; + cmsS15Fixed16Number rx, ry, rz; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + int OutChan; + register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + Prelin8Data* p8 = (Prelin8Data*) D; + register const cmsInterpParams* p = p8 ->p; + int TotalOut = (int) p -> nOutputs; + const cmsUInt16Number* LutTable = (const cmsUInt16Number*) p->Table; + + r = (cmsUInt8Number) (Input[0] >> 8); + g = (cmsUInt8Number) (Input[1] >> 8); + b = (cmsUInt8Number) (Input[2] >> 8); + + X0 = X1 = (cmsS15Fixed16Number) p8->X0[r]; + Y0 = Y1 = (cmsS15Fixed16Number) p8->Y0[g]; + Z0 = Z1 = (cmsS15Fixed16Number) p8->Z0[b]; + + rx = p8 ->rx[r]; + ry = p8 ->ry[g]; + rz = p8 ->rz[b]; + + X1 = X0 + (cmsS15Fixed16Number)((rx == 0) ? 0 : p ->opta[2]); + Y1 = Y0 + (cmsS15Fixed16Number)((ry == 0) ? 0 : p ->opta[1]); + Z1 = Z0 + (cmsS15Fixed16Number)((rz == 0) ? 0 : p ->opta[0]); + + + // These are the 6 Tetrahedral + for (OutChan=0; OutChan < TotalOut; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) + { + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + } + else + if (rx >= rz && rz >= ry) + { + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + } + else + if (rz >= rx && rx >= ry) + { + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + } + else + if (ry >= rx && rx >= rz) + { + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + } + else + if (ry >= rz && rz >= rx) + { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + } + else + if (rz >= ry && ry >= rx) + { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + Output[OutChan] = (cmsUInt16Number) (c0 + ((Rest + (Rest >> 16)) >> 16)); + + } +} + +#undef DENS + + +// Curves that contain wide empty areas are not optimizeable +static +cmsBool IsDegenerated(const cmsToneCurve* g) +{ + cmsUInt32Number i, Zeros = 0, Poles = 0; + cmsUInt32Number nEntries = g ->nEntries; + + for (i=0; i < nEntries; i++) { + + if (g ->Table16[i] == 0x0000) Zeros++; + if (g ->Table16[i] == 0xffff) Poles++; + } + + if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables + if (Zeros > (nEntries / 20)) return TRUE; // Degenerated, many zeros + if (Poles > (nEntries / 20)) return TRUE; // Degenerated, many poles + + return FALSE; +} + +// -------------------------------------------------------------------------------------------------------------- +// We need xput over here + +static +cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsPipeline* OriginalLut; + cmsUInt32Number nGridPoints; + cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS]; + cmsUInt32Number t, i; + cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS]; + cmsBool lIsSuitable, lIsLinear; + cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL; + cmsStage* OptimizedCLUTmpe; + cmsColorSpaceSignature ColorSpace, OutputColorSpace; + cmsStage* OptimizedPrelinMpe; + cmsStage* mpe; + cmsToneCurve** OptimizedPrelinCurves; + _cmsStageCLutData* OptimizedPrelinCLUT; + + + // This is a loosy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + // Only on chunky RGB + if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE; + if (T_PLANAR(*InputFormat)) return FALSE; + + if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE; + if (T_PLANAR(*OutputFormat)) return FALSE; + + // On 16 bits, user has to specify the feature + if (!_cmsFormatterIs8bit(*InputFormat)) { + if (!(*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION)) return FALSE; + } + + OriginalLut = *Lut; + + // Named color pipelines cannot be optimized either + for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + + ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat)); + OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat)); + + // Color space must be specified + if (ColorSpace == (cmsColorSpaceSignature)0 || + OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE; + + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + + // Empty gamma containers + memset(Trans, 0, sizeof(Trans)); + memset(TransReverse, 0, sizeof(TransReverse)); + + // If the last stage of the original lut are curves, and those curves are + // degenerated, it is likely the transform is squeezing and clipping + // the output from previous CLUT. We cannot optimize this case + { + cmsStage* last = cmsPipelineGetPtrToLastStage(OriginalLut); + + if (cmsStageType(last) == cmsSigCurveSetElemType) { + + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*)cmsStageData(last); + for (i = 0; i < Data->nCurves; i++) { + if (IsDegenerated(Data->TheCurves[i])) + goto Error; + } + } + } + + for (t = 0; t < OriginalLut ->InputChannels; t++) { + Trans[t] = cmsBuildTabulatedToneCurve16(OriginalLut ->ContextID, PRELINEARIZATION_POINTS, NULL); + if (Trans[t] == NULL) goto Error; + } + + // Populate the curves + for (i=0; i < PRELINEARIZATION_POINTS; i++) { + + v = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); + + // Feed input with a gray ramp + for (t=0; t < OriginalLut ->InputChannels; t++) + In[t] = v; + + // Evaluate the gray value + cmsPipelineEvalFloat(In, Out, OriginalLut); + + // Store result in curve + for (t=0; t < OriginalLut ->InputChannels; t++) + Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0); + } + + // Slope-limit the obtained curves + for (t = 0; t < OriginalLut ->InputChannels; t++) + SlopeLimiting(Trans[t]); + + // Check for validity + lIsSuitable = TRUE; + lIsLinear = TRUE; + for (t=0; (lIsSuitable && (t < OriginalLut ->InputChannels)); t++) { + + // Exclude if already linear + if (!cmsIsToneCurveLinear(Trans[t])) + lIsLinear = FALSE; + + // Exclude if non-monotonic + if (!cmsIsToneCurveMonotonic(Trans[t])) + lIsSuitable = FALSE; + + if (IsDegenerated(Trans[t])) + lIsSuitable = FALSE; + } + + // If it is not suitable, just quit + if (!lIsSuitable) goto Error; + + // Invert curves if possible + for (t = 0; t < OriginalLut ->InputChannels; t++) { + TransReverse[t] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS, Trans[t]); + if (TransReverse[t] == NULL) goto Error; + } + + // Now inset the reversed curves at the begin of transform + LutPlusCurves = cmsPipelineDup(OriginalLut); + if (LutPlusCurves == NULL) goto Error; + + if (!cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse))) + goto Error; + + // Create the result LUT + OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels); + if (OptimizedLUT == NULL) goto Error; + + OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans); + + // Create and insert the curves at the beginning + if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe)) + goto Error; + + // Allocate the CLUT for result + OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL); + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe)) + goto Error; + + // Resample the LUT + if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error; + + // Free resources + for (t = 0; t < OriginalLut ->InputChannels; t++) { + + if (Trans[t]) cmsFreeToneCurve(Trans[t]); + if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); + } + + cmsPipelineFree(LutPlusCurves); + + + OptimizedPrelinCurves = _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe); + OptimizedPrelinCLUT = (_cmsStageCLutData*) OptimizedCLUTmpe ->Data; + + // Set the evaluator if 8-bit + if (_cmsFormatterIs8bit(*InputFormat)) { + + Prelin8Data* p8 = PrelinOpt8alloc(OptimizedLUT ->ContextID, + OptimizedPrelinCLUT ->Params, + OptimizedPrelinCurves); + if (p8 == NULL) { + cmsPipelineFree(OptimizedLUT); + return FALSE; + } + + _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval8, (void*) p8, Prelin8free, Prelin8dup); + + } + else + { + Prelin16Data* p16 = PrelinOpt16alloc(OptimizedLUT ->ContextID, + OptimizedPrelinCLUT ->Params, + 3, OptimizedPrelinCurves, 3, NULL); + if (p16 == NULL) { + cmsPipelineFree(OptimizedLUT); + return FALSE; + } + + _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); + + } + + // Don't fix white on absolute colorimetric + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + + if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { + + if (!FixWhiteMisalignment(OptimizedLUT, ColorSpace, OutputColorSpace)) { + + return FALSE; + } + } + + // And return the obtained LUT + + cmsPipelineFree(OriginalLut); + *Lut = OptimizedLUT; + return TRUE; + +Error: + + for (t = 0; t < OriginalLut ->InputChannels; t++) { + + if (Trans[t]) cmsFreeToneCurve(Trans[t]); + if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); + } + + if (LutPlusCurves != NULL) cmsPipelineFree(LutPlusCurves); + if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT); + + return FALSE; + + cmsUNUSED_PARAMETER(Intent); + cmsUNUSED_PARAMETER(lIsLinear); +} + + +// Curves optimizer ------------------------------------------------------------------------------------------------------------------ + +static +void CurvesFree(cmsContext ContextID, void* ptr) +{ + Curves16Data* Data = (Curves16Data*) ptr; + cmsUInt32Number i; + + for (i=0; i < Data -> nCurves; i++) { + + _cmsFree(ContextID, Data ->Curves[i]); + } + + _cmsFree(ContextID, Data ->Curves); + _cmsFree(ContextID, ptr); +} + +static +void* CurvesDup(cmsContext ContextID, const void* ptr) +{ + Curves16Data* Data = (Curves16Data*)_cmsDupMem(ContextID, ptr, sizeof(Curves16Data)); + cmsUInt32Number i; + + if (Data == NULL) return NULL; + + Data->Curves = (cmsUInt16Number**) _cmsDupMem(ContextID, Data->Curves, Data->nCurves * sizeof(cmsUInt16Number*)); + + for (i=0; i < Data -> nCurves; i++) { + Data->Curves[i] = (cmsUInt16Number*) _cmsDupMem(ContextID, Data->Curves[i], Data->nElements * sizeof(cmsUInt16Number)); + } + + return (void*) Data; +} + +// Precomputes tables for 8-bit on input devicelink. +static +Curves16Data* CurvesAlloc(cmsContext ContextID, cmsUInt32Number nCurves, cmsUInt32Number nElements, cmsToneCurve** G) +{ + cmsUInt32Number i, j; + Curves16Data* c16; + + c16 = (Curves16Data*)_cmsMallocZero(ContextID, sizeof(Curves16Data)); + if (c16 == NULL) return NULL; + + c16 ->nCurves = nCurves; + c16 ->nElements = nElements; + + c16->Curves = (cmsUInt16Number**) _cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*)); + if (c16->Curves == NULL) { + _cmsFree(ContextID, c16); + return NULL; + } + + for (i=0; i < nCurves; i++) { + + c16->Curves[i] = (cmsUInt16Number*) _cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number)); + + if (c16->Curves[i] == NULL) { + + for (j=0; j < i; j++) { + _cmsFree(ContextID, c16->Curves[j]); + } + _cmsFree(ContextID, c16->Curves); + _cmsFree(ContextID, c16); + return NULL; + } + + if (nElements == 256U) { + + for (j=0; j < nElements; j++) { + + c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], FROM_8_TO_16(j)); + } + } + else { + + for (j=0; j < nElements; j++) { + c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], (cmsUInt16Number) j); + } + } + } + + return c16; +} + +static +void FastEvaluateCurves8(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* D) +{ + Curves16Data* Data = (Curves16Data*) D; + int x; + cmsUInt32Number i; + + for (i=0; i < Data ->nCurves; i++) { + + x = (In[i] >> 8); + Out[i] = Data -> Curves[i][x]; + } +} + + +static +void FastEvaluateCurves16(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* D) +{ + Curves16Data* Data = (Curves16Data*) D; + cmsUInt32Number i; + + for (i=0; i < Data ->nCurves; i++) { + Out[i] = Data -> Curves[i][In[i]]; + } +} + + +static +void FastIdentity16(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* D) +{ + cmsPipeline* Lut = (cmsPipeline*) D; + cmsUInt32Number i; + + for (i=0; i < Lut ->InputChannels; i++) { + Out[i] = In[i]; + } +} + + +// If the target LUT holds only curves, the optimization procedure is to join all those +// curves together. That only works on curves and does not work on matrices. +static +cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsToneCurve** GammaTables = NULL; + cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; + cmsUInt32Number i, j; + cmsPipeline* Src = *Lut; + cmsPipeline* Dest = NULL; + cmsStage* mpe; + cmsStage* ObtainedCurves = NULL; + + + // This is a loosy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + // Only curves in this LUT? + for (mpe = cmsPipelineGetPtrToFirstStage(Src); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (Dest == NULL) return FALSE; + + // Create target curves + GammaTables = (cmsToneCurve**) _cmsCalloc(Src ->ContextID, Src ->InputChannels, sizeof(cmsToneCurve*)); + if (GammaTables == NULL) goto Error; + + for (i=0; i < Src ->InputChannels; i++) { + GammaTables[i] = cmsBuildTabulatedToneCurve16(Src ->ContextID, PRELINEARIZATION_POINTS, NULL); + if (GammaTables[i] == NULL) goto Error; + } + + // Compute 16 bit result by using floating point + for (i=0; i < PRELINEARIZATION_POINTS; i++) { + + for (j=0; j < Src ->InputChannels; j++) + InFloat[j] = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); + + cmsPipelineEvalFloat(InFloat, OutFloat, Src); + + for (j=0; j < Src ->InputChannels; j++) + GammaTables[j] -> Table16[i] = _cmsQuickSaturateWord(OutFloat[j] * 65535.0); + } + + ObtainedCurves = cmsStageAllocToneCurves(Src ->ContextID, Src ->InputChannels, GammaTables); + if (ObtainedCurves == NULL) goto Error; + + for (i=0; i < Src ->InputChannels; i++) { + cmsFreeToneCurve(GammaTables[i]); + GammaTables[i] = NULL; + } + + if (GammaTables != NULL) { + _cmsFree(Src->ContextID, GammaTables); + GammaTables = NULL; + } + + // Maybe the curves are linear at the end + if (!AllCurvesAreLinear(ObtainedCurves)) { + + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves)) + goto Error; + + // If the curves are to be applied in 8 bits, we can save memory + if (_cmsFormatterIs8bit(*InputFormat)) { + + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) ObtainedCurves ->Data; + Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves); + + if (c16 == NULL) goto Error; + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup); + + } + else { + + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves); + Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves); + + if (c16 == NULL) goto Error; + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup); + } + } + else { + + // LUT optimizes to nothing. Set the identity LUT + cmsStageFree(ObtainedCurves); + ObtainedCurves = NULL; + + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels))) + goto Error; + + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL); + } + + // We are done. + cmsPipelineFree(Src); + *Lut = Dest; + return TRUE; + +Error: + + if (ObtainedCurves != NULL) cmsStageFree(ObtainedCurves); + if (GammaTables != NULL) { + for (i=0; i < Src ->InputChannels; i++) { + if (GammaTables[i] != NULL) cmsFreeToneCurve(GammaTables[i]); + } + + _cmsFree(Src ->ContextID, GammaTables); + } + + if (Dest != NULL) cmsPipelineFree(Dest); + return FALSE; + + cmsUNUSED_PARAMETER(Intent); + cmsUNUSED_PARAMETER(InputFormat); + cmsUNUSED_PARAMETER(OutputFormat); + cmsUNUSED_PARAMETER(dwFlags); +} + +// ------------------------------------------------------------------------------------------------------------------------------------- +// LUT is Shaper - Matrix - Matrix - Shaper, which is very frequent when combining two matrix-shaper profiles + + +static +void FreeMatShaper(cmsContext ContextID, void* Data) +{ + if (Data != NULL) _cmsFree(ContextID, Data); +} + +static +void* DupMatShaper(cmsContext ContextID, const void* Data) +{ + return _cmsDupMem(ContextID, Data, sizeof(MatShaper8Data)); +} + + +// A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point +// to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits, +// in total about 50K, and the performance boost is huge! +static +void MatShaperEval16(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* D) +{ + MatShaper8Data* p = (MatShaper8Data*) D; + cmsS1Fixed14Number l1, l2, l3, r, g, b; + cmsUInt32Number ri, gi, bi; + + // In this case (and only in this case!) we can use this simplification since + // In[] is assured to come from a 8 bit number. (a << 8 | a) + ri = In[0] & 0xFFU; + gi = In[1] & 0xFFU; + bi = In[2] & 0xFFU; + + // Across first shaper, which also converts to 1.14 fixed point + r = p->Shaper1R[ri]; + g = p->Shaper1G[gi]; + b = p->Shaper1B[bi]; + + // Evaluate the matrix in 1.14 fixed point + l1 = (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14; + l2 = (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14; + l3 = (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14; + + // Now we have to clip to 0..1.0 range + ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384U : (cmsUInt32Number) l1); + gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384U : (cmsUInt32Number) l2); + bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384U : (cmsUInt32Number) l3); + + // And across second shaper, + Out[0] = p->Shaper2R[ri]; + Out[1] = p->Shaper2G[gi]; + Out[2] = p->Shaper2B[bi]; + +} + +// This table converts from 8 bits to 1.14 after applying the curve +static +void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) +{ + int i; + cmsFloat32Number R, y; + + for (i=0; i < 256; i++) { + + R = (cmsFloat32Number) (i / 255.0); + y = cmsEvalToneCurveFloat(Curve, R); + + if (y < 131072.0) + Table[i] = DOUBLE_TO_1FIXED14(y); + else + Table[i] = 0x7fffffff; + } +} + +// This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve +static +void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) +{ + int i; + cmsFloat32Number R, Val; + + for (i=0; i < 16385; i++) { + + R = (cmsFloat32Number) (i / 16384.0); + Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0 + + if (Val < 0) + Val = 0; + + if (Val > 1.0) + Val = 1.0; + + if (Is8BitsOutput) { + + // If 8 bits output, we can optimize further by computing the / 257 part. + // first we compute the resulting byte and then we store the byte times + // 257. This quantization allows to round very quick by doing a >> 8, but + // since the low byte is always equal to msb, we can do a & 0xff and this works! + cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0); + cmsUInt8Number b = FROM_16_TO_8(w); + + Table[i] = FROM_8_TO_16(b); + } + else Table[i] = _cmsQuickSaturateWord(Val * 65535.0); + } +} + +// Compute the matrix-shaper structure +static +cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, cmsVEC3* Off, cmsToneCurve* Curve2[3], cmsUInt32Number* OutputFormat) +{ + MatShaper8Data* p; + int i, j; + cmsBool Is8Bits = _cmsFormatterIs8bit(*OutputFormat); + + // Allocate a big chuck of memory to store precomputed tables + p = (MatShaper8Data*) _cmsMalloc(Dest ->ContextID, sizeof(MatShaper8Data)); + if (p == NULL) return FALSE; + + p -> ContextID = Dest -> ContextID; + + // Precompute tables + FillFirstShaper(p ->Shaper1R, Curve1[0]); + FillFirstShaper(p ->Shaper1G, Curve1[1]); + FillFirstShaper(p ->Shaper1B, Curve1[2]); + + FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits); + FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits); + FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits); + + // Convert matrix to nFixed14. Note that those values may take more than 16 bits + for (i=0; i < 3; i++) { + for (j=0; j < 3; j++) { + p ->Mat[i][j] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]); + } + } + + for (i=0; i < 3; i++) { + + if (Off == NULL) { + p ->Off[i] = 0; + } + else { + p ->Off[i] = DOUBLE_TO_1FIXED14(Off->n[i]); + } + } + + // Mark as optimized for faster formatter + if (Is8Bits) + *OutputFormat |= OPTIMIZED_SH(1); + + // Fill function pointers + _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper); + return TRUE; +} + +// 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast! +static +cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsStage* Curve1, *Curve2; + cmsStage* Matrix1, *Matrix2; + cmsMAT3 res; + cmsBool IdentityMat; + cmsPipeline* Dest, *Src; + cmsFloat64Number* Offset; + + // Only works on RGB to RGB + if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE; + + // Only works on 8 bit input + if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE; + + // Seems suitable, proceed + Src = *Lut; + + // Check for: + // + // shaper-matrix-matrix-shaper + // shaper-matrix-shaper + // + // Both of those constructs are possible (first because abs. colorimetric). + // additionally, In the first case, the input matrix offset should be zero. + + IdentityMat = FALSE; + if (cmsPipelineCheckAndRetreiveStages(Src, 4, + cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + &Curve1, &Matrix1, &Matrix2, &Curve2)) { + + // Get both matrices + _cmsStageMatrixData* Data1 = (_cmsStageMatrixData*)cmsStageData(Matrix1); + _cmsStageMatrixData* Data2 = (_cmsStageMatrixData*)cmsStageData(Matrix2); + + // Input offset should be zero + if (Data1->Offset != NULL) return FALSE; + + // Multiply both matrices to get the result + _cmsMAT3per(&res, (cmsMAT3*)Data2->Double, (cmsMAT3*)Data1->Double); + + // Only 2nd matrix has offset, or it is zero + Offset = Data2->Offset; + + // Now the result is in res + Data2 -> Offset. Maybe is a plain identity? + if (_cmsMAT3isIdentity(&res) && Offset == NULL) { + + // We can get rid of full matrix + IdentityMat = TRUE; + } + + } + else { + + if (cmsPipelineCheckAndRetreiveStages(Src, 3, + cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + &Curve1, &Matrix1, &Curve2)) { + + _cmsStageMatrixData* Data = (_cmsStageMatrixData*)cmsStageData(Matrix1); + + // Copy the matrix to our result + memcpy(&res, Data->Double, sizeof(res)); + + // Preserve the Odffset (may be NULL as a zero offset) + Offset = Data->Offset; + + if (_cmsMAT3isIdentity(&res) && Offset == NULL) { + + // We can get rid of full matrix + IdentityMat = TRUE; + } + } + else + return FALSE; // Not optimizeable this time + + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (!Dest) return FALSE; + + // Assamble the new LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1))) + goto Error; + + if (!IdentityMat) { + + if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest->ContextID, 3, 3, (const cmsFloat64Number*)&res, Offset))) + goto Error; + } + + if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2))) + goto Error; + + // If identity on matrix, we can further optimize the curves, so call the join curves routine + if (IdentityMat) { + + OptimizeByJoiningCurves(&Dest, Intent, InputFormat, OutputFormat, dwFlags); + } + else { + _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1); + _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2); + + // In this particular optimization, caché does not help as it takes more time to deal with + // the caché that with the pixel handling + *dwFlags |= cmsFLAGS_NOCACHE; + + // Setup the optimizarion routines + SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat); + } + + cmsPipelineFree(Src); + *Lut = Dest; + return TRUE; +Error: + // Leave Src unchanged + cmsPipelineFree(Dest); + return FALSE; +} + + +// ------------------------------------------------------------------------------------------------------------------------------------- +// Optimization plug-ins + +// List of optimizations +typedef struct _cmsOptimizationCollection_st { + + _cmsOPToptimizeFn OptimizePtr; + + struct _cmsOptimizationCollection_st *Next; + +} _cmsOptimizationCollection; + + +// The built-in list. We currently implement 4 types of optimizations. Joining of curves, matrix-shaper, linearization and resampling +static _cmsOptimizationCollection DefaultOptimization[] = { + + { OptimizeByJoiningCurves, &DefaultOptimization[1] }, + { OptimizeMatrixShaper, &DefaultOptimization[2] }, + { OptimizeByComputingLinearization, &DefaultOptimization[3] }, + { OptimizeByResampling, NULL } +}; + +// The linked list head +_cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginOptimizationList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsOptimizationPluginChunkType newHead = { NULL }; + _cmsOptimizationCollection* entry; + _cmsOptimizationCollection* Anterior = NULL; + _cmsOptimizationPluginChunkType* head = (_cmsOptimizationPluginChunkType*) src->chunks[OptimizationPlugin]; + + _cmsAssert(ctx != NULL); + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->OptimizationCollection; + entry != NULL; + entry = entry ->Next) { + + _cmsOptimizationCollection *newEntry = ( _cmsOptimizationCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsOptimizationCollection)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.OptimizationCollection == NULL) + newHead.OptimizationCollection = newEntry; + } + + ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsOptimizationPluginChunkType)); +} + +void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Copy all linked list + DupPluginOptimizationList(ctx, src); + } + else { + static _cmsOptimizationPluginChunkType OptimizationPluginChunkType = { NULL }; + ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx ->MemPool, &OptimizationPluginChunkType, sizeof(_cmsOptimizationPluginChunkType)); + } +} + + +// Register new ways to optimize +cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data; + _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); + _cmsOptimizationCollection* fl; + + if (Data == NULL) { + + ctx->OptimizationCollection = NULL; + return TRUE; + } + + // Optimizer callback is required + if (Plugin ->OptimizePtr == NULL) return FALSE; + + fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection)); + if (fl == NULL) return FALSE; + + // Copy the parameters + fl ->OptimizePtr = Plugin ->OptimizePtr; + + // Keep linked list + fl ->Next = ctx->OptimizationCollection; + + // Set the head + ctx ->OptimizationCollection = fl; + + // All is ok + return TRUE; +} + +// The entry point for LUT optimization +cmsBool _cmsOptimizePipeline(cmsContext ContextID, + cmsPipeline** PtrLut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags) +{ + _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); + _cmsOptimizationCollection* Opts; + cmsBool AnySuccess = FALSE; + + // A CLUT is being asked, so force this specific optimization + if (*dwFlags & cmsFLAGS_FORCE_CLUT) { + + PreOptimize(*PtrLut); + return OptimizeByResampling(PtrLut, Intent, InputFormat, OutputFormat, dwFlags); + } + + // Anything to optimize? + if ((*PtrLut) ->Elements == NULL) { + _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); + return TRUE; + } + + // Try to get rid of identities and trivial conversions. + AnySuccess = PreOptimize(*PtrLut); + + // After removal do we end with an identity? + if ((*PtrLut) ->Elements == NULL) { + _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); + return TRUE; + } + + // Do not optimize, keep all precision + if (*dwFlags & cmsFLAGS_NOOPTIMIZE) + return FALSE; + + // Try plug-in optimizations + for (Opts = ctx->OptimizationCollection; + Opts != NULL; + Opts = Opts ->Next) { + + // If one schema succeeded, we are done + if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { + + return TRUE; // Optimized! + } + } + + // Try built-in optimizations + for (Opts = DefaultOptimization; + Opts != NULL; + Opts = Opts ->Next) { + + if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { + + return TRUE; + } + } + + // Only simple optimizations succeeded + return AnySuccess; +} + + + diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmspack.c --- a/src/share/native/sun/java2d/cmm/lcms/cmspack.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmspack.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,8 +27,10 @@ // However, the following notice accompanied the original version of this // file: // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -47,2129 +49,3414 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "lcms.h" - -// This module handles all formats supported by lcms - +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// This module handles all formats supported by lcms. There are two flavors, 16 bits and +// floating point. Floating point is supported only in a subset, those formats holding +// cmsFloat32Number (4 bytes per component) and double (marked as 0 bytes per component +// as special case) // --------------------------------------------------------------------------- // This macro return words stored as big endian - -#define CHANGE_ENDIAN(w) (WORD) ((WORD) ((w)<<8)|((w)>>8)) +#define CHANGE_ENDIAN(w) (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8)) // These macros handles reversing (negative) - -#define REVERSE_FLAVOR_8(x) ((BYTE) (0xff-(x))) -#define REVERSE_FLAVOR_16(x) ((WORD)(0xffff-(x))) - -// Supress waning about info never being used - -#ifdef __BORLANDC__ -#pragma warn -par -#endif +#define REVERSE_FLAVOR_8(x) ((cmsUInt8Number) (0xff-(x))) +#define REVERSE_FLAVOR_16(x) ((cmsUInt16Number)(0xffff-(x))) + +// * 0xffff / 0xff00 = (255 * 257) / (255 * 256) = 257 / 256 +cmsINLINE cmsUInt16Number FomLabV2ToLabV4(cmsUInt16Number x) +{ + int a = (x << 8 | x) >> 8; // * 257 / 256 + if ( a > 0xffff) return 0xffff; + return (cmsUInt16Number) a; +} + +// * 0xf00 / 0xffff = * 256 / 257 +cmsINLINE cmsUInt16Number FomLabV4ToLabV2(cmsUInt16Number x) +{ + return (cmsUInt16Number) (((x << 8) + 0x80) / 257); +} + + +typedef struct { + cmsUInt32Number Type; + cmsUInt32Number Mask; + cmsFormatter16 Frm; + +} cmsFormatters16; + +typedef struct { + cmsUInt32Number Type; + cmsUInt32Number Mask; + cmsFormatterFloat Frm; + +} cmsFormattersFloat; + + +#define ANYSPACE COLORSPACE_SH(31) +#define ANYCHANNELS CHANNELS_SH(15) +#define ANYEXTRA EXTRA_SH(7) +#define ANYPLANAR PLANAR_SH(1) +#define ANYENDIAN ENDIAN16_SH(1) +#define ANYSWAP DOSWAP_SH(1) +#define ANYSWAPFIRST SWAPFIRST_SH(1) +#define ANYFLAVOR FLAVOR_SH(1) + + +// Suppress waning about info never being used #ifdef _MSC_VER #pragma warning(disable : 4100) #endif -// -------------------------------------------------------- Unpacking routines. +// Unpacking routines (16 bits) ---------------------------------------------------------------------------------------- + + +// Does almost everything but is slow +static +cmsUInt8Number* UnrollChunkyBytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt16Number v; + cmsUInt32Number i; + + if (ExtraFirst) { + accum += Extra; + } + + for (i=0; i < nChan; i++) { + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = FROM_8_TO_16(*accum); + v = Reverse ? REVERSE_FLAVOR_16(v) : v; + wIn[index] = v; + accum++; + } + + if (!ExtraFirst) { + accum += Extra; + } + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); + +} + +// Extra channels are just ignored because come in the next planes +static +cmsUInt8Number* UnrollPlanarBytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number i; + cmsUInt8Number* Init = accum; + + if (DoSwap ^ SwapFirst) { + accum += T_EXTRA(info -> InputFormat) * Stride; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = FROM_8_TO_16(*accum); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + accum += Stride; + } + + return (Init + 1); +} + +// Special cases, provided for performance +static +cmsUInt8Number* Unroll4Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesReverse(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // C + wIn[1] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // M + wIn[2] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // Y + wIn[3] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KYMC +static +cmsUInt8Number* Unroll4BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesSwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // K + wIn[1] = FROM_8_TO_16(*accum); accum++; // Y + wIn[0] = FROM_8_TO_16(*accum); accum++; // M + wIn[3] = FROM_8_TO_16(*accum); accum++; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1Swap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum++; // A + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1SwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + accum++; // A + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum++; // A + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// BRG +static +cmsUInt8Number* Unroll3BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollLabV2_8(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L + wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a + wIn[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollALabV2_8(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum++; // A + wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L + wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a + wIn[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollLabV2_16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // L + wIn[1] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // a + wIn[2] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// for duplex +static +cmsUInt8Number* Unroll2Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // ch1 + wIn[1] = FROM_8_TO_16(*accum); accum++; // ch2 + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + + + +// Monochrome duplicates L into RGB for null-transforms +static +cmsUInt8Number* Unroll1Byte(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Unroll1ByteSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + accum += 1; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1ByteSkip2(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + accum += 2; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1ByteReversed(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(FROM_8_TO_16(*accum)); accum++; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} static -LPBYTE UnrollAnyBytes(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollAnyWords(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number i; + + if (ExtraFirst) { + accum += Extra * sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = *(cmsUInt16Number*) accum; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + + accum += sizeof(cmsUInt16Number); + } + + if (!ExtraFirst) { + accum += Extra * sizeof(cmsUInt16Number); + } + + if (Extra == 0 && SwapFirst) { + + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + return accum; + + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollPlanarWords(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap= T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse= T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat); + cmsUInt32Number i; + cmsUInt8Number* Init = accum; + + if (DoSwap) { + accum += T_EXTRA(info -> InputFormat) * Stride; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = *(cmsUInt16Number*) accum; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + + accum += Stride; + } + + return (Init + sizeof(cmsUInt16Number)); +} + + +static +cmsUInt8Number* Unroll4Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsReverse(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // C + wIn[1] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // M + wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // Y + wIn[3] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KYMC +static +cmsUInt8Number* Unroll4WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsSwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C R + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M G + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // C R + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M G + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // Y B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSkip1Swap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum += 2; // A + wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // R + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // G + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum += 2; // A + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // R + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // G + wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1Word(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1WordReversed(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1WordSkip3(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) { - int nChan = T_CHANNELS(info -> InputFormat); - register int i; - - for (i=0; i < nChan; i++) { - - wIn[i] = RGB_8_TO_16(*accum); accum++; - } - - return accum + T_EXTRA(info -> InputFormat); + wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; + + accum += 8; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll2Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // ch1 + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // ch2 + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// This is a conversion of Lab double to 16 bits +static +cmsUInt8Number* UnrollLabDoubleTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsCIELab Lab; + cmsUInt8Number* pos_L; + cmsUInt8Number* pos_a; + cmsUInt8Number* pos_b; + + pos_L = accum; + pos_a = accum + Stride; + pos_b = accum + Stride * 2; + + Lab.L = *(cmsFloat64Number*) pos_L; + Lab.a = *(cmsFloat64Number*) pos_a; + Lab.b = *(cmsFloat64Number*) pos_b; + + cmsFloat2LabEncoded(wIn, &Lab); + return accum + sizeof(cmsFloat64Number); + } + else { + + cmsFloat2LabEncoded(wIn, (cmsCIELab*) accum); + accum += sizeof(cmsCIELab) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat64Number); + return accum; + } +} + + +// This is a conversion of Lab float to 16 bits +static +cmsUInt8Number* UnrollLabFloatTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + cmsCIELab Lab; + + if (T_PLANAR(info -> InputFormat)) { + + cmsUInt8Number* pos_L; + cmsUInt8Number* pos_a; + cmsUInt8Number* pos_b; + + pos_L = accum; + pos_a = accum + Stride; + pos_b = accum + Stride * 2; + + Lab.L = *(cmsFloat32Number*)pos_L; + Lab.a = *(cmsFloat32Number*)pos_a; + Lab.b = *(cmsFloat32Number*)pos_b; + + cmsFloat2LabEncoded(wIn, &Lab); + return accum + sizeof(cmsFloat32Number); + } + else { + + Lab.L = ((cmsFloat32Number*) accum)[0]; + Lab.a = ((cmsFloat32Number*) accum)[1]; + Lab.b = ((cmsFloat32Number*) accum)[2]; + + cmsFloat2LabEncoded(wIn, &Lab); + accum += (3 + T_EXTRA(info ->InputFormat)) * sizeof(cmsFloat32Number); + return accum; + } +} + +// This is a conversion of XYZ double to 16 bits +static +cmsUInt8Number* UnrollXYZDoubleTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsCIEXYZ XYZ; + cmsUInt8Number* pos_X; + cmsUInt8Number* pos_Y; + cmsUInt8Number* pos_Z; + + pos_X = accum; + pos_Y = accum + Stride; + pos_Z = accum + Stride * 2; + + XYZ.X = *(cmsFloat64Number*)pos_X; + XYZ.Y = *(cmsFloat64Number*)pos_Y; + XYZ.Z = *(cmsFloat64Number*)pos_Z; + + cmsFloat2XYZEncoded(wIn, &XYZ); + + return accum + sizeof(cmsFloat64Number); + + } + + else { + cmsFloat2XYZEncoded(wIn, (cmsCIEXYZ*) accum); + accum += sizeof(cmsCIEXYZ) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat64Number); + + return accum; + } +} + +// This is a conversion of XYZ float to 16 bits +static +cmsUInt8Number* UnrollXYZFloatTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsCIEXYZ XYZ; + cmsUInt8Number* pos_X; + cmsUInt8Number* pos_Y; + cmsUInt8Number* pos_Z; + + pos_X = accum; + pos_Y = accum + Stride; + pos_Z = accum + Stride * 2; + + XYZ.X = *(cmsFloat32Number*)pos_X; + XYZ.Y = *(cmsFloat32Number*)pos_Y; + XYZ.Z = *(cmsFloat32Number*)pos_Z; + + cmsFloat2XYZEncoded(wIn, &XYZ); + + return accum + sizeof(cmsFloat32Number); + + } + + else { + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + cmsCIEXYZ XYZ; + + XYZ.X = Pt[0]; + XYZ.Y = Pt[1]; + XYZ.Z = Pt[2]; + cmsFloat2XYZEncoded(wIn, &XYZ); + + accum += 3 * sizeof(cmsFloat32Number) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat32Number); + + return accum; + } +} + +// Check if space is marked as ink +cmsINLINE cmsBool IsInkSpace(cmsUInt32Number Type) +{ + switch (T_COLORSPACE(Type)) { + + case PT_CMY: + case PT_CMYK: + case PT_MCH5: + case PT_MCH6: + case PT_MCH7: + case PT_MCH8: + case PT_MCH9: + case PT_MCH10: + case PT_MCH11: + case PT_MCH12: + case PT_MCH13: + case PT_MCH14: + case PT_MCH15: return TRUE; + + default: return FALSE; + } +} + +// Return the size in bytes of a given formatter +static +cmsUInt32Number PixelSize(cmsUInt32Number Format) +{ + cmsUInt32Number fmt_bytes = T_BYTES(Format); + + // For double, the T_BYTES field is zero + if (fmt_bytes == 0) + return sizeof(cmsUInt64Number); + + // Otherwise, it is already correct for all formats + return fmt_bytes; +} + +// Inks does come in percentage, remaining cases are between 0..1.0, again to 16 bits +static +cmsUInt8Number* UnrollDoubleTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat64Number v; + cmsUInt16Number vi; + cmsUInt32Number i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0; + + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[i + start]; + + vi = _cmsQuickSaturateWord(v * maximum); + + if (Reverse) + vi = REVERSE_FLAVOR_16(vi); + + wIn[index] = vi; + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat64Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat64Number); } static -LPBYTE Unroll4Bytes(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = RGB_8_TO_16(*accum); accum++; // C - wIn[1] = RGB_8_TO_16(*accum); accum++; // M - wIn[2] = RGB_8_TO_16(*accum); accum++; // Y - wIn[3] = RGB_8_TO_16(*accum); accum++; // K - - return accum; -} - -static -LPBYTE Unroll4BytesReverse(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollFloatTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) { - wIn[0] = RGB_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // C - wIn[1] = RGB_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // M - wIn[2] = RGB_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // Y - wIn[3] = RGB_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // K - - return accum; -} - - -static -LPBYTE Unroll4BytesSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - - wIn[3] = RGB_8_TO_16(*accum); accum++; // K - wIn[0] = RGB_8_TO_16(*accum); accum++; // C - wIn[1] = RGB_8_TO_16(*accum); accum++; // M - wIn[2] = RGB_8_TO_16(*accum); accum++; // Y - - - return accum; + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt16Number vi; + cmsUInt32Number i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0; + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; + + vi = _cmsQuickSaturateWord(v * maximum); + + if (Reverse) + vi = REVERSE_FLAVOR_16(vi); + + wIn[index] = vi; + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat32Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat32Number); } -// KYMC + +// For 1 channel, we need to duplicate data (it comes in 0..1.0 range) static -LPBYTE Unroll4BytesSwap(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollDouble1Chan(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) { - wIn[3] = RGB_8_TO_16(*accum); accum++; // K - wIn[2] = RGB_8_TO_16(*accum); accum++; // Y - wIn[1] = RGB_8_TO_16(*accum); accum++; // M - wIn[0] = RGB_8_TO_16(*accum); accum++; // C - - return accum; + cmsFloat64Number* Inks = (cmsFloat64Number*) accum; + + wIn[0] = wIn[1] = wIn[2] = _cmsQuickSaturateWord(Inks[0] * 65535.0); + + return accum + sizeof(cmsFloat64Number); + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); } - +//------------------------------------------------------------------------------------------------------------------- + +// For anything going from cmsFloat32Number static -LPBYTE Unroll4BytesSwapSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) { - wIn[2] = RGB_8_TO_16(*accum); accum++; // K - wIn[1] = RGB_8_TO_16(*accum); accum++; // Y - wIn[0] = RGB_8_TO_16(*accum); accum++; // M - wIn[3] = RGB_8_TO_16(*accum); accum++; // C - - return accum; + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt32Number i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; + + v /= maximum; + + wIn[index] = Reverse ? 1 - v : v; + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat32Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat32Number); } - -static -LPBYTE UnrollAnyWords(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - int nChan = T_CHANNELS(info -> InputFormat); - register int i; - - for (i=0; i < nChan; i++) { - - wIn[i] = *(LPWORD) accum; accum += 2; - } - - return accum + T_EXTRA(info -> InputFormat) * sizeof(WORD); -} - +// For anything going from double static -LPBYTE Unroll4Words(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = *(LPWORD) accum; accum+= 2; // C - wIn[1] = *(LPWORD) accum; accum+= 2; // M - wIn[2] = *(LPWORD) accum; accum+= 2; // Y - wIn[3] = *(LPWORD) accum; accum+= 2; // K - - return accum; -} - -static -LPBYTE Unroll4WordsReverse(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollDoublesToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) { - wIn[0] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; // C - wIn[1] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; // M - wIn[2] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; // Y - wIn[3] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; // K - - return accum; -} - - -static -LPBYTE Unroll4WordsSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[3] = *(LPWORD) accum; accum+= 2; // K - wIn[0] = *(LPWORD) accum; accum+= 2; // C - wIn[1] = *(LPWORD) accum; accum+= 2; // M - wIn[2] = *(LPWORD) accum; accum+= 2; // Y - - return accum; -} - - -// KYMC -static -LPBYTE Unroll4WordsSwap(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[3] = *(LPWORD) accum; accum+= 2; // K - wIn[2] = *(LPWORD) accum; accum+= 2; // Y - wIn[1] = *(LPWORD) accum; accum+= 2; // M - wIn[0] = *(LPWORD) accum; accum+= 2; // C - - return accum; + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat64Number v; + cmsUInt32Number i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 100.0 : 1.0; + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[i + start]; + + v /= maximum; + + wIn[index] = (cmsFloat32Number) (Reverse ? 1.0 - v : v); + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat64Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat64Number); } + + +// From Lab double to cmsFloat32Number static -LPBYTE Unroll4WordsSwapSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollLabDoubleToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) { - wIn[2] = *(LPWORD) accum; accum+= 2; // K - wIn[1] = *(LPWORD) accum; accum+= 2; // Y - wIn[0] = *(LPWORD) accum; accum+= 2; // M - wIn[3] = *(LPWORD) accum; accum+= 2; // C - - return accum; -} - - -static -LPBYTE Unroll4WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //C - wIn[1] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //M - wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //Y - wIn[3] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //K - - return accum; + cmsFloat64Number* Pt = (cmsFloat64Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + Stride /= PixelSize(info->InputFormat); + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0); + + return accum + sizeof(cmsFloat64Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[1] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[2] + 128) / 255.0); + + accum += sizeof(cmsFloat64Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } } +// From Lab double to cmsFloat32Number static -LPBYTE Unroll4WordsBigEndianReverse(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollLabFloatToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) { - wIn[0] = REVERSE_FLAVOR_16(CHANGE_ENDIAN(*(LPWORD) accum)); accum+= 2; //C - wIn[1] = REVERSE_FLAVOR_16(CHANGE_ENDIAN(*(LPWORD) accum)); accum+= 2; //M - wIn[2] = REVERSE_FLAVOR_16(CHANGE_ENDIAN(*(LPWORD) accum)); accum+= 2; //Y - wIn[3] = REVERSE_FLAVOR_16(CHANGE_ENDIAN(*(LPWORD) accum)); accum+= 2; //K - - return accum; + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + Stride /= PixelSize(info->InputFormat); + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0); + + return accum + sizeof(cmsFloat32Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[1] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[2] + 128) / 255.0); + + accum += sizeof(cmsFloat32Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } } -// KYMC + +// 1.15 fixed point, that means maximum value is MAX_ENCODEABLE_XYZ (0xFFFF) static -LPBYTE Unroll4WordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollXYZDoubleToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) { - wIn[3] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //K - wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //Y - wIn[1] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //M - wIn[0] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //C - - return accum; + cmsFloat64Number* Pt = (cmsFloat64Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + Stride /= PixelSize(info->InputFormat); + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ); + + return accum + sizeof(cmsFloat64Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[1] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[2] / MAX_ENCODEABLE_XYZ); + + accum += sizeof(cmsFloat64Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } } static -LPBYTE Unroll3Bytes(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - - wIn[0] = RGB_8_TO_16(*accum); accum++; // R - wIn[1] = RGB_8_TO_16(*accum); accum++; // G - wIn[2] = RGB_8_TO_16(*accum); accum++; // B - - return accum; -} - - -// Lab8 encoding using v2 PCS - -static -LPBYTE Unroll3BytesLab(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* UnrollXYZFloatToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) { - - wIn[0] = (WORD) ((*accum) << 8); accum++; - wIn[1] = (WORD) ((*accum) << 8); accum++; - wIn[2] = (WORD) ((*accum) << 8); accum++; - - return accum; -} - - -// BRG - -static -LPBYTE Unroll3BytesSwap(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - - wIn[2] = RGB_8_TO_16(*accum); accum++; // B - wIn[1] = RGB_8_TO_16(*accum); accum++; // G - wIn[0] = RGB_8_TO_16(*accum); accum++; // R - - return accum; -} - -static -LPBYTE Unroll3Words(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = *(LPWORD) accum; accum+= 2; // C R - wIn[1] = *(LPWORD) accum; accum+= 2; // M G - wIn[2] = *(LPWORD) accum; accum+= 2; // Y B - return accum; + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + Stride /= PixelSize(info->InputFormat); + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ); + + return accum + sizeof(cmsFloat32Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[1] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[2] / MAX_ENCODEABLE_XYZ); + + accum += sizeof(cmsFloat32Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } } -static -LPBYTE Unroll3WordsSwap(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[2] = *(LPWORD) accum; accum+= 2; // C R - wIn[1] = *(LPWORD) accum; accum+= 2; // M G - wIn[0] = *(LPWORD) accum; accum+= 2; // Y B - return accum; -} - - -static -LPBYTE Unroll3WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - wIn[1] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - return accum; -} - - -static -LPBYTE Unroll3WordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - wIn[1] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - wIn[0] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - return accum; -} - - - -// Monochrome duplicates L into RGB for null-transforms - -static -LPBYTE Unroll1Byte(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = RGB_8_TO_16(*accum); accum++; // L - return accum; -} - - -static -LPBYTE Unroll1ByteSkip2(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = RGB_8_TO_16(*accum); accum++; // L - accum += 2; - return accum; -} + +// Packing routines ----------------------------------------------------------------------------------------------------------- + + +// Generic chunky for byte static -LPBYTE Unroll1ByteReversed(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(RGB_8_TO_16(*accum)); accum++; // L - return accum; -} - - -static -LPBYTE Unroll1Word(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = *(LPWORD) accum; accum+= 2; // L - return accum; -} - -static -LPBYTE Unroll1WordReversed(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; - return accum; -} - - -static -LPBYTE Unroll1WordBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - return accum; -} - -static -LPBYTE Unroll1WordSkip3(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = *(LPWORD) accum; - - accum += 8; - return accum; -} - - -// Monochrome + alpha. Alpha is lost - -static -LPBYTE Unroll2Byte(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = RGB_8_TO_16(*accum); accum++; // L - wIn[3] = RGB_8_TO_16(*accum); accum++; // alpha - return accum; -} - -static -LPBYTE Unroll2ByteSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* PackAnyBytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) { - wIn[3] = RGB_8_TO_16(*accum); accum++; // alpha - wIn[0] = wIn[1] = wIn[2] = RGB_8_TO_16(*accum); accum++; // L - return accum; -} - - -static -LPBYTE Unroll2Word(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = *(LPWORD) accum; accum+= 2; // L - wIn[3] = *(LPWORD) accum; accum += 2; // alpha - - return accum; -} - - -static -LPBYTE Unroll2WordSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[3] = *(LPWORD) accum; accum += 2; // alpha - wIn[0] = wIn[1] = wIn[2] = *(LPWORD) accum; accum+= 2; // L - - return accum; -} - -static -LPBYTE Unroll2WordBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - wIn[0] = wIn[1] = wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - wIn[3] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; - - return accum; -} - - - - -static -LPBYTE UnrollPlanarBytes(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - int nChan = T_CHANNELS(info -> InputFormat); - register int i; - LPBYTE Init = accum; - - for (i=0; i < nChan; i++) { - - wIn[i] = RGB_8_TO_16(*accum); - accum += info -> StrideIn; - } - - return (Init + 1); + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt8Number* swap1; + cmsUInt8Number v = 0; + cmsUInt32Number i; + + swap1 = output; + + if (ExtraFirst) { + output += Extra; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = FROM_16_TO_8(wOut[index]); + + if (Reverse) + v = REVERSE_FLAVOR_8(v); + + *output++ = v; + } + + if (!ExtraFirst) { + output += Extra; + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, nChan-1); + *swap1 = v; + } + + + return output; + + cmsUNUSED_PARAMETER(Stride); } static -LPBYTE UnrollPlanarWords(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* PackAnyWords(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt16Number* swap1; + cmsUInt16Number v = 0; + cmsUInt32Number i; + + swap1 = (cmsUInt16Number*) output; + + if (ExtraFirst) { + output += Extra * sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index]; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + if (Reverse) + v = REVERSE_FLAVOR_16(v); + + *(cmsUInt16Number*) output = v; + + output += sizeof(cmsUInt16Number); + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsUInt16Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsUInt16Number)); + *swap1 = v; + } + + + return output; + + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackPlanarBytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number i; + cmsUInt8Number* Init = output; + + + if (DoSwap ^ SwapFirst) { + output += T_EXTRA(info -> OutputFormat) * Stride; + } + + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt8Number v = FROM_16_TO_8(wOut[index]); + + *(cmsUInt8Number*) output = (cmsUInt8Number) (Reverse ? REVERSE_FLAVOR_8(v) : v); + output += Stride; + } + + return (Init + 1); + + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackPlanarWords(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat); + cmsUInt32Number i; + cmsUInt8Number* Init = output; + cmsUInt16Number v; + + if (DoSwap) { + output += T_EXTRA(info -> OutputFormat) * Stride; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index]; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + if (Reverse) + v = REVERSE_FLAVOR_16(v); + + *(cmsUInt16Number*) output = v; + output += Stride; + } + + return (Init + sizeof(cmsUInt16Number)); +} + +// CMYKcm (unrolled for speed) + +static +cmsUInt8Number* Pack6Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[4]); + *output++ = FROM_16_TO_8(wOut[5]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KCMYcm + +static +cmsUInt8Number* Pack6BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[5]); + *output++ = FROM_16_TO_8(wOut[4]); + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// CMYKcm +static +cmsUInt8Number* Pack6Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[4]; + output+= 2; + *(cmsUInt16Number*) output = wOut[5]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KCMYcm +static +cmsUInt8Number* Pack6WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[5]; + output+= 2; + *(cmsUInt16Number*) output = wOut[4]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack4Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[3]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4BytesReverse(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[0])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[1])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[2])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[3])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack4BytesSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// ABGR +static +cmsUInt8Number* Pack4BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4BytesSwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[3]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4WordsReverse(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[2]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[3]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// ABGR +static +cmsUInt8Number* Pack4WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// CMYK +static +cmsUInt8Number* Pack4WordsBigEndian(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) { - int nChan = T_CHANNELS(info -> InputFormat); - register int i; - LPBYTE Init = accum; - - for (i=0; i < nChan; i++) { - - wIn[i] = *(LPWORD) accum; - accum += (info -> StrideIn * sizeof(WORD)); - } - - return (Init + sizeof(WORD)); + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[2]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[3]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackLabV2_8(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[2])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* PackALabV2_8(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[2])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* PackLabV2_16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[0]); + output += 2; + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[1]); + output += 2; + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[2]); + output += 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = (wOut[0] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[2] & 0xFFU); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesSwapOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = (wOut[2] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[0] & 0xFFU); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsBigEndian(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[2]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1Optimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = (wOut[0] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[2] & 0xFFU); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapFirstOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = (wOut[0] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[2] & 0xFFU); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1Swap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = (wOut[2] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[0] & 0xFFU); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirstOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = (wOut[2] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[0] & 0xFFU); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsAndSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsAndSkip1Swap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3WordsAndSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3WordsAndSkip1SwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); } static -LPBYTE UnrollPlanarWordsBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* Pack1Byte(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1ByteReversed(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(REVERSE_FLAVOR_16(wOut[0])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1ByteSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) { - int nChan = T_CHANNELS(info -> InputFormat); - register int i; - LPBYTE Init = accum; - - for (i=0; i < nChan; i++) { - - wIn[i] = CHANGE_ENDIAN(*(LPWORD) accum); - accum += (info -> StrideIn * sizeof(WORD)); - } - - return (Init + sizeof(WORD)); + *output++ = FROM_16_TO_8(wOut[0]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); } -// floating point +static +cmsUInt8Number* Pack1ByteSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1Word(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + static -LPBYTE UnrollLabDouble(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* Pack1WordReversed(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1WordBigEndian(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1WordSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) { - - if (T_PLANAR(info -> InputFormat)) { - - double* Pt = (double*) accum; - - cmsCIELab Lab; - - Lab.L = Pt[0]; - Lab.a = Pt[info->StrideIn]; - Lab.b = Pt[info->StrideIn*2]; - - if (info ->lInputV4Lab) - cmsFloat2LabEncoded4(wIn, &Lab); - else - cmsFloat2LabEncoded(wIn, &Lab); - - return accum + sizeof(double); + *(cmsUInt16Number*) output = wOut[0]; + output+= 4; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1WordSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output += 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// Unencoded Float values -- don't try optimize speed +static +cmsUInt8Number* PackLabDoubleFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + + if (T_PLANAR(info -> OutputFormat)) { + + cmsCIELab Lab; + cmsFloat64Number* Out = (cmsFloat64Number*) output; + cmsLabEncoded2Float(&Lab, wOut); + + Out[0] = Lab.L; + Out[Stride] = Lab.a; + Out[Stride*2] = Lab.b; + + return output + sizeof(cmsFloat64Number); } else { - if (info ->lInputV4Lab) - cmsFloat2LabEncoded4(wIn, (LPcmsCIELab) accum); - else - cmsFloat2LabEncoded(wIn, (LPcmsCIELab) accum); - - accum += sizeof(cmsCIELab); - - return accum; + cmsLabEncoded2Float((cmsCIELab*) output, wOut); + return output + (sizeof(cmsCIELab) + T_EXTRA(info ->OutputFormat) * sizeof(cmsFloat64Number)); + } +} + + +static +cmsUInt8Number* PackLabFloatFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + cmsCIELab Lab; + cmsLabEncoded2Float(&Lab, wOut); + + if (T_PLANAR(info -> OutputFormat)) { + + cmsFloat32Number* Out = (cmsFloat32Number*) output; + + Stride /= PixelSize(info->OutputFormat); + + Out[0] = (cmsFloat32Number)Lab.L; + Out[Stride] = (cmsFloat32Number)Lab.a; + Out[Stride*2] = (cmsFloat32Number)Lab.b; + + return output + sizeof(cmsFloat32Number); + } + else { + + ((cmsFloat32Number*) output)[0] = (cmsFloat32Number) Lab.L; + ((cmsFloat32Number*) output)[1] = (cmsFloat32Number) Lab.a; + ((cmsFloat32Number*) output)[2] = (cmsFloat32Number) Lab.b; + + return output + (3 + T_EXTRA(info ->OutputFormat)) * sizeof(cmsFloat32Number); } } static -LPBYTE UnrollXYZDouble(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* PackXYZDoubleFrom16(register _cmsTRANSFORM* Info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) { - if (T_PLANAR(info -> InputFormat)) { - - double* Pt = (double*) accum; + if (T_PLANAR(Info -> OutputFormat)) { + cmsCIEXYZ XYZ; - - XYZ.X = Pt[0]; - XYZ.Y = Pt[info->StrideIn]; - XYZ.Z = Pt[info->StrideIn*2]; - cmsFloat2XYZEncoded(wIn, &XYZ); - - return accum + sizeof(double); + cmsFloat64Number* Out = (cmsFloat64Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = XYZ.X; + Out[Stride] = XYZ.Y; + Out[Stride*2] = XYZ.Z; + + return output + sizeof(cmsFloat64Number); } - else { - - cmsFloat2XYZEncoded(wIn, (LPcmsCIEXYZ) accum); - accum += sizeof(cmsCIEXYZ); - - return accum; + cmsXYZEncoded2Float((cmsCIEXYZ*) output, wOut); + + return output + (sizeof(cmsCIEXYZ) + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); } } - - -// Inks does come in percentage -static -LPBYTE UnrollInkDouble(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - double* Inks = (double*) accum; - int nChan = T_CHANNELS(info -> InputFormat); - int Planar = T_PLANAR(info -> InputFormat); - int i; - double v; - - for (i=0; i < nChan; i++) { - - if (Planar) - - v = Inks[i * info ->StrideIn]; - else - v = Inks[i]; - - v = floor(v * 655.35 + 0.5); - - if (v > 65535.0) v = 65535.0; - if (v < 0) v = 0; - - wIn[i] = (WORD) v; - } - - if (T_PLANAR(info -> InputFormat)) - return accum + sizeof(double); - else - return accum + (nChan + T_EXTRA(info ->InputFormat)) * sizeof(double); -} - - -// Remaining cases are between 0..1.0 -static -LPBYTE UnrollDouble(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) -{ - double* Inks = (double*) accum; - int nChan = T_CHANNELS(info -> InputFormat); - int Planar = T_PLANAR(info -> InputFormat); - int i; - double v; - - for (i=0; i < nChan; i++) { - - if (Planar) - - v = Inks[i * info ->StrideIn]; - else - v = Inks[i]; - - v = floor(v * 65535.0 + 0.5); - - if (v > 65535.0) v = 65535.0; - if (v < 0) v = 0; - - wIn[i] = (WORD) v; - } - - if (T_PLANAR(info -> InputFormat)) - return accum + sizeof(double); - else - return accum + (nChan + T_EXTRA(info ->InputFormat)) * sizeof(double); -} - - - static -LPBYTE UnrollDouble1Chan(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum) +cmsUInt8Number* PackXYZFloatFrom16(register _cmsTRANSFORM* Info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) { - double* Inks = (double*) accum; - double v; - - - v = floor(Inks[0] * 65535.0 + 0.5); - - if (v > 65535.0) v = 65535.0; - if (v < 0) v = 0; - - - wIn[0] = wIn[1] = wIn[2] = (WORD) v; - - return accum + sizeof(double); + if (T_PLANAR(Info -> OutputFormat)) { + + cmsCIEXYZ XYZ; + cmsFloat32Number* Out = (cmsFloat32Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat32Number) XYZ.X; + Out[Stride] = (cmsFloat32Number) XYZ.Y; + Out[Stride*2] = (cmsFloat32Number) XYZ.Z; + + return output + sizeof(cmsFloat32Number); + + } + else { + + cmsCIEXYZ XYZ; + cmsFloat32Number* Out = (cmsFloat32Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + Out[0] = (cmsFloat32Number) XYZ.X; + Out[1] = (cmsFloat32Number) XYZ.Y; + Out[2] = (cmsFloat32Number) XYZ.Z; + + return output + (3 * sizeof(cmsFloat32Number) + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); + } } - -// ----------------------------------------------------------- Packing routines - - -// Generic N-bytes plus dither 16-to-8 conversion. Currently is just a quick hack - -static int err[MAXCHANNELS]; - -static -LPBYTE PackNBytesDither(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - unsigned int n, pe, pf; - - for (i=0; i < nChan; i++) { - - n = wOut[i] + err[i]; // Value - - pe = (n / 257); // Whole part - pf = (n % 257); // Fractional part - - err[i] = pf; // Store it for next pixel - - *output++ = (BYTE) pe; - } - - return output + T_EXTRA(info ->OutputFormat); -} - - - static -LPBYTE PackNBytesSwapDither(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) +cmsUInt8Number* PackDoubleFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) { - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - unsigned int n, pe, pf; - - for (i=nChan-1; i >= 0; --i) { - - n = wOut[i] + err[i]; // Value - - pe = (n / 257); // Whole part - pf = (n % 257); // Fractional part - - err[i] = pf; // Store it for next pixel - - *output++ = (BYTE) pe; - } - - - return output + T_EXTRA(info ->OutputFormat); -} - - - -// Generic chunky for byte - -static -LPBYTE PackNBytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - - for (i=0; i < nChan; i++) - *output++ = RGB_16_TO_8(wOut[i]); - - return output + T_EXTRA(info ->OutputFormat); -} - -// Chunky reversed order bytes - -static -LPBYTE PackNBytesSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - - for (i=nChan-1; i >= 0; --i) - *output++ = RGB_16_TO_8(wOut[i]); - - return output + T_EXTRA(info ->OutputFormat); + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info -> OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35 : 65535.0; + cmsFloat64Number v = 0; + cmsFloat64Number* swap1 = (cmsFloat64Number*) output; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat64Number) wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat64Number*) output)[(i + start) * Stride]= v; + else + ((cmsFloat64Number*) output)[i + start] = v; + } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat64Number)); + *swap1 = v; + } + + if (T_PLANAR(info -> OutputFormat)) + return output + sizeof(cmsFloat64Number); + else + return output + (nChan + Extra) * sizeof(cmsFloat64Number); } static -LPBYTE PackNWords(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) +cmsUInt8Number* PackFloatFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) { - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - - for (i=0; i < nChan; i++) { - *(LPWORD) output = wOut[i]; - output += sizeof(WORD); + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 655.35 : 65535.0; + cmsFloat64Number v = 0; + cmsFloat32Number* swap1 = (cmsFloat32Number*)output; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat64Number)wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat32Number*)output)[(i + start) * Stride] = (cmsFloat32Number)v; + else + ((cmsFloat32Number*)output)[i + start] = (cmsFloat32Number)v; + } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsFloat32Number)); + *swap1 = (cmsFloat32Number)v; } - return output + T_EXTRA(info ->OutputFormat) * sizeof(WORD); + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsFloat32Number); + else + return output + (nChan + Extra) * sizeof(cmsFloat32Number); +} + + + +// -------------------------------------------------------------------------------------------------------- + +static +cmsUInt8Number* PackFloatsFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 100.0 : 1.0; + cmsFloat32Number* swap1 = (cmsFloat32Number*)output; + cmsFloat64Number v = 0; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat32Number*)output)[(i + start)* Stride] = (cmsFloat32Number)v; + else + ((cmsFloat32Number*)output)[i + start] = (cmsFloat32Number)v; + } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsFloat32Number)); + *swap1 = (cmsFloat32Number)v; + } + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsFloat32Number); + else + return output + (nChan + Extra) * sizeof(cmsFloat32Number); } static -LPBYTE PackNWordsSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) +cmsUInt8Number* PackDoublesFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) { - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - - for (i=nChan-1; i >= 0; --i) { - *(LPWORD) output = wOut[i]; - output += sizeof(WORD); + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 100.0 : 1.0; + cmsFloat64Number v = 0; + cmsFloat64Number* swap1 = (cmsFloat64Number*)output; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat64Number*)output)[(i + start) * Stride] = v; + else + ((cmsFloat64Number*)output)[i + start] = v; } - return output + T_EXTRA(info ->OutputFormat) * sizeof(WORD); + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsFloat64Number)); + *swap1 = v; + } + + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsFloat64Number); + else + return output + (nChan + Extra) * sizeof(cmsFloat64Number); + } + + static -LPBYTE PackNWordsBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - - for (i=0; i < nChan; i++) { - *(LPWORD) output = CHANGE_ENDIAN(wOut[i]); - output += sizeof(WORD); - } - - return output + T_EXTRA(info ->OutputFormat) * sizeof(WORD); -} - - -static -LPBYTE PackNWordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - - for (i=nChan-1; i >= 0; --i) { - *(LPWORD) output = CHANGE_ENDIAN(wOut[i]); - output += sizeof(WORD); - } - - return output + T_EXTRA(info ->OutputFormat) * sizeof(WORD); -} - - -static -LPBYTE PackPlanarBytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - LPBYTE Init = output; - - for (i=0; i < nChan; i++) { - - *(LPBYTE) output = RGB_16_TO_8(wOut[i]); - output += info -> StrideOut; - } - - return (Init + 1); -} - - -static -LPBYTE PackPlanarWords(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - register int i; - LPBYTE Init = output; - - for (i=0; i < nChan; i++) { - - *(LPWORD) output = wOut[i]; - output += (info -> StrideOut * sizeof(WORD)); - } - - return (Init + 2); -} - - -// CMYKcm (unrolled for speed) - -static -LPBYTE Pack6Bytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[0]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[2]); - *output++ = RGB_16_TO_8(wOut[3]); - *output++ = RGB_16_TO_8(wOut[4]); - *output++ = RGB_16_TO_8(wOut[5]); - - return output; -} - -// KCMYcm - -static -LPBYTE Pack6BytesSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[3]); - *output++ = RGB_16_TO_8(wOut[0]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[2]); - *output++ = RGB_16_TO_8(wOut[4]); - *output++ = RGB_16_TO_8(wOut[5]); - - return output; -} - -// CMYKcm -static -LPBYTE Pack6Words(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[0]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[2]; - output+= 2; - *(LPWORD) output = wOut[3]; - output+= 2; - *(LPWORD) output = wOut[4]; - output+= 2; - *(LPWORD) output = wOut[5]; - output+= 2; - - return output; -} - -// KCMYcm -static -LPBYTE Pack6WordsSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[3]; - output+= 2; - *(LPWORD) output = wOut[0]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[2]; - output+= 2; - *(LPWORD) output = wOut[4]; - output+= 2; - *(LPWORD) output = wOut[5]; - output+= 2; - - return output; -} - -// CMYKcm -static -LPBYTE Pack6WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[3]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[4]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[5]); - output+= 2; - - return output; -} - -// KCMYcm -static -LPBYTE Pack6WordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[3]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[4]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[5]); - output+= 2; - - return output; -} - - -static -LPBYTE Pack4Bytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[0]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[2]); - *output++ = RGB_16_TO_8(wOut[3]); - - return output; -} - -static -LPBYTE Pack4BytesReverse(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[0])); - *output++ = REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[1])); - *output++ = REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[2])); - *output++ = REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[3])); - - return output; -} - - -static -LPBYTE Pack4BytesSwapFirst(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[3]); - *output++ = RGB_16_TO_8(wOut[0]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[2]); - - return output; -} - - -// ABGR - -static -LPBYTE Pack4BytesSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[3]); - *output++ = RGB_16_TO_8(wOut[2]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[0]); - - return output; -} - - -static -LPBYTE Pack4BytesSwapSwapFirst(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[2]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[0]); - *output++ = RGB_16_TO_8(wOut[3]); - - return output; -} - - -static -LPBYTE Pack4Words(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[0]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[2]; - output+= 2; - *(LPWORD) output = wOut[3]; - output+= 2; - - return output; -} - - -static -LPBYTE Pack4WordsReverse(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = REVERSE_FLAVOR_16(wOut[0]); - output+= 2; - *(LPWORD) output = REVERSE_FLAVOR_16(wOut[1]); - output+= 2; - *(LPWORD) output = REVERSE_FLAVOR_16(wOut[2]); - output+= 2; - *(LPWORD) output = REVERSE_FLAVOR_16(wOut[3]); - output+= 2; - - return output; -} - -// ABGR - -static -LPBYTE Pack4WordsSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[3]; - output+= 2; - *(LPWORD) output = wOut[2]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[0]; - output+= 2; - - return output; -} - -// CMYK -static -LPBYTE Pack4WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[3]); - output+= 2; - - return output; -} - - -static -LPBYTE Pack4WordsBigEndianReverse(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) +cmsUInt8Number* PackLabFloatFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) { - *(LPWORD) output = CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[0])); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[1])); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[2])); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[3])); - output+= 2; - - return output; -} - -// KYMC - -static -LPBYTE Pack4WordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[3]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - - return output; -} - -static -LPBYTE Pack3Bytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[0]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[2]); - - return output; -} - -static -LPBYTE Pack3BytesLab(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = (BYTE) (wOut[0] >> 8); - *output++ = (BYTE) (wOut[1] >> 8); - *output++ = (BYTE) (wOut[2] >> 8); - - return output; -} - - -static -LPBYTE Pack3BytesSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[2]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[0]); - - return output; -} - - -static -LPBYTE Pack3Words(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[0]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[2]; - output+= 2; - - return output; -} - -static -LPBYTE Pack3WordsSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[2]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[0]; - output+= 2; - - return output; -} - -static -LPBYTE Pack3WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - - return output; -} - - -static -LPBYTE Pack3WordsSwapBigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - - return output; -} - - -static -LPBYTE Pack3BytesAndSkip1(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[0]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[2]); - output++; - - return output; -} - - -static -LPBYTE Pack3BytesAndSkip1SwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - output++; - *output++ = RGB_16_TO_8(wOut[0]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[2]); - - return output; -} - -static -LPBYTE Pack3BytesAndSkip1Swap(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - output++; - *output++ = RGB_16_TO_8(wOut[2]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[0]); - - return output; -} - - -static -LPBYTE Pack3BytesAndSkip1SwapSwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[2]); - *output++ = RGB_16_TO_8(wOut[1]); - *output++ = RGB_16_TO_8(wOut[0]); - output++; - - return output; -} - - -static -LPBYTE Pack3WordsAndSkip1(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[0]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[2]; - output+= 2; - output+= 2; - - return output; -} - -static -LPBYTE Pack3WordsAndSkip1Swap(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - output+= 2; - *(LPWORD) output = wOut[2]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[0]; - output+= 2; - - - return output; -} - - -static -LPBYTE Pack3WordsAndSkip1SwapSwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[2]; - output+= 2; - *(LPWORD) output = wOut[1]; - output+= 2; - *(LPWORD) output = wOut[0]; - output+= 2; - output+= 2; - - - return output; -} - - -static -LPBYTE Pack3WordsAndSkip1BigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - output+= 2; - - return output; -} - - -static -LPBYTE Pack3WordsAndSkip1SwapBigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - - - return output; -} - - - -static -LPBYTE Pack1Byte(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[0]); - return output; -} - - -static -LPBYTE Pack1ByteAndSkip1(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *output++ = RGB_16_TO_8(wOut[0]); - output++; - return output; -} - - -static -LPBYTE Pack1ByteAndSkip1SwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - output++; - *output++ = RGB_16_TO_8(wOut[0]); - - return output; -} - -static -LPBYTE Pack1Word(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[0]; - output+= 2; - - return output; -} - -static -LPBYTE Pack1WordBigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - - return output; -} - - -static -LPBYTE Pack1WordAndSkip1(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = wOut[0]; - output+= 4; - - return output; -} - -static -LPBYTE Pack1WordAndSkip1SwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - output += 2; - *(LPWORD) output = wOut[0]; - output+= 2; - - return output; -} - - -static -LPBYTE Pack1WordAndSkip1BigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - *(LPWORD) output = CHANGE_ENDIAN(wOut[0]); - output+= 4; - - return output; -} - - -// Unencoded Float values -- don't try optimize speed - -static -LPBYTE PackLabDouble(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ + cmsFloat32Number* Out = (cmsFloat32Number*) output; if (T_PLANAR(Info -> OutputFormat)) { - cmsCIELab Lab; - double* Out = (double*) output; - cmsLabEncoded2Float(&Lab, wOut); - - Out[0] = Lab.L; - Out[Info ->StrideOut] = Lab.a; - Out[Info ->StrideOut*2] = Lab.b; - - return output + sizeof(double); - + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat32Number) (wOut[0] * 100.0); + Out[Stride] = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0); + Out[Stride*2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0); + + return output + sizeof(cmsFloat32Number); } else { - if (Info ->lOutputV4Lab) - cmsLabEncoded2Float4((LPcmsCIELab) output, wOut); - else - cmsLabEncoded2Float((LPcmsCIELab) output, wOut); - - return output + (sizeof(cmsCIELab) + T_EXTRA(Info ->OutputFormat) * sizeof(double)); - } - -} - -static -LPBYTE PackXYZDouble(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - - if (T_PLANAR(Info -> OutputFormat)) { - - cmsCIEXYZ XYZ; - double* Out = (double*) output; - cmsXYZEncoded2Float(&XYZ, wOut); - - Out[0] = XYZ.X; - Out[Info ->StrideOut] = XYZ.Y; - Out[Info ->StrideOut*2] = XYZ.Z; - - return output + sizeof(double); - - } - else { - - cmsXYZEncoded2Float((LPcmsCIEXYZ) output, wOut); - - return output + (sizeof(cmsCIEXYZ) + T_EXTRA(Info ->OutputFormat) * sizeof(double)); - } -} - - - -static -LPBYTE PackInkDouble(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) -{ - double* Inks = (double*) output; - int nChan = T_CHANNELS(Info -> OutputFormat); - int i; - - if (T_PLANAR(Info -> OutputFormat)) { - - for (i=0; i < nChan; i++) { - - Inks[i*Info ->StrideOut] = wOut[i] / 655.35; - } - - return output + sizeof(double); - } - else { - - for (i=0; i < nChan; i++) { - - Inks[i] = wOut[i] / 655.35; - } - - - return output + (nChan + T_EXTRA(Info ->OutputFormat)) * sizeof(double); + Out[0] = (cmsFloat32Number) (wOut[0] * 100.0); + Out[1] = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0); + Out[2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0); + + return output + (sizeof(cmsFloat32Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); } } static -LPBYTE PackDouble(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output) +cmsUInt8Number* PackLabDoubleFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) { - double* Inks = (double*) output; - int nChan = T_CHANNELS(Info -> OutputFormat); - int i; - + cmsFloat64Number* Out = (cmsFloat64Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat64Number) (wOut[0] * 100.0); + Out[Stride] = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0); + Out[Stride*2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0); + + return output + sizeof(cmsFloat64Number); + } + else { + + Out[0] = (cmsFloat64Number) (wOut[0] * 100.0); + Out[1] = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0); + Out[2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0); + + return output + (sizeof(cmsFloat64Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); + } + +} + + +// From 0..1 range to 0..MAX_ENCODEABLE_XYZ +static +cmsUInt8Number* PackXYZFloatFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Out = (cmsFloat32Number*) output; if (T_PLANAR(Info -> OutputFormat)) { - for (i=0; i < nChan; i++) { - - Inks[i*Info ->StrideOut] = wOut[i] / 65535.0; - } - - return output + sizeof(double); - + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[Stride] = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[Stride*2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + sizeof(cmsFloat32Number); } else { - for (i=0; i < nChan; i++) { - - Inks[i] = wOut[i] / 65535.0; - } - - return output + (nChan + T_EXTRA(Info ->OutputFormat)) * sizeof(double); + + Out[0] = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[1] = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + (sizeof(cmsFloat32Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); + } + +} + +// Same, but convert to double +static +cmsUInt8Number* PackXYZDoubleFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Out = (cmsFloat64Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[Stride] = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[Stride*2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + sizeof(cmsFloat64Number); + } + else { + + Out[0] = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[1] = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + (sizeof(cmsFloat64Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); } } -// choose routine from Input identifier - -_cmsFIXFN _cmsIdentifyInputFormat(_LPcmsTRANSFORM xform, DWORD dwInput) +// ---------------------------------------------------------------------------------------------------------------- + +#ifndef CMS_NO_HALF_SUPPORT + +// Decodes an stream of half floats to wIn[] described by input format + +static +cmsUInt8Number* UnrollHalfTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt32Number i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 655.35F : 65535.0F; + + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] ); + else + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[i + start] ) ; + + if (Reverse) v = maximum - v; + + wIn[index] = _cmsQuickSaturateWord(v * maximum); + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsUInt16Number); + else + return accum + (nChan + Extra) * sizeof(cmsUInt16Number); +} + +// Decodes an stream of half floats to wIn[] described by input format + +static +cmsUInt8Number* UnrollHalfToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) { - _cmsFIXFN FromInput = NULL; - - - // Check Named Color - - if (xform) { - - if (xform ->InputProfile) { - - if (cmsGetDeviceClass(xform ->InputProfile) == icSigNamedColorClass) { - - if (dwInput != TYPE_NAMED_COLOR_INDEX) { - cmsSignalError(LCMS_ERRC_ABORTED, "Named color needs TYPE_NAMED_COLOR_INDEX"); - return NULL; - } - } - - } - } - - // Unencoded modes - - if (T_BYTES(dwInput) == 0) { - - switch (T_COLORSPACE(dwInput)) { - - case PT_Lab: - FromInput = UnrollLabDouble; - break; - case PT_XYZ: - FromInput = UnrollXYZDouble; - break; - - // 0.0 .. 1.0 range - - case PT_GRAY: - case PT_RGB: - case PT_YCbCr: - case PT_YUV: - case PT_YUVK: - case PT_HSV: - case PT_HLS: - case PT_Yxy: - if (T_CHANNELS(dwInput) == 1) - FromInput = UnrollDouble1Chan; - else - FromInput = UnrollDouble; - break; - - // Inks (%) 0.0 .. 100.0 - - default: - FromInput = UnrollInkDouble; - break; - } - - } - else { - - if (T_PLANAR(dwInput)) { - - switch (T_BYTES(dwInput)) { - - case 1: - FromInput = UnrollPlanarBytes; - break; - - case 2: - if (T_ENDIAN16(dwInput)) - FromInput = UnrollPlanarWordsBigEndian; - else - FromInput = UnrollPlanarWords; - break; - - default:; - } + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt32Number i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] ); + else + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[i + start] ) ; + + v /= maximum; + + wIn[index] = Reverse ? 1 - v : v; + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsUInt16Number); + else + return accum + (nChan + Extra) * sizeof(cmsUInt16Number); +} + + +static +cmsUInt8Number* PackHalfFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat32Number maximum = IsInkSpace(info->OutputFormat) ? 655.35F : 65535.0F; + cmsFloat32Number v = 0; + cmsUInt16Number* swap1 = (cmsUInt16Number*)output; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat32Number)wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsUInt16Number*)output)[(i + start) * Stride] = _cmsFloat2Half(v); + else + ((cmsUInt16Number*)output)[i + start] = _cmsFloat2Half(v); } - else { - - switch (T_BYTES(dwInput)) { - - case 1: // 1 byte per channel - - switch (T_CHANNELS(dwInput) + T_EXTRA(dwInput)) { - - case 1: if (T_FLAVOR(dwInput)) - FromInput = Unroll1ByteReversed; - else - FromInput = Unroll1Byte; - break; - - case 2: if (T_SWAPFIRST(dwInput)) - FromInput = Unroll2ByteSwapFirst; - else - FromInput = Unroll2Byte; - break; - - case 3: if (T_DOSWAP(dwInput)) - FromInput = Unroll3BytesSwap; - else { - if (T_EXTRA(dwInput) == 2) - FromInput = Unroll1ByteSkip2; - else - if (T_COLORSPACE(dwInput) == PT_Lab) - FromInput = Unroll3BytesLab; - else - FromInput = Unroll3Bytes; - } - break; - case 4: - // TODO: ALab8 must be fixed to match v2 encoding - - if (T_DOSWAP(dwInput)) { - if (T_SWAPFIRST(dwInput)) - - FromInput = Unroll4BytesSwapSwapFirst; - else - FromInput = Unroll4BytesSwap; - } - else { - if (T_SWAPFIRST(dwInput)) - FromInput = Unroll4BytesSwapFirst; - else { - if (T_FLAVOR(dwInput)) - FromInput = Unroll4BytesReverse; - else - FromInput = Unroll4Bytes; - } - } - break; - - - case 5: - case 6: - case 7: - case 8: - if (!T_DOSWAP(dwInput) && !T_SWAPFIRST(dwInput)) - FromInput = UnrollAnyBytes; - break; - - - default:; - } - break; - - - case 2: // 1 word per channel - - switch (T_CHANNELS(dwInput) + T_EXTRA(dwInput)) - { - case 1: if (T_ENDIAN16(dwInput)) - FromInput = Unroll1WordBigEndian; - else - if (T_FLAVOR(dwInput)) - FromInput = Unroll1WordReversed; - else - FromInput = Unroll1Word; - break; - - case 2: if (T_ENDIAN16(dwInput)) - FromInput = Unroll2WordBigEndian; - else { - if (T_SWAPFIRST(dwInput)) - FromInput = Unroll2WordSwapFirst; - else - FromInput = Unroll2Word; - } - break; - - case 3: if (T_DOSWAP(dwInput)) { - if (T_ENDIAN16(dwInput)) - FromInput = Unroll3WordsSwapBigEndian; - else - FromInput = Unroll3WordsSwap; - } - else { - if (T_ENDIAN16(dwInput)) - FromInput = Unroll3WordsBigEndian; - else - FromInput = Unroll3Words; - } - break; - - case 4: if (T_DOSWAP(dwInput)) { - - if (T_ENDIAN16(dwInput)) - FromInput = Unroll4WordsSwapBigEndian; - else { - - if (T_SWAPFIRST(dwInput)) - FromInput = Unroll4WordsSwapSwapFirst; - else - FromInput = Unroll4WordsSwap; - - } - - } - else { - - if (T_EXTRA(dwInput) == 3) - FromInput = Unroll1WordSkip3; - else - - if (T_ENDIAN16(dwInput)) { - - if (T_FLAVOR(dwInput)) - FromInput = Unroll4WordsBigEndianReverse; - else - FromInput = Unroll4WordsBigEndian; - } - else { - if (T_SWAPFIRST(dwInput)) - FromInput = Unroll4WordsSwapFirst; - else { - if (T_FLAVOR(dwInput)) - FromInput = Unroll4WordsReverse; - else - FromInput = Unroll4Words; - } - } - } - break; - - - case 5: - case 6: - case 7: - case 8: - if (!T_DOSWAP(dwInput) && !T_SWAPFIRST(dwInput)) - FromInput = UnrollAnyWords; - break; - - } - break; - - default:; + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsUInt16Number)); + *swap1 = _cmsFloat2Half(v); } - } - } - - - if (!FromInput) - cmsSignalError(LCMS_ERRC_ABORTED, "Unknown input format"); - - return FromInput; + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsUInt16Number); + else + return output + (nChan + Extra) * sizeof(cmsUInt16Number); } -// choose routine from Input identifier - -_cmsFIXFN _cmsIdentifyOutputFormat(_LPcmsTRANSFORM xform, DWORD dwOutput) + + +static +cmsUInt8Number* PackHalfFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) { - _cmsFIXFN ToOutput = NULL; - - - if (T_BYTES(dwOutput) == 0) { - - switch (T_COLORSPACE(dwOutput)) { - - case PT_Lab: - ToOutput = PackLabDouble; - break; - case PT_XYZ: - ToOutput = PackXYZDouble; - break; - - // 0.0 .. 1.0 range - case PT_GRAY: - case PT_RGB: - case PT_YCbCr: - case PT_YUV: - case PT_YUVK: - case PT_HSV: - case PT_HLS: - case PT_Yxy: - ToOutput = PackDouble; - break; - - // Inks (%) 0.0 .. 100.0 - - default: - ToOutput = PackInkDouble; - break; - } - + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat32Number maximum = IsInkSpace(info->OutputFormat) ? 100.0F : 1.0F; + cmsUInt16Number* swap1 = (cmsUInt16Number*)output; + cmsFloat32Number v = 0; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsUInt16Number*)output)[(i + start)* Stride] = _cmsFloat2Half(v); + else + ((cmsUInt16Number*)output)[i + start] = _cmsFloat2Half(v); } - else - - if (T_PLANAR(dwOutput)) { - - switch (T_BYTES(dwOutput)) { - - case 1: ToOutput = PackPlanarBytes; - break; - - case 2:if (!T_ENDIAN16(dwOutput)) - ToOutput = PackPlanarWords; - break; - - default:; - } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsUInt16Number)); + *swap1 = (cmsUInt16Number)_cmsFloat2Half(v); } - else { - - switch (T_BYTES(dwOutput)) { - - case 1: - switch (T_CHANNELS(dwOutput)) - { - case 1: - if (T_DITHER(dwOutput)) - ToOutput = PackNBytesDither; - else - ToOutput = Pack1Byte; - if (T_EXTRA(dwOutput) == 1) { - if (T_SWAPFIRST(dwOutput)) - ToOutput = Pack1ByteAndSkip1SwapFirst; - else - ToOutput = Pack1ByteAndSkip1; - } - break; - - case 3: - switch (T_EXTRA(dwOutput)) { - - case 0: if (T_DOSWAP(dwOutput)) - ToOutput = Pack3BytesSwap; - else - if (T_COLORSPACE(dwOutput) == PT_Lab) - ToOutput = Pack3BytesLab; - else { - if (T_DITHER(dwOutput)) - ToOutput = PackNBytesDither; - else - ToOutput = Pack3Bytes; - } - break; - - case 1: // TODO: ALab8 should be handled here - - if (T_DOSWAP(dwOutput)) { - - if (T_SWAPFIRST(dwOutput)) - ToOutput = Pack3BytesAndSkip1SwapSwapFirst; - else - ToOutput = Pack3BytesAndSkip1Swap; - } - else { - if (T_SWAPFIRST(dwOutput)) - ToOutput = Pack3BytesAndSkip1SwapFirst; - else - ToOutput = Pack3BytesAndSkip1; - } - break; - - default:; - } - break; - - case 4: if (T_EXTRA(dwOutput) == 0) { - - - if (T_DOSWAP(dwOutput)) { - - - if (T_SWAPFIRST(dwOutput)) { - ToOutput = Pack4BytesSwapSwapFirst; - } - else { - - if (T_DITHER(dwOutput)) { - ToOutput = PackNBytesSwapDither; - } - else { - ToOutput = Pack4BytesSwap; - } - } - } - else { - if (T_SWAPFIRST(dwOutput)) - ToOutput = Pack4BytesSwapFirst; - else { - - if (T_FLAVOR(dwOutput)) - ToOutput = Pack4BytesReverse; - else { - if (T_DITHER(dwOutput)) - ToOutput = PackNBytesDither; - else - ToOutput = Pack4Bytes; - } - } - } - } - else { - if (!T_DOSWAP(dwOutput) && !T_SWAPFIRST(dwOutput)) - ToOutput = PackNBytes; - } - break; - - // Hexachrome separations. - case 6: if (T_EXTRA(dwOutput) == 0) { - - if( T_DOSWAP(dwOutput)) - ToOutput = Pack6BytesSwap; - else - ToOutput = Pack6Bytes; - } - else { - if (!T_DOSWAP(dwOutput) && !T_SWAPFIRST(dwOutput)) - ToOutput = PackNBytes; - - } - break; - - case 2: - case 5: - case 7: - case 8: - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - - if ((T_EXTRA(dwOutput) == 0) && (T_SWAPFIRST(dwOutput) == 0)) - { - if (T_DOSWAP(dwOutput)) - ToOutput = PackNBytesSwap; - else { - - if (T_DITHER(dwOutput)) - ToOutput = PackNBytesDither; - else - ToOutput = PackNBytes; - } - } - break; - - default:; - } - break; - - - case 2: - - switch (T_CHANNELS(dwOutput)) { - - case 1: - if (T_ENDIAN16(dwOutput)) - - ToOutput = Pack1WordBigEndian; - else - ToOutput = Pack1Word; - - if (T_EXTRA(dwOutput) == 1) { - - if (T_ENDIAN16(dwOutput)) - - ToOutput = Pack1WordAndSkip1BigEndian; - else { - if (T_SWAPFIRST(dwOutput)) - ToOutput = Pack1WordAndSkip1SwapFirst; - else - ToOutput = Pack1WordAndSkip1; - } - } - break; - - case 3: - - switch (T_EXTRA(dwOutput)) { - - case 0: - if (T_DOSWAP(dwOutput)) { - - if (T_ENDIAN16(dwOutput)) - - ToOutput = Pack3WordsSwapBigEndian; - else - ToOutput = Pack3WordsSwap; - } - else { - if (T_ENDIAN16(dwOutput)) - - ToOutput = Pack3WordsBigEndian; - else - ToOutput = Pack3Words; - } - break; - - case 1: if (T_DOSWAP(dwOutput)) { - - if (T_ENDIAN16(dwOutput)) - - ToOutput = Pack3WordsAndSkip1SwapBigEndian; - else { - if (T_SWAPFIRST(dwOutput)) - ToOutput = Pack3WordsAndSkip1SwapSwapFirst; - else - ToOutput = Pack3WordsAndSkip1Swap; - } - } - else { - if (T_ENDIAN16(dwOutput)) - ToOutput = Pack3WordsAndSkip1BigEndian; - else - ToOutput = Pack3WordsAndSkip1; - } - default:; - } - break; - - case 4: if (T_EXTRA(dwOutput) == 0) { - - if (T_DOSWAP(dwOutput)) { - - if (T_ENDIAN16(dwOutput)) - ToOutput = Pack4WordsSwapBigEndian; - else - ToOutput = Pack4WordsSwap; - } - else { - - if (T_ENDIAN16(dwOutput)) { - - if (T_FLAVOR(dwOutput)) - ToOutput = Pack4WordsBigEndianReverse; - else - ToOutput = Pack4WordsBigEndian; - } - else { - if (T_FLAVOR(dwOutput)) - ToOutput = Pack4WordsReverse; - else - ToOutput = Pack4Words; - } - } - } - else { - if (!T_DOSWAP(dwOutput) && !T_SWAPFIRST(dwOutput)) - ToOutput = PackNWords; - } - break; - - case 6: if (T_EXTRA(dwOutput) == 0) { - - if (T_DOSWAP(dwOutput)) { - - if (T_ENDIAN16(dwOutput)) - ToOutput = Pack6WordsSwapBigEndian; - else - ToOutput = Pack6WordsSwap; - } - else { - - if (T_ENDIAN16(dwOutput)) - ToOutput = Pack6WordsBigEndian; - else - ToOutput = Pack6Words; - } - } - else { - if (!T_DOSWAP(dwOutput) && !T_SWAPFIRST(dwOutput)) - ToOutput = PackNWords; - } - break; - - - case 2: - case 5: - case 7: - case 8: - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: if ((T_EXTRA(dwOutput) == 0) && (T_SWAPFIRST(dwOutput) == 0)) { - - if (T_DOSWAP(dwOutput)) { - - if (T_ENDIAN16(dwOutput)) - ToOutput = PackNWordsSwapBigEndian; - else - ToOutput = PackNWordsSwap; - } - else { - - if (T_ENDIAN16(dwOutput)) - ToOutput = PackNWordsBigEndian; - else - ToOutput = PackNWords; - } - } - break; - - default:; - } - break; - - default:; - } - } - - if (!ToOutput) - cmsSignalError(LCMS_ERRC_ABORTED, "Unknown output format"); - - return ToOutput; + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsUInt16Number); + else + return output + (nChan + Extra)* sizeof(cmsUInt16Number); +} + +#endif + +// ---------------------------------------------------------------------------------------------------------------- + + +static const cmsFormatters16 InputFormatters16[] = { + + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleTo16}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleTo16}, + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatTo16}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatTo16}, + { TYPE_GRAY_DBL, 0, UnrollDouble1Chan}, + { FLOAT_SH(1)|BYTES_SH(0), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYSWAP|ANYEXTRA|ANYSPACE, UnrollDoubleTo16}, + { FLOAT_SH(1)|BYTES_SH(4), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYSWAP|ANYEXTRA|ANYSPACE, UnrollFloatTo16}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYEXTRA|ANYSWAP|ANYSPACE, UnrollHalfTo16}, +#endif + + { CHANNELS_SH(1)|BYTES_SH(1), ANYSPACE, Unroll1Byte}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Unroll1ByteSkip1}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(2), ANYSPACE, Unroll1ByteSkip2}, + { CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Unroll1ByteReversed}, + { COLORSPACE_SH(PT_MCH2)|CHANNELS_SH(2)|BYTES_SH(1), 0, Unroll2Bytes}, + + { TYPE_LabV2_8, 0, UnrollLabV2_8 }, + { TYPE_ALabV2_8, 0, UnrollALabV2_8 }, + { TYPE_LabV2_16, 0, UnrollLabV2_16 }, + + { CHANNELS_SH(3)|BYTES_SH(1), ANYSPACE, Unroll3Bytes}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3BytesSwap}, + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3BytesSkip1Swap}, + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll3BytesSkip1SwapFirst}, + + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Unroll3BytesSkip1SwapSwapFirst}, + + { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Unroll4Bytes}, + { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Unroll4BytesReverse}, + { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll4BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapSwapFirst}, + + { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST| + ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarBytes}, + + { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollChunkyBytes}, + + { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Unroll1Word}, + { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Unroll1WordReversed}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(3), ANYSPACE, Unroll1WordSkip3}, + + { CHANNELS_SH(2)|BYTES_SH(2), ANYSPACE, Unroll2Words}, + { CHANNELS_SH(3)|BYTES_SH(2), ANYSPACE, Unroll3Words}, + { CHANNELS_SH(4)|BYTES_SH(2), ANYSPACE, Unroll4Words}, + + { CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Unroll3WordsSwap}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll3WordsSkip1SwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3WordsSkip1Swap}, + { CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Unroll4WordsReverse}, + { CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1), ANYSPACE, Unroll4WordsSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Unroll4WordsSwap}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4WordsSwapSwapFirst}, + + + { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarWords}, + { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollAnyWords}, +}; + + + +static const cmsFormattersFloat InputFormattersFloat[] = { + + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleToFloat}, + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatToFloat}, + + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleToFloat}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatToFloat}, + + { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollFloatsToFloat}, + + { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollDoublesToFloat}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollHalfToFloat}, +#endif +}; + + +// Bit fields set to one in the mask are not compared +static +cmsFormatter _cmsGetStockInputFormatter(cmsUInt32Number dwInput, cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsFormatter fr; + + switch (dwFlags) { + + case CMS_PACK_FLAGS_16BITS: { + for (i=0; i < sizeof(InputFormatters16) / sizeof(cmsFormatters16); i++) { + const cmsFormatters16* f = InputFormatters16 + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.Fmt16 = f ->Frm; + return fr; + } + } + } + break; + + case CMS_PACK_FLAGS_FLOAT: { + for (i=0; i < sizeof(InputFormattersFloat) / sizeof(cmsFormattersFloat); i++) { + const cmsFormattersFloat* f = InputFormattersFloat + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.FmtFloat = f ->Frm; + return fr; + } + } + } + break; + + default:; + + } + + fr.Fmt16 = NULL; + return fr; } -// User formatters for (weird) cases not already included - -void LCMSEXPORT cmsSetUserFormatters(cmsHTRANSFORM hTransform, DWORD dwInput, cmsFORMATTER Input, - DWORD dwOutput, cmsFORMATTER Output) -{ - _LPcmsTRANSFORM xform = (_LPcmsTRANSFORM) (LPSTR) hTransform; - - if (Input != NULL) { - xform ->FromInput = (_cmsFIXFN) Input; - xform ->InputFormat = dwInput; - } - - if (Output != NULL) { - xform ->ToOutput = (_cmsFIXFN) Output; - xform ->OutputFormat = dwOutput; - } - -} - -void LCMSEXPORT cmsGetUserFormatters(cmsHTRANSFORM hTransform, - LPDWORD InputFormat, cmsFORMATTER* Input, - LPDWORD OutputFormat, cmsFORMATTER* Output) +static const cmsFormatters16 OutputFormatters16[] = { + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFrom16}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFrom16}, + + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, PackLabFloatFrom16}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, PackXYZFloatFrom16}, + + { FLOAT_SH(1)|BYTES_SH(0), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackDoubleFrom16}, + { FLOAT_SH(1)|BYTES_SH(4), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackFloatFrom16}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackHalfFrom16}, +#endif + + { CHANNELS_SH(1)|BYTES_SH(1), ANYSPACE, Pack1Byte}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Pack1ByteSkip1}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack1ByteSkip1SwapFirst}, + + { CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack1ByteReversed}, + + { TYPE_LabV2_8, 0, PackLabV2_8 }, + { TYPE_ALabV2_8, 0, PackALabV2_8 }, + { TYPE_LabV2_16, 0, PackLabV2_16 }, + + { CHANNELS_SH(3)|BYTES_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesAndSkip1Optimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapFirstOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapSwapFirstOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesSwapOptimized}, + + + + { CHANNELS_SH(3)|BYTES_SH(1), ANYSPACE, Pack3Bytes}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack3BytesAndSkip1SwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapSwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1Swap}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3BytesSwap}, + { CHANNELS_SH(6)|BYTES_SH(1), ANYSPACE, Pack6Bytes}, + { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack6BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Pack4Bytes}, + { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack4BytesReverse}, + { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack4BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapSwapFirst}, + + { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyBytes}, + { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarBytes}, + + { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Pack1Word}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack1WordSkip1}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack1WordSkip1SwapFirst}, + { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Pack1WordReversed}, + { CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack1WordBigEndian}, + { CHANNELS_SH(3)|BYTES_SH(2), ANYSPACE, Pack3Words}, + { CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack3WordsSwap}, + { CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack3WordsBigEndian}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack3WordsAndSkip1}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3WordsAndSkip1Swap}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack3WordsAndSkip1SwapFirst}, + + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Pack3WordsAndSkip1SwapSwapFirst}, + + { CHANNELS_SH(4)|BYTES_SH(2), ANYSPACE, Pack4Words}, + { CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Pack4WordsReverse}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack4WordsSwap}, + { CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack4WordsBigEndian}, + + { CHANNELS_SH(6)|BYTES_SH(2), ANYSPACE, Pack6Words}, + { CHANNELS_SH(6)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack6WordsSwap}, + + { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarWords}, + { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyWords} + +}; + + +static const cmsFormattersFloat OutputFormattersFloat[] = { + // Type Mask Function + // ---------------------------- --------------------------------------------------- ---------------------------- + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, PackLabFloatFromFloat}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, PackXYZFloatFromFloat}, + + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFromFloat}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFromFloat}, + + { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR| + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackFloatsFromFloat }, + { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR| + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackDoublesFromFloat }, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackHalfFromFloat }, +#endif + +}; + + +// Bit fields set to one in the mask are not compared +static +cmsFormatter _cmsGetStockOutputFormatter(cmsUInt32Number dwInput, cmsUInt32Number dwFlags) { - _LPcmsTRANSFORM xform = (_LPcmsTRANSFORM) (LPSTR) hTransform; - - if (Input) *Input = (cmsFORMATTER) xform ->FromInput; - if (InputFormat) *InputFormat = xform -> InputFormat; - if (Output) *Output = (cmsFORMATTER) xform ->ToOutput; - if (OutputFormat) *OutputFormat = xform -> OutputFormat; + cmsUInt32Number i; + cmsFormatter fr; + + // Optimization is only a hint + dwInput &= ~OPTIMIZED_SH(1); + + switch (dwFlags) + { + + case CMS_PACK_FLAGS_16BITS: { + + for (i=0; i < sizeof(OutputFormatters16) / sizeof(cmsFormatters16); i++) { + const cmsFormatters16* f = OutputFormatters16 + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.Fmt16 = f ->Frm; + return fr; + } + } + } + break; + + case CMS_PACK_FLAGS_FLOAT: { + + for (i=0; i < sizeof(OutputFormattersFloat) / sizeof(cmsFormattersFloat); i++) { + const cmsFormattersFloat* f = OutputFormattersFloat + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.FmtFloat = f ->Frm; + return fr; + } + } + } + break; + + default:; + + } + + fr.Fmt16 = NULL; + return fr; +} + + +typedef struct _cms_formatters_factory_list { + + cmsFormatterFactory Factory; + struct _cms_formatters_factory_list *Next; + +} cmsFormattersFactoryList; + +_cmsFormattersPluginChunkType _cmsFormattersPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupFormatterFactoryList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsFormattersPluginChunkType newHead = { NULL }; + cmsFormattersFactoryList* entry; + cmsFormattersFactoryList* Anterior = NULL; + _cmsFormattersPluginChunkType* head = (_cmsFormattersPluginChunkType*) src->chunks[FormattersPlugin]; + + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->FactoryList; + entry != NULL; + entry = entry ->Next) { + + cmsFormattersFactoryList *newEntry = ( cmsFormattersFactoryList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsFormattersFactoryList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.FactoryList == NULL) + newHead.FactoryList = newEntry; + } + + ctx ->chunks[FormattersPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsFormattersPluginChunkType)); } - -// Change format of yet existing transform. No colorspace checking is performed - -void LCMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, - DWORD dwInputFormat, - DWORD dwOutputFormat) +// The interpolation plug-in memory chunk allocator/dup +void _cmsAllocFormattersPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Duplicate the LIST + DupFormatterFactoryList(ctx, src); + } + else { + static _cmsFormattersPluginChunkType FormattersPluginChunk = { NULL }; + ctx ->chunks[FormattersPlugin] = _cmsSubAllocDup(ctx ->MemPool, &FormattersPluginChunk, sizeof(_cmsFormattersPluginChunkType)); + } +} + + + +// Formatters management +cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); + cmsPluginFormatters* Plugin = (cmsPluginFormatters*) Data; + cmsFormattersFactoryList* fl ; + + // Reset to built-in defaults + if (Data == NULL) { + + ctx ->FactoryList = NULL; + return TRUE; + } + + fl = (cmsFormattersFactoryList*) _cmsPluginMalloc(ContextID, sizeof(cmsFormattersFactoryList)); + if (fl == NULL) return FALSE; + + fl ->Factory = Plugin ->FormattersFactory; + + fl ->Next = ctx -> FactoryList; + ctx ->FactoryList = fl; + + return TRUE; +} + +cmsFormatter CMSEXPORT _cmsGetFormatter(cmsContext ContextID, + cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags) { - - cmsSetUserFormatters(hTransform, - dwInputFormat, - (cmsFORMATTER) _cmsIdentifyInputFormat((_LPcmsTRANSFORM) hTransform, dwInputFormat), - dwOutputFormat, - (cmsFORMATTER) _cmsIdentifyOutputFormat((_LPcmsTRANSFORM) hTransform, dwOutputFormat)); + _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); + cmsFormattersFactoryList* f; + + for (f =ctx->FactoryList; f != NULL; f = f ->Next) { + + cmsFormatter fn = f ->Factory(Type, Dir, dwFlags); + if (fn.Fmt16 != NULL) return fn; + } + + // Revert to default + if (Dir == cmsFormatterInput) + return _cmsGetStockInputFormatter(Type, dwFlags); + else + return _cmsGetStockOutputFormatter(Type, dwFlags); +} + + +// Return whatever given formatter refers to float values +cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type) +{ + return T_FLOAT(Type) ? TRUE : FALSE; } + +// Return whatever given formatter refers to 8 bits +cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type) +{ + cmsUInt32Number Bytes = T_BYTES(Type); + + return (Bytes == 1); +} + +// Build a suitable formatter for the colorspace of this profile +cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat) +{ + + cmsColorSpaceSignature ColorSpace = cmsGetColorSpace(hProfile); + cmsUInt32Number ColorSpaceBits = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace); + cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); + cmsUInt32Number Float = lIsFloat ? 1U : 0; + + // Create a fake formatter for result + return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); +} + +// Build a suitable formatter for the colorspace of this profile +cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat) +{ + + cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); + + cmsUInt32Number ColorSpaceBits = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace); + cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); + cmsUInt32Number Float = lIsFloat ? 1U : 0; + + // Create a fake formatter for result + return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); +} + diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmspcs.c --- a/src/share/native/sun/java2d/cmm/lcms/cmspcs.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmspcs.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,11 +49,13 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" // inter PCS conversions XYZ <-> CIE L* a* b* - -#include "lcms.h" - /* @@ -75,7 +78,7 @@ - Following ICC. PCS in Lab is coded as: + PCS in Lab2 is encoded as: 8 bit Lab PCS: @@ -90,9 +93,6 @@ b* - We are always playing with 16 bits-data, so I will ignore the - 8-bits encoding scheme. - Interchange Space Component Actual Range Encoded Range CIE XYZ X 0 -> 1.99997 0x0000 -> 0xffff @@ -116,318 +116,143 @@ */ +// Conversions +void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source) +{ + cmsFloat64Number ISum; + ISum = 1./(Source -> X + Source -> Y + Source -> Z); + Dest -> x = (Source -> X) * ISum; + Dest -> y = (Source -> Y) * ISum; + Dest -> Y = Source -> Y; +} -// On most modern computers, D > 4 M (i.e. a division takes more than 4 -// multiplications worth of time), so it is probably preferable to compute -// a 24 bit result directly. +void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source) +{ + Dest -> X = (Source -> x / Source -> y) * Source -> Y; + Dest -> Y = Source -> Y; + Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y; +} -// #define ITERATE 1 +/* + The break point (24/116)^3 = (6/29)^3 is a very small amount of tristimulus + primary (0.008856). Generally, this only happens for + nearly ideal blacks and for some orange / amber colors in transmission mode. + For example, the Z value of the orange turn indicator lamp lens on an + automobile will often be below this value. But the Z does not + contribute to the perceived color directly. +*/ static -float CubeRoot(float x) +cmsFloat64Number f(cmsFloat64Number t) { - float fr, r; - int ex, shx; - - /* Argument reduction */ - fr = (float) frexp(x, &ex); /* separate into mantissa and exponent */ - shx = ex % 3; - - if (shx > 0) - shx -= 3; /* compute shx such that (ex - shx) is divisible by 3 */ - - ex = (ex - shx) / 3; /* exponent of cube root */ - fr = (float) ldexp(fr, shx); - - /* 0.125 <= fr < 1.0 */ - -#ifdef ITERATE - /* Compute seed with a quadratic approximation */ - - fr = (-0.46946116F * fr + 1.072302F) * fr + 0.3812513F;/* 0.5<=fr<1 */ - r = ldexp(fr, ex); /* 6 bits of precision */ + const cmsFloat64Number Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0); - /* Newton-Raphson iterations */ - - r = (float)(2.0/3.0) * r + (float)(1.0/3.0) * x / (r * r); /* 12 bits */ - r = (float)(2.0/3.0) * r + (float)(1.0/3.0) * x / (r * r); /* 24 bits */ -#else /* ITERATE */ - - /* Use quartic rational polynomial with error < 2^(-24) */ - - fr = (float) (((((45.2548339756803022511987494 * fr + - 192.2798368355061050458134625) * fr + - 119.1654824285581628956914143) * fr + - 13.43250139086239872172837314) * fr + - 0.1636161226585754240958355063) - / - ((((14.80884093219134573786480845 * fr + - 151.9714051044435648658557668) * fr + - 168.5254414101568283957668343) * fr + - 33.9905941350215598754191872) * fr + - 1.0)); - r = (float) ldexp(fr, ex); /* 24 bits of precision */ -#endif - return r; + if (t <= Limit) + return (841.0/108.0) * t + (16.0/116.0); + else + return pow(t, 1.0/3.0); } static -double f(double t) +cmsFloat64Number f_1(cmsFloat64Number t) +{ + const cmsFloat64Number Limit = (24.0/116.0); + + if (t <= Limit) { + return (108.0/841.0) * (t - (16.0/116.0)); + } + + return t * t * t; +} + + +// Standard XYZ to Lab. it can handle negative XZY numbers in some cases +void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz) { + cmsFloat64Number fx, fy, fz; - const double Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0); + if (WhitePoint == NULL) + WhitePoint = cmsD50_XYZ(); + + fx = f(xyz->X / WhitePoint->X); + fy = f(xyz->Y / WhitePoint->Y); + fz = f(xyz->Z / WhitePoint->Z); + + Lab->L = 116.0*fy - 16.0; + Lab->a = 500.0*(fx - fy); + Lab->b = 200.0*(fy - fz); +} + + +// Standard XYZ to Lab. It can return negative XYZ in some cases +void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab) +{ + cmsFloat64Number x, y, z; - if (t <= Limit) - return (841.0/108.0) * t + (16.0/116.0); - else - return CubeRoot((float) t); + if (WhitePoint == NULL) + WhitePoint = cmsD50_XYZ(); + + y = (Lab-> L + 16.0) / 116.0; + x = y + 0.002 * Lab -> a; + z = y - 0.005 * Lab -> b; + + xyz -> X = f_1(x) * WhitePoint -> X; + xyz -> Y = f_1(y) * WhitePoint -> Y; + xyz -> Z = f_1(z) * WhitePoint -> Z; + +} + +static +cmsFloat64Number L2float2(cmsUInt16Number v) +{ + return (cmsFloat64Number) v / 652.800; +} + +// the a/b part +static +cmsFloat64Number ab2float2(cmsUInt16Number v) +{ + return ((cmsFloat64Number) v / 256.0) - 128.0; +} + +static +cmsUInt16Number L2Fix2(cmsFloat64Number L) +{ + return _cmsQuickSaturateWord(L * 652.8); +} + +static +cmsUInt16Number ab2Fix2(cmsFloat64Number ab) +{ + return _cmsQuickSaturateWord((ab + 128.0) * 256.0); } static -double f_1(double t) -{ - const double Limit = (24.0/116.0); - - if (t <= Limit) - { - double tmp; - - tmp = (108.0/841.0) * (t - (16.0/116.0)); - if (tmp <= 0.0) return 0.0; - else return tmp; - } - - return t * t * t; -} - - - -void LCMSEXPORT cmsXYZ2Lab(LPcmsCIEXYZ WhitePoint, LPcmsCIELab Lab, const cmsCIEXYZ* xyz) +cmsFloat64Number L2float4(cmsUInt16Number v) { - double fx, fy, fz; - - if (xyz -> X == 0 && xyz -> Y == 0 && xyz -> Z == 0) - { - Lab -> L = 0; - Lab -> a = 0; - Lab -> b = 0; - return; - } - - if (WhitePoint == NULL) - WhitePoint = cmsD50_XYZ(); - - fx = f(xyz->X / WhitePoint->X); - fy = f(xyz->Y / WhitePoint->Y); - fz = f(xyz->Z / WhitePoint->Z); - - Lab->L = 116.0* fy - 16.; - - Lab->a = 500.0*(fx - fy); - Lab->b = 200.0*(fy - fz); + return (cmsFloat64Number) v / 655.35; } - - -void cmsXYZ2LabEncoded(WORD XYZ[3], WORD Lab[3]) +// the a/b part +static +cmsFloat64Number ab2float4(cmsUInt16Number v) { - Fixed32 X, Y, Z; - double x, y, z, L, a, b; - double fx, fy, fz; - Fixed32 wL, wa, wb; - - X = (Fixed32) XYZ[0] << 1; - Y = (Fixed32) XYZ[1] << 1; - Z = (Fixed32) XYZ[2] << 1; - - - if (X==0 && Y==0 && Z==0) { - - Lab[0] = 0; - Lab[1] = Lab[2] = 0x8000; - return; - } - - // PCS is in D50 - - - x = FIXED_TO_DOUBLE(X) / D50X; - y = FIXED_TO_DOUBLE(Y) / D50Y; - z = FIXED_TO_DOUBLE(Z) / D50Z; - - - fx = f(x); - fy = f(y); - fz = f(z); - - L = 116.* fy - 16.; - - a = 500.*(fx - fy); - b = 200.*(fy - fz); - - a += 128.; - b += 128.; - - wL = (int) (L * 652.800 + .5); - wa = (int) (a * 256.0 + .5); - wb = (int) (b * 256.0 + .5); - - - Lab[0] = Clamp_L(wL); - Lab[1] = Clamp_ab(wa); - Lab[2] = Clamp_ab(wb); - - + return ((cmsFloat64Number) v / 257.0) - 128.0; } - - - - -void LCMSEXPORT cmsLab2XYZ(LPcmsCIEXYZ WhitePoint, LPcmsCIEXYZ xyz, const cmsCIELab* Lab) +void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3]) { - double x, y, z; - - if (Lab -> L <= 0) { - xyz -> X = 0; - xyz -> Y = 0; - xyz -> Z = 0; - return; - } - - - if (WhitePoint == NULL) - WhitePoint = cmsD50_XYZ(); - - y = (Lab-> L + 16.0) / 116.0; - x = y + 0.002 * Lab -> a; - z = y - 0.005 * Lab -> b; - - xyz -> X = f_1(x) * WhitePoint -> X; - xyz -> Y = f_1(y) * WhitePoint -> Y; - xyz -> Z = f_1(z) * WhitePoint -> Z; - + Lab->L = L2float2(wLab[0]); + Lab->a = ab2float2(wLab[1]); + Lab->b = ab2float2(wLab[2]); } - -void cmsLab2XYZEncoded(WORD Lab[3], WORD XYZ[3]) -{ - double L, a, b; - double X, Y, Z, x, y, z; - - - L = ((double) Lab[0] * 100.0) / 65280.0; - if (L==0.0) { - - XYZ[0] = 0; XYZ[1] = 0; XYZ[2] = 0; - return; - } - - a = ((double) Lab[1] / 256.0) - 128.0; - b = ((double) Lab[2] / 256.0) - 128.0; - - y = (L + 16.) / 116.0; - x = y + 0.002 * a; - z = y - 0.005 * b; - - X = f_1(x) * D50X; - Y = f_1(y) * D50Y; - Z = f_1(z) * D50Z; - - // Convert to 1.15 fixed format PCS - - - XYZ[0] = _cmsClampWord((int) floor(X * 32768.0 + 0.5)); - XYZ[1] = _cmsClampWord((int) floor(Y * 32768.0 + 0.5)); - XYZ[2] = _cmsClampWord((int) floor(Z * 32768.0 + 0.5)); - - -} - -static -double L2float3(WORD v) -{ - Fixed32 fix32; - - fix32 = (Fixed32) v; - return (double) fix32 / 652.800; -} - - -// the a/b part - -static -double ab2float3(WORD v) -{ - Fixed32 fix32; - - fix32 = (Fixed32) v; - return ((double) fix32/256.0)-128.0; -} - -static -WORD L2Fix3(double L) -{ - return (WORD) (L * 652.800 + 0.5); -} - -static -WORD ab2Fix3(double ab) -{ - return (WORD) ((ab + 128.0) * 256.0 + 0.5); -} - - -// ICC 4.0 -- ICC has changed PCS Lab encoding. - -static -WORD L2Fix4(double L) -{ - return (WORD) (L * 655.35 + 0.5); -} - -static -WORD ab2Fix4(double ab) -{ - return (WORD) ((ab + 128.0) * 257.0 + 0.5); -} - -static -double L2float4(WORD v) -{ - Fixed32 fix32; - - fix32 = (Fixed32) v; - return (double) fix32 / 655.35; -} - - -// the a/b part - -static -double ab2float4(WORD v) -{ - Fixed32 fix32; - - fix32 = (Fixed32) v; - return ((double) fix32/257.0)-128.0; -} - - -void LCMSEXPORT cmsLabEncoded2Float(LPcmsCIELab Lab, const WORD wLab[3]) -{ - Lab->L = L2float3(wLab[0]); - Lab->a = ab2float3(wLab[1]); - Lab->b = ab2float3(wLab[2]); -} - - -void LCMSEXPORT cmsLabEncoded2Float4(LPcmsCIELab Lab, const WORD wLab[3]) +void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3]) { Lab->L = L2float4(wLab[0]); Lab->a = ab2float4(wLab[1]); @@ -435,122 +260,147 @@ } static -double Clamp_L_double(double L) +cmsFloat64Number Clamp_L_doubleV2(cmsFloat64Number L) { + const cmsFloat64Number L_max = (cmsFloat64Number) (0xFFFF * 100.0) / 0xFF00; + if (L < 0) L = 0; - if (L > 100) L = 100; + if (L > L_max) L = L_max; return L; } static -double Clamp_ab_double(double ab) +cmsFloat64Number Clamp_ab_doubleV2(cmsFloat64Number ab) { - if (ab < -128) ab = -128.0; - if (ab > +127.9961) ab = +127.9961; + if (ab < MIN_ENCODEABLE_ab2) ab = MIN_ENCODEABLE_ab2; + if (ab > MAX_ENCODEABLE_ab2) ab = MAX_ENCODEABLE_ab2; return ab; } -void LCMSEXPORT cmsFloat2LabEncoded(WORD wLab[3], const cmsCIELab* fLab) +void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* fLab) { cmsCIELab Lab; + Lab.L = Clamp_L_doubleV2(fLab ->L); + Lab.a = Clamp_ab_doubleV2(fLab ->a); + Lab.b = Clamp_ab_doubleV2(fLab ->b); - Lab.L = Clamp_L_double(fLab ->L); - Lab.a = Clamp_ab_double(fLab ->a); - Lab.b = Clamp_ab_double(fLab ->b); - - wLab[0] = L2Fix3(Lab.L); - wLab[1] = ab2Fix3(Lab.a); - wLab[2] = ab2Fix3(Lab.b); + wLab[0] = L2Fix2(Lab.L); + wLab[1] = ab2Fix2(Lab.a); + wLab[2] = ab2Fix2(Lab.b); } -void LCMSEXPORT cmsFloat2LabEncoded4(WORD wLab[3], const cmsCIELab* fLab) +static +cmsFloat64Number Clamp_L_doubleV4(cmsFloat64Number L) +{ + if (L < 0) L = 0; + if (L > 100.0) L = 100.0; + + return L; +} + +static +cmsFloat64Number Clamp_ab_doubleV4(cmsFloat64Number ab) +{ + if (ab < MIN_ENCODEABLE_ab4) ab = MIN_ENCODEABLE_ab4; + if (ab > MAX_ENCODEABLE_ab4) ab = MAX_ENCODEABLE_ab4; + + return ab; +} + +static +cmsUInt16Number L2Fix4(cmsFloat64Number L) +{ + return _cmsQuickSaturateWord(L * 655.35); +} + +static +cmsUInt16Number ab2Fix4(cmsFloat64Number ab) +{ + return _cmsQuickSaturateWord((ab + 128.0) * 257.0); +} + +void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* fLab) { cmsCIELab Lab; - - Lab.L = fLab ->L; - Lab.a = fLab ->a; - Lab.b = fLab ->b; - - - if (Lab.L < 0) Lab.L = 0; - if (Lab.L > 100.) Lab.L = 100.; - - if (Lab.a < -128.) Lab.a = -128.; - if (Lab.a > 127.) Lab.a = 127.; - if (Lab.b < -128.) Lab.b = -128.; - if (Lab.b > 127.) Lab.b = 127.; - + Lab.L = Clamp_L_doubleV4(fLab ->L); + Lab.a = Clamp_ab_doubleV4(fLab ->a); + Lab.b = Clamp_ab_doubleV4(fLab ->b); wLab[0] = L2Fix4(Lab.L); wLab[1] = ab2Fix4(Lab.a); wLab[2] = ab2Fix4(Lab.b); } - +// Auxiliary: convert to Radians +static +cmsFloat64Number RADIANS(cmsFloat64Number deg) +{ + return (deg * M_PI) / 180.; +} -void LCMSEXPORT cmsLab2LCh(LPcmsCIELCh LCh, const cmsCIELab* Lab) +// Auxiliary: atan2 but operating in degrees and returning 0 if a==b==0 +static +cmsFloat64Number atan2deg(cmsFloat64Number a, cmsFloat64Number b) { - double a, b; - - LCh -> L = Clamp_L_double(Lab -> L); - - a = Clamp_ab_double(Lab -> a); - b = Clamp_ab_double(Lab -> b); - - LCh -> C = pow(a * a + b * b, 0.5); + cmsFloat64Number h; - if (a == 0 && b == 0) - LCh -> h = 0; + if (a == 0 && b == 0) + h = 0; else - LCh -> h = atan2(b, a); + h = atan2(a, b); - - LCh -> h *= (180. / M_PI); + h *= (180. / M_PI); - - while (LCh -> h >= 360.) // Not necessary, but included as a check. - LCh -> h -= 360.; + while (h > 360.) + h -= 360.; - while (LCh -> h < 0) - LCh -> h += 360.; + while ( h < 0) + h += 360.; + return h; } - - -void LCMSEXPORT cmsLCh2Lab(LPcmsCIELab Lab, const cmsCIELCh* LCh) +// Auxiliary: Square +static +cmsFloat64Number Sqr(cmsFloat64Number v) { - - double h = (LCh -> h * M_PI) / 180.0; - - Lab -> L = Clamp_L_double(LCh -> L); - Lab -> a = Clamp_ab_double(LCh -> C * cos(h)); - Lab -> b = Clamp_ab_double(LCh -> C * sin(h)); - + return v * v; +} +// From cylindrical coordinates. No check is performed, then negative values are allowed +void CMSEXPORT cmsLab2LCh(cmsCIELCh* LCh, const cmsCIELab* Lab) +{ + LCh -> L = Lab -> L; + LCh -> C = pow(Sqr(Lab ->a) + Sqr(Lab ->b), 0.5); + LCh -> h = atan2deg(Lab ->b, Lab ->a); } +// To cylindrical coordinates. No check is performed, then negative values are allowed +void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh) +{ + cmsFloat64Number h = (LCh -> h * M_PI) / 180.0; - + Lab -> L = LCh -> L; + Lab -> a = LCh -> C * cos(h); + Lab -> b = LCh -> C * sin(h); +} // In XYZ All 3 components are encoded using 1.15 fixed point - static -WORD XYZ2Fix(double d) +cmsUInt16Number XYZ2Fix(cmsFloat64Number d) { - return (WORD) floor(d * 32768.0 + 0.5); + return _cmsQuickSaturateWord(d * 32768.0); } - -void LCMSEXPORT cmsFloat2XYZEncoded(WORD XYZ[3], const cmsCIEXYZ* fXYZ) +void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ) { cmsCIEXYZ xyz; @@ -558,73 +408,562 @@ xyz.Y = fXYZ -> Y; xyz.Z = fXYZ -> Z; - // Clamp to encodeable values. - // 1.99997 is reserved as out-of-gamut marker - - if (xyz.Y <= 0) { - xyz.X = 0; - xyz.Y = 0; - xyz.Z = 0; + xyz.X = 0; + xyz.Y = 0; + xyz.Z = 0; } - - if (xyz.X > 1.99996) - xyz.X = 1.99996; + if (xyz.X > MAX_ENCODEABLE_XYZ) + xyz.X = MAX_ENCODEABLE_XYZ; if (xyz.X < 0) - xyz.X = 0; + xyz.X = 0; - if (xyz.Y > 1.99996) - xyz.Y = 1.99996; + if (xyz.Y > MAX_ENCODEABLE_XYZ) + xyz.Y = MAX_ENCODEABLE_XYZ; if (xyz.Y < 0) - xyz.Y = 0; - + xyz.Y = 0; - if (xyz.Z > 1.99996) - xyz.Z = 1.99996; + if (xyz.Z > MAX_ENCODEABLE_XYZ) + xyz.Z = MAX_ENCODEABLE_XYZ; if (xyz.Z < 0) - xyz.Z = 0; - + xyz.Z = 0; XYZ[0] = XYZ2Fix(xyz.X); XYZ[1] = XYZ2Fix(xyz.Y); XYZ[2] = XYZ2Fix(xyz.Z); +} + +// To convert from Fixed 1.15 point to cmsFloat64Number +static +cmsFloat64Number XYZ2float(cmsUInt16Number v) +{ + cmsS15Fixed16Number fix32; + + // From 1.15 to 15.16 + fix32 = v << 1; + + // From fixed 15.16 to cmsFloat64Number + return _cms15Fixed16toDouble(fix32); +} + + +void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fXYZ, const cmsUInt16Number XYZ[3]) +{ + fXYZ -> X = XYZ2float(XYZ[0]); + fXYZ -> Y = XYZ2float(XYZ[1]); + fXYZ -> Z = XYZ2float(XYZ[2]); +} + + +// Returns dE on two Lab values +cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsFloat64Number dL, da, db; + + dL = fabs(Lab1 -> L - Lab2 -> L); + da = fabs(Lab1 -> a - Lab2 -> a); + db = fabs(Lab1 -> b - Lab2 -> b); + + return pow(Sqr(dL) + Sqr(da) + Sqr(db), 0.5); } -// To convert from Fixed 1.15 point to double +// Return the CIE94 Delta E +cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsCIELCh LCh1, LCh2; + cmsFloat64Number dE, dL, dC, dh, dhsq; + cmsFloat64Number c12, sc, sh; -static -double XYZ2float(WORD v) -{ - Fixed32 fix32; + dL = fabs(Lab1 ->L - Lab2 ->L); + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); - // From 1.15 to 15.16 - - fix32 = v << 1; + dC = fabs(LCh1.C - LCh2.C); + dE = cmsDeltaE(Lab1, Lab2); - // From fixed 15.16 to double + dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC); + if (dhsq < 0) + dh = 0; + else + dh = pow(dhsq, 0.5); - return FIXED_TO_DOUBLE(fix32); + c12 = sqrt(LCh1.C * LCh2.C); + + sc = 1.0 + (0.048 * c12); + sh = 1.0 + (0.014 * c12); + + return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh)); } -void LCMSEXPORT cmsXYZEncoded2Float(LPcmsCIEXYZ fXYZ, const WORD XYZ[3]) +// Auxiliary +static +cmsFloat64Number ComputeLBFD(const cmsCIELab* Lab) { + cmsFloat64Number yt; - fXYZ -> X = XYZ2float(XYZ[0]); - fXYZ -> Y = XYZ2float(XYZ[1]); - fXYZ -> Z = XYZ2float(XYZ[2]); + if (Lab->L > 7.996969) + yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100; + else + yt = 100 * (Lab->L / 903.3); + return (54.6 * (M_LOG10E * (log(yt + 1.5))) - 9.6); } +// bfd - gets BFD(1:1) difference between Lab1, Lab2 +cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsFloat64Number lbfd1,lbfd2,AveC,Aveh,dE,deltaL, + deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd; + cmsCIELCh LCh1, LCh2; + + lbfd1 = ComputeLBFD(Lab1); + lbfd2 = ComputeLBFD(Lab2); + deltaL = lbfd2 - lbfd1; + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); + + deltaC = LCh2.C - LCh1.C; + AveC = (LCh1.C+LCh2.C)/2; + Aveh = (LCh1.h+LCh2.h)/2; + + dE = cmsDeltaE(Lab1, Lab2); + + if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC))) + deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC)); + else + deltah =0; + + + dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521; + g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000)); + t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))- + 0.040*cos((2*Aveh-136)/(180/M_PI))+ + 0.070*cos((3*Aveh-31)/(180/M_PI))+ + 0.049*cos((4*Aveh+114)/(180/M_PI))- + 0.015*cos((5*Aveh-103)/(180/M_PI))); + + dh = dc*(g*t+1-g); + rh = -0.260*cos((Aveh-308)/(180/M_PI))- + 0.379*cos((2*Aveh-160)/(180/M_PI))- + 0.636*cos((3*Aveh+254)/(180/M_PI))+ + 0.226*cos((4*Aveh+140)/(180/M_PI))- + 0.194*cos((5*Aveh+280)/(180/M_PI)); + + rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000)); + rt = rh*rc; + + bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh))); + + return bfd; +} + + +// cmc - CMC(l:c) difference between Lab1, Lab2 +cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c) +{ + cmsFloat64Number dE,dL,dC,dh,sl,sc,sh,t,f,cmc; + cmsCIELCh LCh1, LCh2; + + if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0; + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); + + + dL = Lab2->L-Lab1->L; + dC = LCh2.C-LCh1.C; + + dE = cmsDeltaE(Lab1, Lab2); + + if (Sqr(dE)>(Sqr(dL)+Sqr(dC))) + dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC)); + else + dh =0; + + if ((LCh1.h > 164) && (LCh1.h < 345)) + t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI)))); + else + t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI)))); + + sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638; + sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L); + + if (Lab1->L<16) + sl = 0.511; + + f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900)); + sh = sc*(t*f+1-f); + cmc = sqrt(Sqr(dL/(l*sl))+Sqr(dC/(c*sc))+Sqr(dh/sh)); + + return cmc; +} + +// dE2000 The weightings KL, KC and KH can be modified to reflect the relative +// importance of lightness, chroma and hue in different industrial applications +cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, + cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh) +{ + cmsFloat64Number L1 = Lab1->L; + cmsFloat64Number a1 = Lab1->a; + cmsFloat64Number b1 = Lab1->b; + cmsFloat64Number C = sqrt( Sqr(a1) + Sqr(b1) ); + + cmsFloat64Number Ls = Lab2 ->L; + cmsFloat64Number as = Lab2 ->a; + cmsFloat64Number bs = Lab2 ->b; + cmsFloat64Number Cs = sqrt( Sqr(as) + Sqr(bs) ); + + cmsFloat64Number G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) )); + + cmsFloat64Number a_p = (1 + G ) * a1; + cmsFloat64Number b_p = b1; + cmsFloat64Number C_p = sqrt( Sqr(a_p) + Sqr(b_p)); + cmsFloat64Number h_p = atan2deg(b_p, a_p); + + + cmsFloat64Number a_ps = (1 + G) * as; + cmsFloat64Number b_ps = bs; + cmsFloat64Number C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps)); + cmsFloat64Number h_ps = atan2deg(b_ps, a_ps); + + cmsFloat64Number meanC_p =(C_p + C_ps) / 2; + + cmsFloat64Number hps_plus_hp = h_ps + h_p; + cmsFloat64Number hps_minus_hp = h_ps - h_p; + + cmsFloat64Number meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 : + (hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 : + (hps_plus_hp - 360)/2; + + cmsFloat64Number delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) : + (hps_minus_hp) > 180 ? (hps_minus_hp - 360) : + (hps_minus_hp); + cmsFloat64Number delta_L = (Ls - L1); + cmsFloat64Number delta_C = (C_ps - C_p ); + + + cmsFloat64Number delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANS(delta_h) / 2); + + cmsFloat64Number T = 1 - 0.17 * cos(RADIANS(meanh_p-30)) + + 0.24 * cos(RADIANS(2*meanh_p)) + + 0.32 * cos(RADIANS(3*meanh_p + 6)) + - 0.2 * cos(RADIANS(4*meanh_p - 63)); + + cmsFloat64Number Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) ); + + cmsFloat64Number Sc = 1 + 0.045 * (C_p + C_ps)/2; + cmsFloat64Number Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T; + + cmsFloat64Number delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25))); + + cmsFloat64Number Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0))); + + cmsFloat64Number Rt = -sin(2 * RADIANS(delta_ro)) * Rc; + + cmsFloat64Number deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) + + Sqr(delta_C/(Sc * Kc)) + + Sqr(delta_H/(Sh * Kh)) + + Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh))); + + return deltaE00; +} + +// This function returns a number of gridpoints to be used as LUT table. It assumes same number +// of gripdpoints in all dimensions. Flags may override the choice. +cmsUInt32Number _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags) +{ + cmsUInt32Number nChannels; + + // Already specified? + if (dwFlags & 0x00FF0000) { + // Yes, grab'em + return (dwFlags >> 16) & 0xFF; + } + + nChannels = cmsChannelsOf(Colorspace); + + // HighResPrecalc is maximum resolution + if (dwFlags & cmsFLAGS_HIGHRESPRECALC) { + + if (nChannels > 4) + return 7; // 7 for Hifi + + if (nChannels == 4) // 23 for CMYK + return 23; + + return 49; // 49 for RGB and others + } + + + // LowResPrecal is lower resolution + if (dwFlags & cmsFLAGS_LOWRESPRECALC) { + + if (nChannels > 4) + return 6; // 6 for more than 4 channels + + if (nChannels == 1) + return 33; // For monochrome + + return 17; // 17 for remaining + } + + // Default values + if (nChannels > 4) + return 7; // 7 for Hifi + + if (nChannels == 4) + return 17; // 17 for CMYK + + return 33; // 33 for RGB +} + + +cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, + cmsUInt16Number **White, + cmsUInt16Number **Black, + cmsUInt32Number *nOutputs) +{ + // Only most common spaces + + static cmsUInt16Number RGBblack[4] = { 0, 0, 0 }; + static cmsUInt16Number RGBwhite[4] = { 0xffff, 0xffff, 0xffff }; + static cmsUInt16Number CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink + static cmsUInt16Number CMYKwhite[4] = { 0, 0, 0, 0 }; + static cmsUInt16Number LABblack[4] = { 0, 0x8080, 0x8080 }; // V4 Lab encoding + static cmsUInt16Number LABwhite[4] = { 0xFFFF, 0x8080, 0x8080 }; + static cmsUInt16Number CMYblack[4] = { 0xffff, 0xffff, 0xffff }; + static cmsUInt16Number CMYwhite[4] = { 0, 0, 0 }; + static cmsUInt16Number Grayblack[4] = { 0 }; + static cmsUInt16Number GrayWhite[4] = { 0xffff }; + + switch (Space) { + + case cmsSigGrayData: if (White) *White = GrayWhite; + if (Black) *Black = Grayblack; + if (nOutputs) *nOutputs = 1; + return TRUE; + + case cmsSigRgbData: if (White) *White = RGBwhite; + if (Black) *Black = RGBblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + case cmsSigLabData: if (White) *White = LABwhite; + if (Black) *Black = LABblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + case cmsSigCmykData: if (White) *White = CMYKwhite; + if (Black) *Black = CMYKblack; + if (nOutputs) *nOutputs = 4; + return TRUE; + + case cmsSigCmyData: if (White) *White = CMYwhite; + if (Black) *Black = CMYblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + default:; + } + + return FALSE; +} + + + +// Several utilities ------------------------------------------------------- + +// Translate from our colorspace to ICC representation + +cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation) +{ + switch (OurNotation) { + + case 1: + case PT_GRAY: return cmsSigGrayData; + + case 2: + case PT_RGB: return cmsSigRgbData; + + case PT_CMY: return cmsSigCmyData; + case PT_CMYK: return cmsSigCmykData; + case PT_YCbCr:return cmsSigYCbCrData; + case PT_YUV: return cmsSigLuvData; + case PT_XYZ: return cmsSigXYZData; + + case PT_LabV2: + case PT_Lab: return cmsSigLabData; + + case PT_YUVK: return cmsSigLuvKData; + case PT_HSV: return cmsSigHsvData; + case PT_HLS: return cmsSigHlsData; + case PT_Yxy: return cmsSigYxyData; + + case PT_MCH1: return cmsSigMCH1Data; + case PT_MCH2: return cmsSigMCH2Data; + case PT_MCH3: return cmsSigMCH3Data; + case PT_MCH4: return cmsSigMCH4Data; + case PT_MCH5: return cmsSigMCH5Data; + case PT_MCH6: return cmsSigMCH6Data; + case PT_MCH7: return cmsSigMCH7Data; + case PT_MCH8: return cmsSigMCH8Data; + + case PT_MCH9: return cmsSigMCH9Data; + case PT_MCH10: return cmsSigMCHAData; + case PT_MCH11: return cmsSigMCHBData; + case PT_MCH12: return cmsSigMCHCData; + case PT_MCH13: return cmsSigMCHDData; + case PT_MCH14: return cmsSigMCHEData; + case PT_MCH15: return cmsSigMCHFData; + + default: return (cmsColorSpaceSignature) 0; + } +} + + +int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace) +{ + switch (ProfileSpace) { + + case cmsSigGrayData: return PT_GRAY; + case cmsSigRgbData: return PT_RGB; + case cmsSigCmyData: return PT_CMY; + case cmsSigCmykData: return PT_CMYK; + case cmsSigYCbCrData:return PT_YCbCr; + case cmsSigLuvData: return PT_YUV; + case cmsSigXYZData: return PT_XYZ; + case cmsSigLabData: return PT_Lab; + case cmsSigLuvKData: return PT_YUVK; + case cmsSigHsvData: return PT_HSV; + case cmsSigHlsData: return PT_HLS; + case cmsSigYxyData: return PT_Yxy; + + case cmsSig1colorData: + case cmsSigMCH1Data: return PT_MCH1; + + case cmsSig2colorData: + case cmsSigMCH2Data: return PT_MCH2; + + case cmsSig3colorData: + case cmsSigMCH3Data: return PT_MCH3; + + case cmsSig4colorData: + case cmsSigMCH4Data: return PT_MCH4; + + case cmsSig5colorData: + case cmsSigMCH5Data: return PT_MCH5; + + case cmsSig6colorData: + case cmsSigMCH6Data: return PT_MCH6; + + case cmsSigMCH7Data: + case cmsSig7colorData:return PT_MCH7; + + case cmsSigMCH8Data: + case cmsSig8colorData:return PT_MCH8; + + case cmsSigMCH9Data: + case cmsSig9colorData:return PT_MCH9; + + case cmsSigMCHAData: + case cmsSig10colorData:return PT_MCH10; + + case cmsSigMCHBData: + case cmsSig11colorData:return PT_MCH11; + + case cmsSigMCHCData: + case cmsSig12colorData:return PT_MCH12; + + case cmsSigMCHDData: + case cmsSig13colorData:return PT_MCH13; + + case cmsSigMCHEData: + case cmsSig14colorData:return PT_MCH14; + + case cmsSigMCHFData: + case cmsSig15colorData:return PT_MCH15; + + default: return (cmsColorSpaceSignature) 0; + } +} + + +cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace) +{ + switch (ColorSpace) { + + case cmsSigMCH1Data: + case cmsSig1colorData: + case cmsSigGrayData: return 1; + + case cmsSigMCH2Data: + case cmsSig2colorData: return 2; + + case cmsSigXYZData: + case cmsSigLabData: + case cmsSigLuvData: + case cmsSigYCbCrData: + case cmsSigYxyData: + case cmsSigRgbData: + case cmsSigHsvData: + case cmsSigHlsData: + case cmsSigCmyData: + case cmsSigMCH3Data: + case cmsSig3colorData: return 3; + + case cmsSigLuvKData: + case cmsSigCmykData: + case cmsSigMCH4Data: + case cmsSig4colorData: return 4; + + case cmsSigMCH5Data: + case cmsSig5colorData: return 5; + + case cmsSigMCH6Data: + case cmsSig6colorData: return 6; + + case cmsSigMCH7Data: + case cmsSig7colorData: return 7; + + case cmsSigMCH8Data: + case cmsSig8colorData: return 8; + + case cmsSigMCH9Data: + case cmsSig9colorData: return 9; + + case cmsSigMCHAData: + case cmsSig10colorData: return 10; + + case cmsSigMCHBData: + case cmsSig11colorData: return 11; + + case cmsSigMCHCData: + case cmsSig12colorData: return 12; + + case cmsSigMCHDData: + case cmsSig13colorData: return 13; + + case cmsSigMCHEData: + case cmsSig14colorData: return 14; + + case cmsSigMCHFData: + case cmsSig15colorData: return 15; + + default: return 3; + } +} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsplugin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsplugin.c Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,1021 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// ---------------------------------------------------------------------------------- +// Encoding & Decoding support functions +// ---------------------------------------------------------------------------------- + +// Little-Endian to Big-Endian + +// Adjust a word value after being readed/ before being written from/to an ICC profile +cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word) +{ +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pByte = (cmsUInt8Number*) &Word; + cmsUInt8Number tmp; + + tmp = pByte[0]; + pByte[0] = pByte[1]; + pByte[1] = tmp; +#endif + + return Word; +} + + +// Transports to properly encoded values - note that icc profiles does use big endian notation. + +// 1 2 3 4 +// 4 3 2 1 + +cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number DWord) +{ +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord; + cmsUInt8Number temp1; + cmsUInt8Number temp2; + + temp1 = *pByte++; + temp2 = *pByte++; + *(pByte-1) = *pByte; + *pByte++ = temp2; + *(pByte-3) = *pByte; + *pByte = temp1; +#endif + return DWord; +} + +// 1 2 3 4 5 6 7 8 +// 8 7 6 5 4 3 2 1 + +void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord) +{ + +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pIn = (cmsUInt8Number*) QWord; + cmsUInt8Number* pOut = (cmsUInt8Number*) Result; + + _cmsAssert(Result != NULL); + + pOut[7] = pIn[0]; + pOut[6] = pIn[1]; + pOut[5] = pIn[2]; + pOut[4] = pIn[3]; + pOut[3] = pIn[4]; + pOut[2] = pIn[5]; + pOut[1] = pIn[6]; + pOut[0] = pIn[7]; + +#else + _cmsAssert(Result != NULL); + +# ifdef CMS_DONT_USE_INT64 + (*Result)[0] = QWord[0]; + (*Result)[1] = QWord[1]; +# else + *Result = *QWord; +# endif +#endif +} + +// Auxiliary -- read 8, 16 and 32-bit numbers +cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n) +{ + cmsUInt8Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = tmp; + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n) +{ + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = _cmsAdjustEndianess16(tmp); + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + + for (i=0; i < n; i++) { + + if (Array != NULL) { + if (!_cmsReadUInt16Number(io, Array + i)) return FALSE; + } + else { + if (!_cmsReadUInt16Number(io, NULL)) return FALSE; + } + + } + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = _cmsAdjustEndianess32(tmp); + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + return FALSE; + + if (n != NULL) { + + tmp = _cmsAdjustEndianess32(tmp); + *n = *(cmsFloat32Number*)(void*)&tmp; + + // Safeguard which covers against absurd values + if (*n > 1E+20 || *n < -1E+20) return FALSE; + + #if defined(_MSC_VER) && _MSC_VER < 1800 + return TRUE; + #elif defined (__BORLANDC__) + return TRUE; + #else + + // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards) + return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL)); + #endif + } + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n) +{ + cmsUInt64Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1) + return FALSE; + + if (n != NULL) { + + _cmsAdjustEndianess64(n, &tmp); + } + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + return FALSE; + + if (n != NULL) { + *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp)); + } + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ) +{ + cmsEncodedXYZNumber xyz; + + _cmsAssert(io != NULL); + + if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE; + + if (XYZ != NULL) { + + XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X)); + XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y)); + XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z)); + } + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n) +{ + _cmsAssert(io != NULL); + + if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n) +{ + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess16(n); + if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + _cmsAssert(Array != NULL); + + for (i=0; i < n; i++) { + if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE; + } + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess32(n); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = *(cmsUInt32Number*) (void*) &n; + tmp = _cmsAdjustEndianess32(tmp); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n) +{ + cmsUInt64Number tmp; + + _cmsAssert(io != NULL); + + _cmsAdjustEndianess64(&tmp, n); + if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n)); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ) +{ + cmsEncodedXYZNumber xyz; + + _cmsAssert(io != NULL); + _cmsAssert(XYZ != NULL); + + xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X)); + xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y)); + xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z)); + + return io -> Write(io, sizeof(cmsEncodedXYZNumber), &xyz); +} + +// from Fixed point 8.8 to double +cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8) +{ + cmsUInt8Number msb, lsb; + + lsb = (cmsUInt8Number) (fixed8 & 0xff); + msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff); + + return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0)); +} + +cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val) +{ + cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val); + return (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF); +} + +// from Fixed point 15.16 to double +cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32) +{ + cmsFloat64Number floater, sign, mid; + int Whole, FracPart; + + sign = (fix32 < 0 ? -1 : 1); + fix32 = abs(fix32); + + Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff; + FracPart = (cmsUInt16Number)(fix32 & 0xffff); + + mid = (cmsFloat64Number) FracPart / 65536.0; + floater = (cmsFloat64Number) Whole + mid; + + return sign * floater; +} + +// from double to Fixed point 15.16 +cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v) +{ + return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5)); +} + +// Date/Time functions + +void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest) +{ + + _cmsAssert(Dest != NULL); + _cmsAssert(Source != NULL); + + Dest->tm_sec = _cmsAdjustEndianess16(Source->seconds); + Dest->tm_min = _cmsAdjustEndianess16(Source->minutes); + Dest->tm_hour = _cmsAdjustEndianess16(Source->hours); + Dest->tm_mday = _cmsAdjustEndianess16(Source->day); + Dest->tm_mon = _cmsAdjustEndianess16(Source->month) - 1; + Dest->tm_year = _cmsAdjustEndianess16(Source->year) - 1900; + Dest->tm_wday = -1; + Dest->tm_yday = -1; + Dest->tm_isdst = 0; +} + +void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source) +{ + _cmsAssert(Dest != NULL); + _cmsAssert(Source != NULL); + + Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec); + Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min); + Dest->hours = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour); + Dest->day = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday); + Dest->month = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1)); + Dest->year = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900)); +} + +// Read base and return type base +cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io) +{ + _cmsTagBase Base; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1) + return (cmsTagTypeSignature) 0; + + return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig); +} + +// Setup base marker +cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig) +{ + _cmsTagBase Base; + + _cmsAssert(io != NULL); + + Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig); + memset(&Base.reserved, 0, sizeof(Base.reserved)); + return io -> Write(io, sizeof(_cmsTagBase), &Base); +} + +cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io) +{ + cmsUInt8Number Buffer[4]; + cmsUInt32Number NextAligned, At; + cmsUInt32Number BytesToNextAlignedPos; + + _cmsAssert(io != NULL); + + At = io -> Tell(io); + NextAligned = _cmsALIGNLONG(At); + BytesToNextAlignedPos = NextAligned - At; + if (BytesToNextAlignedPos == 0) return TRUE; + if (BytesToNextAlignedPos > 4) return FALSE; + + return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1); +} + +cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io) +{ + cmsUInt8Number Buffer[4]; + cmsUInt32Number NextAligned, At; + cmsUInt32Number BytesToNextAlignedPos; + + _cmsAssert(io != NULL); + + At = io -> Tell(io); + NextAligned = _cmsALIGNLONG(At); + BytesToNextAlignedPos = NextAligned - At; + if (BytesToNextAlignedPos == 0) return TRUE; + if (BytesToNextAlignedPos > 4) return FALSE; + + memset(Buffer, 0, BytesToNextAlignedPos); + return io -> Write(io, BytesToNextAlignedPos, Buffer); +} + + +// To deal with text streams. 2K at most +cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...) +{ + va_list args; + int len; + cmsUInt8Number Buffer[2048]; + cmsBool rc; + + _cmsAssert(io != NULL); + _cmsAssert(frm != NULL); + + va_start(args, frm); + + len = vsnprintf((char*) Buffer, 2047, frm, args); + if (len < 0) { + va_end(args); + return FALSE; // Truncated, which is a fatal error for us + } + + rc = io ->Write(io, (cmsUInt32Number) len, Buffer); + + va_end(args); + + return rc; +} + + +// Plugin memory management ------------------------------------------------------------------------------------------------- + +// Specialized malloc for plug-ins, that is freed upon exit. +void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size) +{ + struct _cmsContext_struct* ctx = _cmsGetContext(ContextID); + + if (ctx ->MemPool == NULL) { + + if (ContextID == NULL) { + + ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024); + if (ctx->MemPool == NULL) return NULL; + } + else { + cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context"); + return NULL; + } + } + + return _cmsSubAlloc(ctx->MemPool, size); +} + + +// Main plug-in dispatcher +cmsBool CMSEXPORT cmsPlugin(void* Plug_in) +{ + return cmsPluginTHR(NULL, Plug_in); +} + +cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) +{ + cmsPluginBase* Plugin; + + for (Plugin = (cmsPluginBase*) Plug_in; + Plugin != NULL; + Plugin = Plugin -> Next) { + + if (Plugin -> Magic != cmsPluginMagicNumber) { + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin"); + return FALSE; + } + + if (Plugin ->ExpectedVersion > LCMS_VERSION) { + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d", + Plugin ->ExpectedVersion, LCMS_VERSION); + return FALSE; + } + + switch (Plugin -> Type) { + + case cmsPluginMemHandlerSig: + if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginInterpolationSig: + if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTagTypeSig: + if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTagSig: + if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginFormattersSig: + if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginRenderingIntentSig: + if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginParametricCurveSig: + if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginMultiProcessElementSig: + if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginOptimizationSig: + if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTransformSig: + if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginMutexSig: + if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE; + break; + + default: + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type); + return FALSE; + } + } + + // Keep a reference to the plug-in + return TRUE; +} + + +// Revert all plug-ins to default +void CMSEXPORT cmsUnregisterPlugins(void) +{ + cmsUnregisterPluginsTHR(NULL); +} + + +// The Global storage for system context. This is the one and only global variable +// pointers structure. All global vars are referenced here. +static struct _cmsContext_struct globalContext = { + + NULL, // Not in the linked list + NULL, // No suballocator + { + NULL, // UserPtr, + &_cmsLogErrorChunk, // Logger, + &_cmsAlarmCodesChunk, // AlarmCodes, + &_cmsAdaptationStateChunk, // AdaptationState, + &_cmsMemPluginChunk, // MemPlugin, + &_cmsInterpPluginChunk, // InterpPlugin, + &_cmsCurvesPluginChunk, // CurvesPlugin, + &_cmsFormattersPluginChunk, // FormattersPlugin, + &_cmsTagTypePluginChunk, // TagTypePlugin, + &_cmsTagPluginChunk, // TagPlugin, + &_cmsIntentsPluginChunk, // IntentPlugin, + &_cmsMPETypePluginChunk, // MPEPlugin, + &_cmsOptimizationPluginChunk, // OptimizationPlugin, + &_cmsTransformPluginChunk, // TransformPlugin, + &_cmsMutexPluginChunk // MutexPlugin + }, + + { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0 +}; + + +// The context pool (linked list head) +static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER; +static struct _cmsContext_struct* _cmsContextPoolHead = NULL; + +// Internal, get associated pointer, with guessing. Never returns NULL. +struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID) +{ + struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID; + struct _cmsContext_struct* ctx; + + + // On 0, use global settings + if (id == NULL) + return &globalContext; + + // Search + for (ctx = _cmsContextPoolHead; + ctx != NULL; + ctx = ctx ->Next) { + + // Found it? + if (id == ctx) + return ctx; // New-style context, + } + + return &globalContext; +} + + +// Internal: get the memory area associanted with each context client +// Returns the block assigned to the specific zone. Never return NULL. +void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc) +{ + struct _cmsContext_struct* ctx; + void *ptr; + + if ((int) mc < 0 || mc >= MemoryClientMax) { + + cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption"); + + // This is catastrophic. Should never reach here + _cmsAssert(0); + + // Reverts to global context + return globalContext.chunks[UserPtr]; + } + + ctx = _cmsGetContext(ContextID); + ptr = ctx ->chunks[mc]; + + if (ptr != NULL) + return ptr; + + // A null ptr means no special settings for that context, and this + // reverts to Context0 globals + return globalContext.chunks[mc]; +} + + +// This function returns the given context its default pristine state, +// as no plug-ins were declared. There is no way to unregister a single +// plug-in, as a single call to cmsPluginTHR() function may register +// many different plug-ins simultaneously, then there is no way to +// identify which plug-in to unregister. +void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID) +{ + _cmsRegisterMemHandlerPlugin(ContextID, NULL); + _cmsRegisterInterpPlugin(ContextID, NULL); + _cmsRegisterTagTypePlugin(ContextID, NULL); + _cmsRegisterTagPlugin(ContextID, NULL); + _cmsRegisterFormattersPlugin(ContextID, NULL); + _cmsRegisterRenderingIntentPlugin(ContextID, NULL); + _cmsRegisterParametricCurvesPlugin(ContextID, NULL); + _cmsRegisterMultiProcessElementPlugin(ContextID, NULL); + _cmsRegisterOptimizationPlugin(ContextID, NULL); + _cmsRegisterTransformPlugin(ContextID, NULL); + _cmsRegisterMutexPlugin(ContextID, NULL); +} + + +// Returns the memory manager plug-in, if any, from the Plug-in bundle +static +cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle) +{ + cmsPluginBase* Plugin; + + for (Plugin = (cmsPluginBase*) PluginBundle; + Plugin != NULL; + Plugin = Plugin -> Next) { + + if (Plugin -> Magic == cmsPluginMagicNumber && + Plugin -> ExpectedVersion <= LCMS_VERSION && + Plugin -> Type == cmsPluginMemHandlerSig) { + + // Found! + return (cmsPluginMemHandler*) Plugin; + } + } + + // Nope, revert to defaults + return NULL; +} + + +// Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined +// data that will be forwarded to plug-ins and logger. +cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData) +{ + struct _cmsContext_struct* ctx; + struct _cmsContext_struct fakeContext; + + // See the comments regarding locking in lcms2_internal.h + // for an explanation of why we need the following code. +#ifdef CMS_IS_WINDOWS_ +#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT + { + static HANDLE _cmsWindowsInitMutex = NULL; + static volatile HANDLE* mutex = &_cmsWindowsInitMutex; + + if (*mutex == NULL) + { + HANDLE p = CreateMutex(NULL, FALSE, NULL); + if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL) + CloseHandle(p); + } + if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED) + return NULL; + if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL) + InitializeCriticalSection(&_cmsContextPoolHeadMutex); + if (*mutex == NULL || !ReleaseMutex(*mutex)) + return NULL; + } +#endif +#endif + + _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager); + + fakeContext.chunks[UserPtr] = UserData; + fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager; + + // Create the context structure. + ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct)); + if (ctx == NULL) + return NULL; // Something very wrong happened! + + // Init the structure and the memory manager + memset(ctx, 0, sizeof(struct _cmsContext_struct)); + + // Keep memory manager + memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk)); + + // Maintain the linked list (with proper locking) + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + ctx ->Next = _cmsContextPoolHead; + _cmsContextPoolHead = ctx; + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + ctx ->chunks[UserPtr] = UserData; + ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager; + + // Now we can allocate the pool by using default memory manager + ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*)); // default size about 22 pointers + if (ctx ->MemPool == NULL) { + + cmsDeleteContext(ctx); + return NULL; + } + + _cmsAllocLogErrorChunk(ctx, NULL); + _cmsAllocAlarmCodesChunk(ctx, NULL); + _cmsAllocAdaptationStateChunk(ctx, NULL); + _cmsAllocMemPluginChunk(ctx, NULL); + _cmsAllocInterpPluginChunk(ctx, NULL); + _cmsAllocCurvesPluginChunk(ctx, NULL); + _cmsAllocFormattersPluginChunk(ctx, NULL); + _cmsAllocTagTypePluginChunk(ctx, NULL); + _cmsAllocMPETypePluginChunk(ctx, NULL); + _cmsAllocTagPluginChunk(ctx, NULL); + _cmsAllocIntentsPluginChunk(ctx, NULL); + _cmsAllocOptimizationPluginChunk(ctx, NULL); + _cmsAllocTransformPluginChunk(ctx, NULL); + _cmsAllocMutexPluginChunk(ctx, NULL); + + // Setup the plug-ins + if (!cmsPluginTHR(ctx, Plugin)) { + + cmsDeleteContext(ctx); + return NULL; + } + + return (cmsContext) ctx; +} + +// Duplicates a context with all associated plug-ins. +// Caller may specify an optional pointer to user-defined +// data that will be forwarded to plug-ins and logger. +cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData) +{ + int i; + struct _cmsContext_struct* ctx; + const struct _cmsContext_struct* src = _cmsGetContext(ContextID); + + void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr]; + + + ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct)); + if (ctx == NULL) + return NULL; // Something very wrong happened + + // Setup default memory allocators + memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); + + // Maintain the linked list + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + ctx ->Next = _cmsContextPoolHead; + _cmsContextPoolHead = ctx; + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + ctx ->chunks[UserPtr] = userData; + ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager; + + ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*)); + if (ctx ->MemPool == NULL) { + + cmsDeleteContext(ctx); + return NULL; + } + + // Allocate all required chunks. + _cmsAllocLogErrorChunk(ctx, src); + _cmsAllocAlarmCodesChunk(ctx, src); + _cmsAllocAdaptationStateChunk(ctx, src); + _cmsAllocMemPluginChunk(ctx, src); + _cmsAllocInterpPluginChunk(ctx, src); + _cmsAllocCurvesPluginChunk(ctx, src); + _cmsAllocFormattersPluginChunk(ctx, src); + _cmsAllocTagTypePluginChunk(ctx, src); + _cmsAllocMPETypePluginChunk(ctx, src); + _cmsAllocTagPluginChunk(ctx, src); + _cmsAllocIntentsPluginChunk(ctx, src); + _cmsAllocOptimizationPluginChunk(ctx, src); + _cmsAllocTransformPluginChunk(ctx, src); + _cmsAllocMutexPluginChunk(ctx, src); + + // Make sure no one failed + for (i=Logger; i < MemoryClientMax; i++) { + + if (src ->chunks[i] == NULL) { + cmsDeleteContext((cmsContext) ctx); + return NULL; + } + } + + return (cmsContext) ctx; +} + + +/* +static +struct _cmsContext_struct* FindPrev(struct _cmsContext_struct* id) +{ + struct _cmsContext_struct* prev; + + // Search for previous + for (prev = _cmsContextPoolHead; + prev != NULL; + prev = prev ->Next) + { + if (prev ->Next == id) + return prev; + } + + return NULL; // List is empty or only one element! +} +*/ + +// Frees any resources associated with the given context, +// and destroys the context placeholder. +// The ContextID can no longer be used in any THR operation. +void CMSEXPORT cmsDeleteContext(cmsContext ContextID) +{ + if (ContextID != NULL) { + + struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID; + struct _cmsContext_struct fakeContext; + struct _cmsContext_struct* prev; + + memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); + + fakeContext.chunks[UserPtr] = ctx ->chunks[UserPtr]; + fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager; + + // Get rid of plugins + cmsUnregisterPluginsTHR(ContextID); + + // Since all memory is allocated in the private pool, all what we need to do is destroy the pool + if (ctx -> MemPool != NULL) + _cmsSubAllocDestroy(ctx ->MemPool); + ctx -> MemPool = NULL; + + // Maintain list + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + if (_cmsContextPoolHead == ctx) { + + _cmsContextPoolHead = ctx->Next; + } + else { + + // Search for previous + for (prev = _cmsContextPoolHead; + prev != NULL; + prev = prev ->Next) + { + if (prev -> Next == ctx) { + prev -> Next = ctx ->Next; + break; + } + } + } + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + // free the memory block itself + _cmsFree(&fakeContext, ctx); + } +} + +// Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation +void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID) +{ + return _cmsContextGetClientChunk(ContextID, UserPtr); +} + + diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsps2.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsps2.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsps2.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,22 +49,14 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -// Postscript level 2 operators - +// +//--------------------------------------------------------------------------------- +// - -#include "lcms.h" -#include -#include +#include "lcms2_internal.h" // PostScript ColorRenderingDictionary and ColorSpaceArray -LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen); -LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen); -LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile, int Intent, DWORD dwFlags, LPVOID Buffer, DWORD dwBufferLen); -// -------------------------------------------------------------------- Implementation #define MAXPSCOLS 60 // Columns on tables @@ -83,9 +76,9 @@ Color Space Arrays (CSA) ================================================================================== - In order to obtain precission, code chooses between three ways to implement + In order to obtain precision, code chooses between three ways to implement the device -> XYZ transform. These cases identifies monochrome profiles (often - implemented as a set of curves), matrix-shaper and LUT-based. + implemented as a set of curves), matrix-shaper and Pipeline-based. Monochrome ----------- @@ -101,7 +94,7 @@ << /DecodeA { transfer function } bind /MatrixA [D50] - /RangeLMN [ 0.0 D50X 0.0 D50Y 0.0 D50Z ] + /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] /WhitePoint [D50] /BlackPoint [BP] /RenderingIntent (intent) @@ -115,7 +108,7 @@ ------------------- This is implemented both with /CIEBasedABC or /CIEBasedDEF on dependig - of profile implementation. Since here is no interpolation tables, I do + of profile implementation. Since here there are no interpolation tables, I do the conversion directly to XYZ @@ -124,7 +117,7 @@ << /DecodeABC [ {transfer1} {transfer2} {transfer3} ] /MatrixABC [Matrix] - /RangeLMN [ 0.0 D50X 0.0 D50Y 0.0 D50Z ] + /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] /DecodeLMN [ { / 2} dup dup ] /WhitePoint [D50] /BlackPoint [BP] @@ -299,153 +292,66 @@ */ -static icTagSignature Device2PCSTab[] = {icSigAToB0Tag, // Perceptual - icSigAToB1Tag, // Relative colorimetric - icSigAToB2Tag, // Saturation - icSigAToB1Tag }; // Absolute colorimetric - // (Relative/WhitePoint) - -// --------------------------------------------------------------- Memory Stream -// // This struct holds the memory block currently being write -// +typedef struct { + _cmsStageCLutData* Pipeline; + cmsIOHANDLER* m; + + int FirstComponent; + int SecondComponent; -typedef struct { - LPBYTE Block; - LPBYTE Ptr; - DWORD dwMax; - DWORD dwUsed; - int MaxCols; - int Col; - int HasError; + const char* PreMaj; + const char* PostMaj; + const char* PreMin; + const char* PostMin; - } MEMSTREAM, FAR* LPMEMSTREAM; + int FixWhite; // Force mapping of pure white + + cmsColorSpaceSignature ColorSpace; // ColorSpace of profile -typedef struct { - LPLUT Lut; - LPMEMSTREAM m; - - int FirstComponent; - int SecondComponent; - - int bps; - const char* PreMaj; - const char* PostMaj; - const char* PreMin; - const char* PostMin; - - int lIsInput; // Handle L* encoding - int FixWhite; // Force mapping of pure white - - icColorSpaceSignature ColorSpace; // ColorSpace of profile - - - } SAMPLERCARGO, FAR* LPSAMPLERCARGO; +} cmsPsSamplerCargo; - -// Creates a ready to use memory stream -static -LPMEMSTREAM CreateMemStream(LPBYTE Buffer, DWORD dwMax, int MaxCols) -{ - LPMEMSTREAM m = (LPMEMSTREAM) _cmsMalloc(sizeof(MEMSTREAM)); - if (m == NULL) return NULL; - - ZeroMemory(m, sizeof(MEMSTREAM)); - - m -> Block = m -> Ptr = Buffer; - m -> dwMax = dwMax; - m -> dwUsed = 0; - m -> MaxCols = MaxCols; - m -> Col = 0; - m -> HasError = 0; - - return m; -} - +static int _cmsPSActualColumn = 0; // Convert to byte static -BYTE Word2Byte(WORD w) +cmsUInt8Number Word2Byte(cmsUInt16Number w) { - return (BYTE) floor((double) w / 257.0 + 0.5); + return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5); } // Convert to byte (using ICC2 notation) - +/* static -BYTE L2Byte(WORD w) +cmsUInt8Number L2Byte(cmsUInt16Number w) { int ww = w + 0x0080; if (ww > 0xFFFF) return 0xFF; - return (BYTE) ((WORD) (ww >> 8) & 0xFF); + return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF); } - -// Write a raw, uncooked byte. Check for space -static -void WriteRawByte(LPMEMSTREAM m, BYTE b) -{ - if (m -> dwUsed + 1 > m -> dwMax) { - m -> HasError = 1; - } - - if (!m ->HasError && m ->Block) { - *m ->Ptr++ = b; - } - - m -> dwUsed++; -} +*/ // Write a cooked byte + static -void WriteByte(LPMEMSTREAM m, BYTE b) +void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b) { - static const BYTE Hex[] = "0123456789ABCDEF"; - BYTE c; - - c = Hex[(b >> 4) & 0x0f]; - WriteRawByte(m, c); - - c = Hex[b & 0x0f]; - WriteRawByte(m, c); - - m -> Col += 2; - - if (m -> Col > m -> MaxCols) { - - WriteRawByte(m, '\n'); - m -> Col = 0; - } - -} + _cmsIOPrintf(m, "%02x", b); + _cmsPSActualColumn += 2; -// Does write a formatted string. Guaranteed to be 2048 bytes at most. -static -void Writef(LPMEMSTREAM m, const char *frm, ...) -{ - va_list args; - LPBYTE pt; - BYTE Buffer[2048]; - - va_start(args, frm); + if (_cmsPSActualColumn > MAXPSCOLS) { - vsnprintf((char*) Buffer, 2048, frm, args); - - for (pt = Buffer; *pt; pt++) { - - WriteRawByte(m, *pt); - } - - va_end(args); + _cmsIOPrintf(m, "\n"); + _cmsPSActualColumn = 0; + } } - - // ----------------------------------------------------------------- PostScript generation @@ -466,21 +372,31 @@ } static -void EmitHeader(LPMEMSTREAM m, const char* Title, cmsHPROFILE hProfile) +void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile) { - time_t timer; + cmsMLU *Description, *Copyright; + char DescASCII[256], CopyrightASCII[256]; time(&timer); - Writef(m, "%%!PS-Adobe-3.0\n"); - Writef(m, "%%\n"); - Writef(m, "%% %s\n", Title); - Writef(m, "%% Source: %s\n", RemoveCR(cmsTakeProductName(hProfile))); - Writef(m, "%% Description: %s\n", RemoveCR(cmsTakeProductDesc(hProfile))); - Writef(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!! - Writef(m, "%%\n"); - Writef(m, "%%%%BeginResource\n"); + Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag); + Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag); + + DescASCII[0] = DescASCII[255] = 0; + CopyrightASCII[0] = CopyrightASCII[255] = 0; + + if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255); + if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255); + + _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n"); + _cmsIOPrintf(m, "%%\n"); + _cmsIOPrintf(m, "%% %s\n", Title); + _cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII)); + _cmsIOPrintf(m, "%% %s\n", RemoveCR(CopyrightASCII)); + _cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!! + _cmsIOPrintf(m, "%%\n"); + _cmsIOPrintf(m, "%%%%BeginResource\n"); } @@ -489,31 +405,31 @@ // Black point adapted to D50. static -void EmitWhiteBlackD50(LPMEMSTREAM m, LPcmsCIEXYZ BlackPoint) +void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint) { - Writef(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X, + _cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X, BlackPoint -> Y, BlackPoint -> Z); - Writef(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X, + _cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X, cmsD50_XYZ()->Y, cmsD50_XYZ()->Z); } static -void EmitRangeCheck(LPMEMSTREAM m) +void EmitRangeCheck(cmsIOHANDLER* m) { - Writef(m, "dup 0.0 lt { pop 0.0 } if " - "dup 1.0 gt { pop 1.0 } if "); + _cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if " + "dup 1.0 gt { pop 1.0 } if "); } // Does write the intent static -void EmitIntent(LPMEMSTREAM m, int RenderingIntent) +void EmitIntent(cmsIOHANDLER* m, cmsUInt32Number RenderingIntent) { const char *intent; @@ -527,7 +443,7 @@ default: intent = "Undefined"; break; } - Writef(m, "/RenderingIntent (%s)\n", intent ); + _cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent ); } // @@ -539,9 +455,9 @@ /* static -void EmitL2Y(LPMEMSTREAM m) +void EmitL2Y(cmsIOHANDLER* m) { - Writef(m, + _cmsIOPrintf(m, "{ " "100 mul 16 add 116 div " // (L * 100 + 16) / 116 "dup 6 29 div ge " // >= 6 / 29 ? @@ -555,21 +471,21 @@ // Lab -> XYZ, see the discussion above static -void EmitLab2XYZ(LPMEMSTREAM m) +void EmitLab2XYZ(cmsIOHANDLER* m) { - Writef(m, "/RangeABC [ 0 1 0 1 0 1]\n"); - Writef(m, "/DecodeABC [\n"); - Writef(m, "{100 mul 16 add 116 div } bind\n"); - Writef(m, "{255 mul 128 sub 500 div } bind\n"); - Writef(m, "{255 mul 128 sub 200 div } bind\n"); - Writef(m, "]\n"); - Writef(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n"); - Writef(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n"); - Writef(m, "/DecodeLMN [\n"); - Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n"); - Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n"); - Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n"); - Writef(m, "]\n"); + _cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n"); + _cmsIOPrintf(m, "/DecodeABC [\n"); + _cmsIOPrintf(m, "{100 mul 16 add 116 div } bind\n"); + _cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n"); + _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n"); + _cmsIOPrintf(m, "]\n"); + _cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n"); + _cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n"); + _cmsIOPrintf(m, "/DecodeLMN [\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n"); + _cmsIOPrintf(m, "]\n"); } @@ -577,29 +493,26 @@ // Outputs a table of words. It does use 16 bits static -void Emit1Gamma(LPMEMSTREAM m, LPWORD Table, int nEntries) +void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table) { - int i; - double gamma; + cmsUInt32Number i; + cmsFloat64Number gamma; + if (Table == NULL) return; // Error - if (nEntries <= 0) return; // Empty table + if (Table ->nEntries <= 0) return; // Empty table // Suppress whole if identity - if (cmsIsLinear(Table, nEntries)) { - Writef(m, "{} "); - return; - } - + if (cmsIsToneCurveLinear(Table)) return; // Check if is really an exponential. If so, emit "exp" - gamma = cmsEstimateGammaEx(Table, nEntries, 0.001); + gamma = cmsEstimateGamma(Table, 0.001); if (gamma > 0) { - Writef(m, "{ %g exp } bind ", gamma); + _cmsIOPrintf(m, "{ %g exp } bind ", gamma); return; } - Writef(m, "{ "); + _cmsIOPrintf(m, "{ "); // Bounds check EmitRangeCheck(m); @@ -609,94 +522,76 @@ // PostScript code Stack // =============== ======================== // v - Writef(m, " ["); + _cmsIOPrintf(m, " ["); - // TODO: Check for endianess!!! - - for (i=0; i < nEntries; i++) { - Writef(m, "%d ", Table[i]); + for (i=0; i < Table->nEntries; i++) { + _cmsIOPrintf(m, "%d ", Table->Table16[i]); } - Writef(m, "] "); // v tab + _cmsIOPrintf(m, "] "); // v tab - Writef(m, "dup "); // v tab tab - Writef(m, "length 1 sub "); // v tab dom - Writef(m, "3 -1 roll "); // tab dom v - Writef(m, "mul "); // tab val2 - Writef(m, "dup "); // tab val2 val2 - Writef(m, "dup "); // tab val2 val2 val2 - Writef(m, "floor cvi "); // tab val2 val2 cell0 - Writef(m, "exch "); // tab val2 cell0 val2 - Writef(m, "ceiling cvi "); // tab val2 cell0 cell1 - Writef(m, "3 index "); // tab val2 cell0 cell1 tab - Writef(m, "exch "); // tab val2 cell0 tab cell1 - Writef(m, "get "); // tab val2 cell0 y1 - Writef(m, "4 -1 roll "); // val2 cell0 y1 tab - Writef(m, "3 -1 roll "); // val2 y1 tab cell0 - Writef(m, "get "); // val2 y1 y0 - Writef(m, "dup "); // val2 y1 y0 y0 - Writef(m, "3 1 roll "); // val2 y0 y1 y0 - Writef(m, "sub "); // val2 y0 (y1-y0) - Writef(m, "3 -1 roll "); // y0 (y1-y0) val2 - Writef(m, "dup "); // y0 (y1-y0) val2 val2 - Writef(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2) - Writef(m, "sub "); // y0 (y1-y0) rest - Writef(m, "mul "); // y0 t1 - Writef(m, "add "); // y - Writef(m, "65535 div "); // result + _cmsIOPrintf(m, "dup "); // v tab tab + _cmsIOPrintf(m, "length 1 sub "); // v tab dom + _cmsIOPrintf(m, "3 -1 roll "); // tab dom v + _cmsIOPrintf(m, "mul "); // tab val2 + _cmsIOPrintf(m, "dup "); // tab val2 val2 + _cmsIOPrintf(m, "dup "); // tab val2 val2 val2 + _cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0 + _cmsIOPrintf(m, "exch "); // tab val2 cell0 val2 + _cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1 + _cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab + _cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1 + _cmsIOPrintf(m, "get "); // tab val2 cell0 y1 + _cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab + _cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0 + _cmsIOPrintf(m, "get "); // val2 y1 y0 + _cmsIOPrintf(m, "dup "); // val2 y1 y0 y0 + _cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0 + _cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0) + _cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2 + _cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2 + _cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2) + _cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest + _cmsIOPrintf(m, "mul "); // y0 t1 + _cmsIOPrintf(m, "add "); // y + _cmsIOPrintf(m, "65535 div "); // result - Writef(m, " } bind "); + _cmsIOPrintf(m, " } bind "); } // Compare gamma table static -LCMSBOOL GammaTableEquals(LPWORD g1, LPWORD g2, int nEntries) +cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nEntries) { - return memcmp(g1, g2, nEntries* sizeof(WORD)) == 0; + return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0; } // Does write a set of gamma curves static -void EmitNGamma(LPMEMSTREAM m, int n, LPWORD g[], int nEntries) +void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[]) { - int i; + cmsUInt32Number i; for( i=0; i < n; i++ ) { - if (i > 0 && GammaTableEquals(g[i-1], g[i], nEntries)) { + if (g[i] == NULL) return; // Error - Writef(m, "dup "); + if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) { + + _cmsIOPrintf(m, "dup "); } else { - Emit1Gamma(m, g[i], nEntries); + Emit1Gamma(m, g[i]); } } } -// Check whatever a profile has CLUT tables (only on input) - -static -LCMSBOOL IsLUTbased(cmsHPROFILE hProfile, int Intent) -{ - icTagSignature Tag; - - // Check if adequate tag is present - Tag = Device2PCSTab[Intent]; - - if (cmsIsTag(hProfile, Tag)) return 1; - - // If not present, revert to default (perceptual) - Tag = icSigAToB0Tag; - - // If no tag present, try matrix-shaper - return cmsIsTag(hProfile, Tag); -} @@ -707,19 +602,19 @@ // that is, the callback will be called for each knot with // // In[] The grid location coordinates, normalized to 0..ffff -// Out[] The LUT values, normalized to 0..ffff +// Out[] The Pipeline values, normalized to 0..ffff // // Returning a value other than 0 does terminate the sampling process // -// Each row contains LUT values for all but first component. So, I +// Each row contains Pipeline values for all but first component. So, I // detect row changing by keeping a copy of last value of first -// component. -1 is used to mark begining of whole block. +// component. -1 is used to mark beginning of whole block. static -int OutputValueSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) { - LPSAMPLERCARGO sc = (LPSAMPLERCARGO) Cargo; - unsigned int i; + cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo; + cmsUInt32Number i; if (sc -> FixWhite) { @@ -729,14 +624,14 @@ if ((In[1] >= 0x7800 && In[1] <= 0x8800) && (In[2] >= 0x7800 && In[2] <= 0x8800)) { - WORD* Black; - WORD* White; - int nOutputs; + cmsUInt16Number* Black; + cmsUInt16Number* White; + cmsUInt32Number nOutputs; if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs)) return 0; - for (i=0; i < (unsigned int) nOutputs; i++) + for (i=0; i < nOutputs; i++) Out[i] = White[i]; } @@ -751,15 +646,15 @@ if (sc ->FirstComponent != -1) { - Writef(sc ->m, sc ->PostMin); + _cmsIOPrintf(sc ->m, sc ->PostMin); sc ->SecondComponent = -1; - Writef(sc ->m, sc ->PostMaj); + _cmsIOPrintf(sc ->m, sc ->PostMaj); } // Begin block - sc->m->Col = 0; + _cmsPSActualColumn = 0; - Writef(sc ->m, sc ->PreMaj); + _cmsIOPrintf(sc ->m, sc ->PreMaj); sc ->FirstComponent = In[0]; } @@ -768,96 +663,67 @@ if (sc ->SecondComponent != -1) { - Writef(sc ->m, sc ->PostMin); + _cmsIOPrintf(sc ->m, sc ->PostMin); } - Writef(sc ->m, sc ->PreMin); + _cmsIOPrintf(sc ->m, sc ->PreMin); sc ->SecondComponent = In[1]; } - - - // Dump table. Could be Word or byte based on - // depending on bps member (16 bps mode is not currently - // being used at all, but is here for future ampliations) - - for (i=0; i < sc -> Lut ->OutputChan; i++) { + // Dump table. - WORD wWordOut = Out[i]; - - if (sc ->bps == 8) { + for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) { - // Value as byte - BYTE wByteOut; - - // If is input, convert from Lab2 to Lab4 (just divide by 256) - - if (sc ->lIsInput) { + cmsUInt16Number wWordOut = Out[i]; + cmsUInt8Number wByteOut; // Value as byte - wByteOut = L2Byte(wWordOut); - } - else - wByteOut = Word2Byte(wWordOut); + // We always deal with Lab4 - WriteByte(sc -> m, wByteOut); - } - else { + wByteOut = Word2Byte(wWordOut); + WriteByte(sc -> m, wByteOut); + } - // Value as word - WriteByte(sc -> m, (BYTE) (wWordOut & 0xFF)); - WriteByte(sc -> m, (BYTE) ((wWordOut >> 8) & 0xFF)); - } - } - - return 1; + return 1; } -// Writes a LUT on memstream. Could be 8 or 16 bits based +// Writes a Pipeline on memstream. Could be 8 or 16 bits based static -void WriteCLUT(LPMEMSTREAM m, LPLUT Lut, int bps, const char* PreMaj, - const char* PostMaj, - const char* PreMin, - const char* PostMin, - int lIsInput, - int FixWhite, - icColorSpaceSignature ColorSpace) +void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj, + const char* PostMaj, + const char* PreMin, + const char* PostMin, + int FixWhite, + cmsColorSpaceSignature ColorSpace) { - unsigned int i; - SAMPLERCARGO sc; + cmsUInt32Number i; + cmsPsSamplerCargo sc; sc.FirstComponent = -1; sc.SecondComponent = -1; - sc.Lut = Lut; + sc.Pipeline = (_cmsStageCLutData *) mpe ->Data; sc.m = m; - sc.bps = bps; sc.PreMaj = PreMaj; sc.PostMaj= PostMaj; sc.PreMin = PreMin; sc.PostMin = PostMin; - sc.lIsInput = lIsInput; sc.FixWhite = FixWhite; sc.ColorSpace = ColorSpace; - Writef(m, "["); + _cmsIOPrintf(m, "["); - for (i=0; i < Lut ->InputChan; i++) - Writef(m, " %d ", Lut ->cLutPoints); - - Writef(m, " [\n"); - - + for (i=0; i < sc.Pipeline->Params->nInputs; i++) + _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]); - cmsSample3DGrid(Lut, OutputValueSampler, (LPVOID) &sc, SAMPLER_INSPECT); + _cmsIOPrintf(m, " [\n"); + cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT); - Writef(m, PostMin); - Writef(m, PostMaj); - Writef(m, "] "); - - + _cmsIOPrintf(m, PostMin); + _cmsIOPrintf(m, PostMaj); + _cmsIOPrintf(m, "] "); } @@ -865,89 +731,91 @@ // Dumps CIEBasedA Color Space Array static -int EmitCIEBasedA(LPMEMSTREAM m, LPWORD Tab, int nEntries, LPcmsCIEXYZ BlackPoint) +int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint) { - Writef(m, "[ /CIEBasedA\n"); - Writef(m, " <<\n"); + _cmsIOPrintf(m, "[ /CIEBasedA\n"); + _cmsIOPrintf(m, " <<\n"); - Writef(m, "/DecodeA "); + _cmsIOPrintf(m, "/DecodeA "); - Emit1Gamma(m,Tab, nEntries); + Emit1Gamma(m, Curve); - Writef(m, " \n"); + _cmsIOPrintf(m, " \n"); - Writef(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n"); - Writef(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); + _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n"); + _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); - EmitWhiteBlackD50(m, BlackPoint); - EmitIntent(m, INTENT_PERCEPTUAL); + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, INTENT_PERCEPTUAL); - Writef(m, ">>\n"); - Writef(m, "]\n"); + _cmsIOPrintf(m, ">>\n"); + _cmsIOPrintf(m, "]\n"); - return 1; + return 1; } // Dumps CIEBasedABC Color Space Array static -int EmitCIEBasedABC(LPMEMSTREAM m, LPWORD L[], int nEntries, LPWMAT3 Matrix, LPcmsCIEXYZ BlackPoint) +int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint) { int i; - Writef(m, "[ /CIEBasedABC\n"); - Writef(m, "<<\n"); - Writef(m, "/DecodeABC [ "); + _cmsIOPrintf(m, "[ /CIEBasedABC\n"); + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "/DecodeABC [ "); - EmitNGamma(m, 3, L, nEntries); + EmitNGamma(m, 3, CurveSet); - Writef(m, "]\n"); + _cmsIOPrintf(m, "]\n"); - Writef(m, "/MatrixABC [ " ); + _cmsIOPrintf(m, "/MatrixABC [ " ); - for( i=0; i < 3; i++ ) { + for( i=0; i < 3; i++ ) { - Writef(m, "%.6f %.6f %.6f ", - FIXED_TO_DOUBLE(Matrix->v[0].n[i]), - FIXED_TO_DOUBLE(Matrix->v[1].n[i]), - FIXED_TO_DOUBLE(Matrix->v[2].n[i])); - } + _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0], + Matrix[i + 3*1], + Matrix[i + 3*2]); + } - Writef(m, "]\n"); + _cmsIOPrintf(m, "]\n"); - Writef(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); + _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); - EmitWhiteBlackD50(m, BlackPoint); - EmitIntent(m, INTENT_PERCEPTUAL); + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, INTENT_PERCEPTUAL); - Writef(m, ">>\n"); - Writef(m, "]\n"); + _cmsIOPrintf(m, ">>\n"); + _cmsIOPrintf(m, "]\n"); - return 1; + return 1; } static -int EmitCIEBasedDEF(LPMEMSTREAM m, LPLUT Lut, int Intent, LPcmsCIEXYZ BlackPoint) +int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint) { const char* PreMaj; const char* PostMaj; const char* PreMin, *PostMin; + cmsStage* mpe; - switch (Lut ->InputChan) { + mpe = Pipeline ->Elements; + + switch (cmsStageInputChannels(mpe)) { case 3: - Writef(m, "[ /CIEBasedDEF\n"); + _cmsIOPrintf(m, "[ /CIEBasedDEF\n"); PreMaj ="<"; PostMaj= ">\n"; PreMin = PostMin = ""; break; case 4: - Writef(m, "[ /CIEBasedDEFG\n"); + _cmsIOPrintf(m, "[ /CIEBasedDEFG\n"); PreMaj = "["; PostMaj = "]\n"; PreMin = "<"; @@ -958,31 +826,30 @@ } - Writef(m, "<<\n"); + _cmsIOPrintf(m, "<<\n"); - if (Lut ->wFlags & LUT_HASTL1) { + if (cmsStageType(mpe) == cmsSigCurveSetElemType) { - Writef(m, "/DecodeDEF [ "); - EmitNGamma(m, Lut ->InputChan, Lut ->L1, Lut ->CLut16params.nSamples); - Writef(m, "]\n"); + _cmsIOPrintf(m, "/DecodeDEF [ "); + EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe)); + _cmsIOPrintf(m, "]\n"); + + mpe = mpe ->Next; } - - - if (Lut ->wFlags & LUT_HAS3DGRID) { + if (cmsStageType(mpe) == cmsSigCLutElemType) { - Writef(m, "/Table "); - WriteCLUT(m, Lut, 8, PreMaj, PostMaj, PreMin, PostMin, TRUE, FALSE, (icColorSpaceSignature) 0); - Writef(m, "]\n"); + _cmsIOPrintf(m, "/Table "); + WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0); + _cmsIOPrintf(m, "]\n"); } EmitLab2XYZ(m); EmitWhiteBlackD50(m, BlackPoint); EmitIntent(m, Intent); - Writef(m, " >>\n"); - Writef(m, "]\n"); - + _cmsIOPrintf(m, " >>\n"); + _cmsIOPrintf(m, "]\n"); return 1; } @@ -990,41 +857,42 @@ // Generates a curve from a gray profile static -LPGAMMATABLE ExtractGray2Y(cmsHPROFILE hProfile, int Intent) +cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent) { - LPGAMMATABLE Out = cmsAllocGamma(256); - cmsHPROFILE hXYZ = cmsCreateXYZProfile(); - cmsHTRANSFORM xform = cmsCreateTransform(hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOTPRECALC); + cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); + cmsHPROFILE hXYZ = cmsCreateXYZProfile(); + cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE); int i; - for (i=0; i < 256; i++) { + if (Out != NULL && xform != NULL) { + for (i=0; i < 256; i++) { - BYTE Gray = (BYTE) i; - cmsCIEXYZ XYZ; + cmsUInt8Number Gray = (cmsUInt8Number) i; + cmsCIEXYZ XYZ; - cmsDoTransform(xform, &Gray, &XYZ, 1); + cmsDoTransform(xform, &Gray, &XYZ, 1); - Out ->GammaTable[i] =_cmsClampWord((int) floor(XYZ.Y * 65535.0 + 0.5)); + Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0); + } } - cmsDeleteTransform(xform); - cmsCloseProfile(hXYZ); + if (xform) cmsDeleteTransform(xform); + if (hXYZ) cmsCloseProfile(hXYZ); return Out; } -// Because PostScrip has only 8 bits in /Table, we should use +// Because PostScript has only 8 bits in /Table, we should use // a more perceptually uniform space... I do choose Lab. static -int WriteInputLUT(LPMEMSTREAM m, cmsHPROFILE hProfile, int Intent) +int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) { cmsHPROFILE hLab; cmsHTRANSFORM xform; - icColorSpaceSignature ColorSpace; - int nChannels; - DWORD InputFormat; + cmsUInt32Number nChannels; + cmsUInt32Number InputFormat; int rc; cmsHPROFILE Profiles[2]; cmsCIEXYZ BlackPointAdaptedToD50; @@ -1032,49 +900,25 @@ // Does create a device-link based transform. // The DeviceLink is next dumped as working CSA. - hLab = cmsCreateLabProfile(NULL); - ColorSpace = cmsGetColorSpace(hProfile); - nChannels = _cmsChannelsOf(ColorSpace); - InputFormat = CHANNELS_SH(nChannels) | BYTES_SH(2); - - cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent,LCMS_BPFLAGS_D50_ADAPTED); - - // Is a devicelink profile? - if (cmsGetDeviceClass(hProfile) == icSigLinkClass) { - - // if devicelink output already Lab, use it directly - - if (cmsGetPCS(hProfile) == icSigLabData) { - - xform = cmsCreateTransform(hProfile, InputFormat, NULL, - TYPE_Lab_DBL, Intent, 0); - } - else { - - // Nope, adjust output to Lab if possible - - Profiles[0] = hProfile; - Profiles[1] = hLab; - - xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, - TYPE_Lab_DBL, Intent, 0); - } + InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); + nChannels = T_CHANNELS(InputFormat); - } - else { + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); + + // Adjust output to Lab4 + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); - // This is a normal profile - xform = cmsCreateTransform(hProfile, InputFormat, hLab, - TYPE_Lab_DBL, Intent, 0); - } + Profiles[0] = hProfile; + Profiles[1] = hLab; - + xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0); + cmsCloseProfile(hLab); if (xform == NULL) { - cmsSignalError(LCMS_ERRC_ABORTED, "Cannot create transform Profile -> Lab"); - return 0; + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab"); + return 0; } // Only 1, 3 and 4 channels are allowed @@ -1082,87 +926,92 @@ switch (nChannels) { case 1: { - LPGAMMATABLE Gray2Y = ExtractGray2Y(hProfile, Intent); - EmitCIEBasedA(m, Gray2Y->GammaTable, Gray2Y ->nEntries, &BlackPointAdaptedToD50); - cmsFreeGamma(Gray2Y); + cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent); + EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50); + cmsFreeToneCurve(Gray2Y); } break; case 3: case 4: { - LPLUT DeviceLink; - _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; + cmsUInt32Number OutFrm = TYPE_Lab_16; + cmsPipeline* DeviceLink; + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + + DeviceLink = cmsPipelineDup(v ->Lut); + if (DeviceLink == NULL) return 0; - if (v ->DeviceLink) - rc = EmitCIEBasedDEF(m, v->DeviceLink, Intent, &BlackPointAdaptedToD50); - else { - DeviceLink = _cmsPrecalculateDeviceLink(xform, 0); - rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); - cmsFreeLUT(DeviceLink); - } + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); + + rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); + cmsPipelineFree(DeviceLink); + if (rc == 0) return 0; } break; default: - cmsSignalError(LCMS_ERRC_ABORTED, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels); - return 0; + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels); + return 0; } cmsDeleteTransform(xform); - cmsCloseProfile(hLab); + return 1; } +static +cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + + return Data -> Double; +} // Does create CSA based on matrix-shaper. Allowed types are gray and RGB based - static -int WriteInputMatrixShaper(LPMEMSTREAM m, cmsHPROFILE hProfile) +int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper) { - icColorSpaceSignature ColorSpace; - LPMATSHAPER MatShaper; + cmsColorSpaceSignature ColorSpace; int rc; cmsCIEXYZ BlackPointAdaptedToD50; - ColorSpace = cmsGetColorSpace(hProfile); - MatShaper = cmsBuildInputMatrixShaper(hProfile); - cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_BPFLAGS_D50_ADAPTED); - - if (MatShaper == NULL) { + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0); - cmsSignalError(LCMS_ERRC_ABORTED, "This profile is not suitable for input"); - return 0; - } + if (ColorSpace == cmsSigGrayData) { - if (ColorSpace == icSigGrayData) { - - rc = EmitCIEBasedA(m, MatShaper ->L[0], - MatShaper ->p16.nSamples, - &BlackPointAdaptedToD50); + cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper); + rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50); } else - if (ColorSpace == icSigRgbData) { + if (ColorSpace == cmsSigRgbData) { + cmsMAT3 Mat; + int i, j; + + memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat)); - rc = EmitCIEBasedABC(m, MatShaper->L, - MatShaper ->p16.nSamples, - &MatShaper ->Matrix, - &BlackPointAdaptedToD50); + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ; + + rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat, + _cmsStageGetPtrToCurveSet(Shaper), + &BlackPointAdaptedToD50); } - else { + else { - cmsSignalError(LCMS_ERRC_ABORTED, "Profile is not suitable for CSA. Unsupported colorspace."); + cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace."); return 0; } - cmsFreeMatShaper(MatShaper); - return rc; + return rc; } @@ -1171,45 +1020,46 @@ // This is a HP extension, and it works in Lab instead of XYZ static -int WriteNamedColorCSA(LPMEMSTREAM m, cmsHPROFILE hNamedColor, int Intent) +int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent) { cmsHTRANSFORM xform; cmsHPROFILE hLab; - int i, nColors; - char ColorName[32]; - + cmsUInt32Number i, nColors; + char ColorName[cmsMAX_PATH]; + cmsNAMEDCOLORLIST* NamedColorList; - hLab = cmsCreateLabProfile(NULL); - xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, - hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC); + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); + xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0); if (xform == NULL) return 0; + NamedColorList = cmsGetNamedColorList(xform); + if (NamedColorList == NULL) return 0; - Writef(m, "<<\n"); - Writef(m, "(colorlistcomment) (%s)\n", "Named color CSA"); - Writef(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); - Writef(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA"); + _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); + _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); - nColors = cmsNamedColorCount(xform); + nColors = cmsNamedColorCount(NamedColorList); for (i=0; i < nColors; i++) { - WORD In[1]; + cmsUInt16Number In[1]; cmsCIELab Lab; - In[0] = (WORD) i; + In[0] = (cmsUInt16Number) i; - if (!cmsNamedColorInfo(xform, i, ColorName, NULL, NULL)) + if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) continue; cmsDoTransform(xform, In, &Lab, 1); - Writef(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b); + _cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b); } - Writef(m, ">>\n"); + _cmsIOPrintf(m, ">>\n"); cmsDeleteTransform(xform); cmsCloseProfile(hLab); @@ -1218,75 +1068,68 @@ // Does create a Color Space Array on XYZ colorspace for PostScript usage - -DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile, - int Intent, - LPVOID Buffer, DWORD dwBufferLen) +static +cmsUInt32Number GenerateCSA(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* mem) { - - LPMEMSTREAM mem; - DWORD dwBytesUsed; - - // Set up the serialization engine - mem = CreateMemStream((LPBYTE) Buffer, dwBufferLen, MAXPSCOLS); - if (!mem) return 0; + cmsUInt32Number dwBytesUsed; + cmsPipeline* lut = NULL; + cmsStage* Matrix, *Shaper; // Is a named color profile? - if (cmsGetDeviceClass(hProfile) == icSigNamedColorClass) { - - if (!WriteNamedColorCSA(mem, hProfile, Intent)) { + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { - _cmsFree((void*) mem); - return 0; - } + if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error; } else { - // Any profile class are allowed (including devicelink), but - // output (PCS) colorspace must be XYZ or Lab - icColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); - - if (ColorSpace != icSigXYZData && - ColorSpace != icSigLabData) { + // Any profile class are allowed (including devicelink), but + // output (PCS) colorspace must be XYZ or Lab + cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); - cmsSignalError(LCMS_ERRC_ABORTED, "Invalid output color space"); - _cmsFree((void*) mem); - return 0; - } + if (ColorSpace != cmsSigXYZData && + ColorSpace != cmsSigLabData) { - // Is there any CLUT? - if (IsLUTbased(hProfile, Intent)) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space"); + goto Error; + } + - // Yes, so handle as LUT-based - if (!WriteInputLUT(mem, hProfile, Intent)) { + // Read the lut with all necessary conversion stages + lut = _cmsReadInputLUT(hProfile, Intent); + if (lut == NULL) goto Error; - _cmsFree((void*) mem); - return 0; - } - } - else { + + // Tone curves + matrix can be implemented without any LUT + if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) { - // No, try Matrix-shaper (this only works on XYZ) - - if (!WriteInputMatrixShaper(mem, hProfile)) { + if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error; - _cmsFree((void*) mem); // Something went wrong - return 0; } - } + else { + // We need a LUT for the rest + if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error; + } } // Done, keep memory usage - dwBytesUsed = mem ->dwUsed; + dwBytesUsed = mem ->UsedSpace; - // Get rid of memory stream - _cmsFree((void*) mem); + // Get rid of LUT + if (lut != NULL) cmsPipelineFree(lut); // Finally, return used byte count return dwBytesUsed; + +Error: + if (lut != NULL) cmsPipelineFree(lut); + return 0; } // ------------------------------------------------------ Color Rendering Dictionary (CRD) @@ -1356,25 +1199,25 @@ static -void EmitPQRStage(LPMEMSTREAM m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute) +void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute) { if (lIsAbsolute) { // For absolute colorimetric intent, encode back to relative - // and generate a relative LUT + // and generate a relative Pipeline // Relative encoding is obtained across XYZpcs*(D50/WhitePoint) cmsCIEXYZ White; - cmsTakeMediaWhitePoint(&White, hProfile); + _cmsReadMediaWhitePoint(&White, hProfile); - Writef(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n"); - Writef(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); + _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n"); + _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); - Writef(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n" + _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n" "/TransformPQR [\n" "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n" "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n" @@ -1384,17 +1227,17 @@ } - Writef(m,"%% Bradford Cone Space\n" + _cmsIOPrintf(m,"%% Bradford Cone Space\n" "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n"); - Writef(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); + _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); // No BPC if (!DoBPC) { - Writef(m, "%% VonKries-like transform in Bradford Cone Space\n" + _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n" "/TransformPQR [\n" "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n" "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n" @@ -1403,22 +1246,22 @@ // BPC - Writef(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n" + _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n" "/TransformPQR [\n"); - Writef(m, "{4 index 3 get div 2 index 3 get mul " + _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul " "2 index 3 get 2 index 3 get sub mul " "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub " "3 index 3 get 3 index 3 get exch sub div " "exch pop exch pop exch pop exch pop } bind\n"); - Writef(m, "{4 index 4 get div 2 index 4 get mul " + _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul " "2 index 4 get 2 index 4 get sub mul " "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub " "3 index 4 get 3 index 4 get exch sub div " "exch pop exch pop exch pop exch pop } bind\n"); - Writef(m, "{4 index 5 get div 2 index 5 get mul " + _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul " "2 index 5 get 2 index 5 get sub mul " "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub " "3 index 5 get 3 index 5 get exch sub div " @@ -1431,24 +1274,24 @@ static -void EmitXYZ2Lab(LPMEMSTREAM m) +void EmitXYZ2Lab(cmsIOHANDLER* m) { - Writef(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n"); - Writef(m, "/EncodeLMN [\n"); - Writef(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); - Writef(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); - Writef(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); - Writef(m, "]\n"); - Writef(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n"); - Writef(m, "/EncodeABC [\n"); + _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n"); + _cmsIOPrintf(m, "/EncodeLMN [\n"); + _cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "]\n"); + _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n"); + _cmsIOPrintf(m, "/EncodeABC [\n"); - Writef(m, "{ 116 mul 16 sub 100 div } bind\n"); - Writef(m, "{ 500 mul 128 add 256 div } bind\n"); - Writef(m, "{ 200 mul 128 add 256 div } bind\n"); + _cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n"); + _cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n"); + _cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n"); - Writef(m, "]\n"); + _cmsIOPrintf(m, "]\n"); } @@ -1460,104 +1303,74 @@ // 8 bits. static -int WriteOutputLUT(LPMEMSTREAM m, cmsHPROFILE hProfile, int Intent, DWORD dwFlags) +int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) { cmsHPROFILE hLab; cmsHTRANSFORM xform; - icColorSpaceSignature ColorSpace; - int i, nChannels; - DWORD OutputFormat; - _LPcmsTRANSFORM v; - LPLUT DeviceLink; + cmsUInt32Number i, nChannels; + cmsUInt32Number OutputFormat; + _cmsTRANSFORM* v; + cmsPipeline* DeviceLink; cmsHPROFILE Profiles[3]; cmsCIEXYZ BlackPointAdaptedToD50; - LCMSBOOL lFreeDeviceLink = FALSE; - LCMSBOOL lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); - LCMSBOOL lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); - int RelativeEncodingIntent; - + cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); + cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); + cmsUInt32Number InFrm = TYPE_Lab_16; + cmsUInt32Number RelativeEncodingIntent; + cmsColorSpaceSignature ColorSpace; - hLab = cmsCreateLabProfile(NULL); + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); + if (hLab == NULL) return 0; - ColorSpace = cmsGetColorSpace(hProfile); - nChannels = _cmsChannelsOf(ColorSpace); - OutputFormat = CHANNELS_SH(nChannels) | BYTES_SH(2); + OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); + nChannels = T_CHANNELS(OutputFormat); - // For absolute colorimetric, the LUT is encoded as relative - // in order to preserve precission. + ColorSpace = cmsGetColorSpace(hProfile); + + // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision. RelativeEncodingIntent = Intent; if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; - // Is a devicelink profile? - if (cmsGetDeviceClass(hProfile) == icSigLinkClass) { - - // if devicelink input already in Lab - - if (ColorSpace == icSigLabData) { - - // adjust input to Lab to our v4 - - Profiles[0] = hLab; - Profiles[1] = hProfile; + // Use V4 Lab always + Profiles[0] = hLab; + Profiles[1] = hProfile; - xform = cmsCreateMultiprofileTransform(Profiles, 2, TYPE_Lab_DBL, - OutputFormat, RelativeEncodingIntent, - dwFlags|cmsFLAGS_NOWHITEONWHITEFIXUP|cmsFLAGS_NOPRELINEARIZATION); - - } - else { - cmsSignalError(LCMS_ERRC_ABORTED, "Cannot use devicelink profile for CRD creation"); - return 0; - } - - - } - else { - - // This is a normal profile - xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hProfile, - OutputFormat, RelativeEncodingIntent, dwFlags|cmsFLAGS_NOWHITEONWHITEFIXUP|cmsFLAGS_NOPRELINEARIZATION); - } + xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, + Profiles, 2, TYPE_Lab_DBL, + OutputFormat, RelativeEncodingIntent, 0); + cmsCloseProfile(hLab); if (xform == NULL) { - cmsSignalError(LCMS_ERRC_ABORTED, "Cannot create transform Lab -> Profile in CRD creation"); - return 0; + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation"); + return 0; } - // Get the internal precalculated devicelink - - v = (_LPcmsTRANSFORM) xform; - DeviceLink = v ->DeviceLink; - - if (!DeviceLink) { - - DeviceLink = _cmsPrecalculateDeviceLink(xform, cmsFLAGS_NOPRELINEARIZATION); - lFreeDeviceLink = TRUE; - } - - Writef(m, "<<\n"); - Writef(m, "/ColorRenderingType 1\n"); + // Get a copy of the internal devicelink + v = (_cmsTRANSFORM*) xform; + DeviceLink = cmsPipelineDup(v ->Lut); + if (DeviceLink == NULL) return 0; - cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, LCMS_BPFLAGS_D50_ADAPTED); + // We need a CLUT + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); + + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "/ColorRenderingType 1\n"); + + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); // Emit headers, etc. EmitWhiteBlackD50(m, &BlackPointAdaptedToD50); EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC); EmitXYZ2Lab(m); - if (DeviceLink ->wFlags & LUT_HASTL1) { - - // Shouldn't happen - cmsSignalError(LCMS_ERRC_ABORTED, "Internal error (prelinearization on CRD)"); - return 0; - } - // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127, @@ -1568,31 +1381,30 @@ if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) lFixWhite = FALSE; - Writef(m, "/RenderTable "); + _cmsIOPrintf(m, "/RenderTable "); + - WriteCLUT(m, DeviceLink, 8, "<", ">\n", "", "", FALSE, - lFixWhite, ColorSpace); + WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace); - Writef(m, " %d {} bind ", nChannels); + _cmsIOPrintf(m, " %d {} bind ", nChannels); for (i=1; i < nChannels; i++) - Writef(m, "dup "); + _cmsIOPrintf(m, "dup "); - Writef(m, "]\n"); + _cmsIOPrintf(m, "]\n"); EmitIntent(m, Intent); - Writef(m, ">>\n"); + _cmsIOPrintf(m, ">>\n"); if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { - Writef(m, "/Current exch /ColorRendering defineresource pop\n"); + _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n"); } - if (lFreeDeviceLink) cmsFreeLUT(DeviceLink); + cmsPipelineFree(DeviceLink); cmsDeleteTransform(xform); - cmsCloseProfile(hLab); return 1; } @@ -1600,23 +1412,24 @@ // Builds a ASCII string containing colorant list in 0..1.0 range static -void BuildColorantList(char *Colorant, int nColorant, WORD Out[]) +void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[]) { char Buff[32]; - int j; + cmsUInt32Number j; Colorant[0] = 0; - if (nColorant > MAXCHANNELS) - nColorant = MAXCHANNELS; + if (nColorant > cmsMAXCHANNELS) + nColorant = cmsMAXCHANNELS; - for (j=0; j < nColorant; j++) { + for (j = 0; j < nColorant; j++) { - sprintf(Buff, "%.3f", Out[j] / 65535.0); - strcat(Colorant, Buff); - if (j < nColorant -1) - strcat(Colorant, " "); + snprintf(Buff, 31, "%.3f", Out[j] / 65535.0); + Buff[31] = 0; + strcat(Colorant, Buff); + if (j < nColorant - 1) + strcat(Colorant, " "); - } + } } @@ -1624,50 +1437,54 @@ // This is a HP extension. static -int WriteNamedColorCRD(LPMEMSTREAM m, cmsHPROFILE hNamedColor, int Intent, DWORD dwFlags) +int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags) { cmsHTRANSFORM xform; - int i, nColors, nColorant; - DWORD OutputFormat; - char ColorName[32]; + cmsUInt32Number i, nColors, nColorant; + cmsUInt32Number OutputFormat; + char ColorName[cmsMAX_PATH]; char Colorant[128]; + cmsNAMEDCOLORLIST* NamedColorList; - nColorant = _cmsChannelsOf(cmsGetColorSpace(hNamedColor)); - OutputFormat = CHANNELS_SH(nColorant) | BYTES_SH(2); - xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, - NULL, OutputFormat, Intent, cmsFLAGS_NOTPRECALC); + OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE); + nColorant = T_CHANNELS(OutputFormat); + + + xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags); if (xform == NULL) return 0; - Writef(m, "<<\n"); - Writef(m, "(colorlistcomment) (%s) \n", "Named profile"); - Writef(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); - Writef(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + NamedColorList = cmsGetNamedColorList(xform); + if (NamedColorList == NULL) return 0; - nColors = cmsNamedColorCount(xform); + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile"); + _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); + _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + nColors = cmsNamedColorCount(NamedColorList); for (i=0; i < nColors; i++) { - WORD In[1]; - WORD Out[MAXCHANNELS]; + cmsUInt16Number In[1]; + cmsUInt16Number Out[cmsMAXCHANNELS]; - In[0] = (WORD) i; + In[0] = (cmsUInt16Number) i; - if (!cmsNamedColorInfo(xform, i, ColorName, NULL, NULL)) + if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) continue; cmsDoTransform(xform, In, Out, 1); BuildColorantList(Colorant, nColorant, Out); - Writef(m, " (%s) [ %s ]\n", ColorName, Colorant); + _cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant); } - Writef(m, " >>"); + _cmsIOPrintf(m, " >>"); if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { - Writef(m, " /Current exch /HPSpotTable defineresource pop\n"); + _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n"); } cmsDeleteTransform(xform); @@ -1680,67 +1497,130 @@ // CRD are always LUT-Based, no matter if profile is // implemented as matrix-shaper. -DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile, - int Intent, DWORD dwFlags, - LPVOID Buffer, DWORD dwBufferLen) +static +cmsUInt32Number GenerateCRD(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number dwFlags, + cmsIOHANDLER* mem) { - - LPMEMSTREAM mem; - DWORD dwBytesUsed; - - // Set up the serialization artifact - mem = CreateMemStream((LPBYTE) Buffer, dwBufferLen, MAXPSCOLS); - if (!mem) return 0; - + cmsUInt32Number dwBytesUsed; if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { - EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile); + EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile); } // Is a named color profile? - if (cmsGetDeviceClass(hProfile) == icSigNamedColorClass) { + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) { - - _cmsFree((void*) mem); - return 0; + return 0; } } else { - // CRD are always implemented as LUT. - + // CRD are always implemented as LUT - if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { - _cmsFree((void*) mem); - return 0; - } + if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { + return 0; + } } if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { - Writef(mem, "%%%%EndResource\n"); - Writef(mem, "\n%% CRD End\n"); + _cmsIOPrintf(mem, "%%%%EndResource\n"); + _cmsIOPrintf(mem, "\n%% CRD End\n"); } // Done, keep memory usage - dwBytesUsed = mem ->dwUsed; + dwBytesUsed = mem ->UsedSpace; + + // Finally, return used byte count + return dwBytesUsed; + + cmsUNUSED_PARAMETER(ContextID); +} + + + + +cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, + cmsPSResourceType Type, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* io) +{ + cmsUInt32Number rc; + + + switch (Type) { + + case cmsPS_RESOURCE_CSA: + rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io); + break; + + default: + case cmsPS_RESOURCE_CRD: + rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io); + break; + } + + return rc; +} + + + +cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number dwFlags, + void* Buffer, cmsUInt32Number dwBufferLen) +{ + cmsIOHANDLER* mem; + cmsUInt32Number dwBytesUsed; + + // Set up the serialization engine + if (Buffer == NULL) + mem = cmsOpenIOhandlerFromNULL(ContextID); + else + mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); + + if (!mem) return 0; + + dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem); // Get rid of memory stream - _cmsFree((void*) mem); + cmsCloseIOhandler(mem); - // Finally, return used byte count return dwBytesUsed; } -// For compatibility with previous versions + +// Does create a Color Space Array on XYZ colorspace for PostScript usage +cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + void* Buffer, + cmsUInt32Number dwBufferLen) +{ + cmsIOHANDLER* mem; + cmsUInt32Number dwBytesUsed; -DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile, - int Intent, - LPVOID Buffer, DWORD dwBufferLen) -{ - return cmsGetPostScriptCRDEx(hProfile, Intent, 0, Buffer, dwBufferLen); + if (Buffer == NULL) + mem = cmsOpenIOhandlerFromNULL(ContextID); + else + mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); + + if (!mem) return 0; + + dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem); + + // Get rid of memory stream + cmsCloseIOhandler(mem); + + return dwBytesUsed; + } diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmssamp.c --- a/src/share/native/sun/java2d/cmm/lcms/cmssamp.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmssamp.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,650 +49,528 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -#include "lcms.h" - - -// --------------------------------------------------------------------------------- - -static volatile int GlobalBlackPreservationStrategy = 0; +// +//--------------------------------------------------------------------------------- +// -// Quantize a value 0 <= i < MaxSamples - -WORD _cmsQuantizeVal(double i, int MaxSamples) -{ - double x; - - x = ((double) i * 65535.) / (double) (MaxSamples - 1); - - return (WORD) floor(x + .5); -} +#include "lcms2_internal.h" -// Is a table linear? - -int cmsIsLinear(WORD Table[], int nEntries) -{ - register int i; - int diff; - - for (i=0; i < nEntries; i++) { - - diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries)); - if (diff > 3) - return 0; - } +#define cmsmin(a, b) (((a) < (b)) ? (a) : (b)) +#define cmsmax(a, b) (((a) > (b)) ? (a) : (b)) - return 1; -} - - - -// pow() restricted to integer +// This file contains routines for resampling and LUT optimization, black point detection +// and black preservation. -static -int ipow(int base, int exp) -{ - int res = base; - - while (--exp) - res *= base; - - return res; -} +// Black point detection ------------------------------------------------------------------------- -// Given n, 0<=n<=clut^dim, returns the colorant. - +// PCS -> PCS round trip transform, always uses relative intent on the device -> pcs static -int ComponentOf(int n, int clut, int nColorant) +cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent) { - if (nColorant <= 0) - return (n % clut); + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsHPROFILE hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + cmsHTRANSFORM xform; + cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE }; + cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 }; + cmsHPROFILE hProfiles[4]; + cmsUInt32Number Intents[4]; - n /= ipow(clut, nColorant); + hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab; + Intents[0] = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC; - return (n % clut); + xform = cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents, + States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + + cmsCloseProfile(hLab); + return xform; } - - -// This routine does a sweep on whole input space, and calls its callback -// function on knots. returns TRUE if all ok, FALSE otherwise. - -LCMSBOOL LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags) +// Use darker colorants to obtain black point. This works in the relative colorimetric intent and +// assumes more ink results in darker colors. No ink limit is assumed. +static +cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput, + cmsUInt32Number Intent, + cmsCIEXYZ* BlackPoint, + cmsUInt32Number dwFlags) { - int i, t, nTotalPoints, Colorant, index; - WORD In[MAXCHANNELS], Out[MAXCHANNELS]; + cmsUInt16Number *Black; + cmsHTRANSFORM xform; + cmsColorSpaceSignature Space; + cmsUInt32Number nChannels; + cmsUInt32Number dwFormat; + cmsHPROFILE hLab; + cmsCIELab Lab; + cmsCIEXYZ BlackXYZ; + cmsContext ContextID = cmsGetProfileContextID(hInput); - nTotalPoints = ipow(Lut->cLutPoints, Lut -> InputChan); + // If the profile does not support input direction, assume Black point 0 + if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) { - index = 0; - for (i = 0; i < nTotalPoints; i++) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Create a formatter which has n channels and floating point + dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE); - for (t=0; t < (int) Lut -> InputChan; t++) { + // Try to get black by using black colorant + Space = cmsGetColorSpace(hInput); - Colorant = ComponentOf(i, Lut -> cLutPoints, (Lut -> InputChan - t - 1 )); - In[t] = _cmsQuantizeVal(Colorant, Lut -> cLutPoints); - } + // This function returns darker colorant in 16 bits for several spaces + if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } - if (dwFlags & SAMPLER_HASTL1) { + if (nChannels != T_CHANNELS(dwFormat)) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } - for (t=0; t < (int) Lut -> InputChan; t++) - In[t] = cmsReverseLinearInterpLUT16(In[t], - Lut -> L1[t], - &Lut -> In16params); - } + // Lab will be used as the output space, but lab2 will avoid recursion + hLab = cmsCreateLab2ProfileTHR(ContextID, NULL); + if (hLab == NULL) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Create the transform + xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat, + hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); + cmsCloseProfile(hLab); + + if (xform == NULL) { - for (t=0; t < (int) Lut -> OutputChan; t++) - Out[t] = Lut->T[index + t]; + // Something went wrong. Get rid of open resources and return zero as black + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } - if (dwFlags & SAMPLER_HASTL2) { + // Convert black to Lab + cmsDoTransform(xform, Black, &Lab, 1); + + // Force it to be neutral, clip to max. L* of 50 + Lab.a = Lab.b = 0; + if (Lab.L > 50) Lab.L = 50; - for (t=0; t < (int) Lut -> OutputChan; t++) - Out[t] = cmsLinearInterpLUT16(Out[t], - Lut -> L2[t], - &Lut -> Out16params); - } + // Free the resources + cmsDeleteTransform(xform); + + // Convert from Lab (which is now clipped) to XYZ. + cmsLab2XYZ(NULL, &BlackXYZ, &Lab); + if (BlackPoint != NULL) + *BlackPoint = BlackXYZ; - if (!Sampler(In, Out, Cargo)) - return FALSE; + return TRUE; - if (!(dwFlags & SAMPLER_INSPECT)) { + cmsUNUSED_PARAMETER(dwFlags); +} - if (dwFlags & SAMPLER_HASTL2) { +// Get a black point of output CMYK profile, discounting any ink-limiting embedded +// in the profile. For doing that, we use perceptual intent in input direction: +// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab +static +cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile) +{ + cmsHTRANSFORM hRoundTrip; + cmsCIELab LabIn, LabOut; + cmsCIEXYZ BlackXYZ; - for (t=0; t < (int) Lut -> OutputChan; t++) - Out[t] = cmsReverseLinearInterpLUT16(Out[t], - Lut -> L2[t], - &Lut -> Out16params); - } + // Is the intent supported by the profile? + if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return TRUE; + } - for (t=0; t < (int) Lut -> OutputChan; t++) - Lut->T[index + t] = Out[t]; + hRoundTrip = CreateRoundtripXForm(hProfile, INTENT_PERCEPTUAL); + if (hRoundTrip == NULL) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + LabIn.L = LabIn.a = LabIn.b = 0; + cmsDoTransform(hRoundTrip, &LabIn, &LabOut, 1); - } + // Clip Lab to reasonable limits + if (LabOut.L > 50) LabOut.L = 50; + LabOut.a = LabOut.b = 0; + + cmsDeleteTransform(hRoundTrip); - index += Lut -> OutputChan; + // Convert it to XYZ + cmsLab2XYZ(NULL, &BlackXYZ, &LabOut); - } + if (BlackPoint != NULL) + *BlackPoint = BlackXYZ; return TRUE; } - - - - +// This function shouldn't exist at all -- there is such quantity of broken +// profiles on black point tag, that we must somehow fix chromaticity to +// avoid huge tint when doing Black point compensation. This function does +// just that. There is a special flag for using black point tag, but turned +// off by default because it is bogus on most profiles. The detection algorithm +// involves to turn BP to neutral and to use only L component. +cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +{ + cmsProfileClassSignature devClass; -// choose reasonable resolution -int _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD dwFlags) -{ - int nChannels; - - // Already specified? - if (dwFlags & 0x00FF0000) { - // Yes, grab'em - return (dwFlags >> 16) & 0xFF; + // Make sure the device class is adequate + devClass = cmsGetDeviceClass(hProfile); + if (devClass == cmsSigLinkClass || + devClass == cmsSigAbstractClass || + devClass == cmsSigNamedColorClass) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; } - nChannels = _cmsChannelsOf(Colorspace); - - // HighResPrecalc is maximum resolution - - if (dwFlags & cmsFLAGS_HIGHRESPRECALC) { + // Make sure intent is adequate + if (Intent != INTENT_PERCEPTUAL && + Intent != INTENT_RELATIVE_COLORIMETRIC && + Intent != INTENT_SATURATION) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } - if (nChannels > 4) - return 7; // 7 for Hifi + // v4 + perceptual & saturation intents does have its own black point, and it is + // well specified enough to use it. Black point tag is deprecated in V4. + if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && + (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { - if (nChannels == 4) // 23 for CMYK - return 23; + // Matrix shaper share MRC & perceptual intents + if (cmsIsMatrixShaper(hProfile)) + return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); - return 49; // 49 for RGB and others + // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents + BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; + BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; + BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; + + return TRUE; } - // LowResPrecal is stripped resolution - - if (dwFlags & cmsFLAGS_LOWRESPRECALC) { - - if (nChannels > 4) - return 6; // 6 for Hifi - - if (nChannels == 1) - return 33; // For monochrome +#ifdef CMS_USE_PROFILE_BLACK_POINT_TAG - return 17; // 17 for remaining - } - - // Default values + // v2, v4 rel/abs colorimetric + if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) && + Intent == INTENT_RELATIVE_COLORIMETRIC) { - if (nChannels > 4) - return 7; // 7 for Hifi - - if (nChannels == 4) - return 17; // 17 for CMYK - - return 33; // 33 for RGB + cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite; + cmsCIELab Lab; -} - -// Sampler implemented by another transform. This is a clean way to -// precalculate the devicelink 3D CLUT for almost any transform + // If black point is specified, then use it, -static -int XFormSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) -{ - cmsDoTransform((cmsHTRANSFORM) Cargo, In, Out, 1); - return TRUE; -} + BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag); + if (BlackPtr != NULL) { -// This routine does compute the devicelink CLUT containing whole -// transform. Handles any channel number. + BlackXYZ = *BlackPtr; + _cmsReadMediaWhitePoint(&MediaWhite, hProfile); -LPLUT _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags) -{ - _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h; - LPLUT Grid; - int nGridPoints; - DWORD dwFormatIn, dwFormatOut; - DWORD SaveFormatIn, SaveFormatOut; - int ChannelsIn, ChannelsOut; - LPLUT SaveGamutLUT; - + // Black point is absolute XYZ, so adapt to D50 to get PCS value + cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ); - // Remove any gamut checking - SaveGamutLUT = p ->Gamut; - p ->Gamut = NULL; - - ChannelsIn = _cmsChannelsOf(p -> EntryColorSpace); - ChannelsOut = _cmsChannelsOf(p -> ExitColorSpace); - - nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags); - - Grid = cmsAllocLUT(); - if (!Grid) return NULL; + // Force a=b=0 to get rid of any chroma + cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint); + Lab.a = Lab.b = 0; + if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50 + cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab); - Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut); - - // Compute device link on 16-bit basis - dwFormatIn = (CHANNELS_SH(ChannelsIn)|BYTES_SH(2)); - dwFormatOut = (CHANNELS_SH(ChannelsOut)|BYTES_SH(2)); - - SaveFormatIn = p ->InputFormat; - SaveFormatOut = p ->OutputFormat; - - p -> InputFormat = dwFormatIn; - p -> OutputFormat = dwFormatOut; - p -> FromInput = _cmsIdentifyInputFormat(p, dwFormatIn); - p -> ToOutput = _cmsIdentifyOutputFormat(p, dwFormatOut); + if (BlackPoint != NULL) + *BlackPoint = TrustedBlackPoint; - // Fix gamut & gamma possible mismatches. - - if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) { + return TRUE; + } + } +#endif - cmsHTRANSFORM hOne[1]; - hOne[0] = h; - - _cmsComputePrelinearizationTablesFromXFORM(hOne, 1, Grid); - } + // That is about v2 profiles. - // Attention to this typecast! we can take the luxury to - // do this since cmsHTRANSFORM is only an alias to a pointer - // to the transform struct. - - if (!cmsSample3DGrid(Grid, XFormSampler, (LPVOID) p, Grid -> wFlags)) { + // If output profile, discount ink-limiting and that's all + if (Intent == INTENT_RELATIVE_COLORIMETRIC && + (cmsGetDeviceClass(hProfile) == cmsSigOutputClass) && + (cmsGetColorSpace(hProfile) == cmsSigCmykData)) + return BlackPointUsingPerceptualBlack(BlackPoint, hProfile); - cmsFreeLUT(Grid); - Grid = NULL; - } - - p ->Gamut = SaveGamutLUT; - p ->InputFormat = SaveFormatIn; - p ->OutputFormat = SaveFormatOut; - - return Grid; + // Nope, compute BP using current intent. + return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags); } -// Sampler for Black-preserving CMYK->CMYK transforms +// --------------------------------------------------------------------------------------------------------- -typedef struct { - cmsHTRANSFORM cmyk2cmyk; - cmsHTRANSFORM cmyk2Lab; - LPGAMMATABLE KTone; - L16PARAMS KToneParams; - LPLUT LabK2cmyk; - double MaxError; - - cmsHTRANSFORM hRoundTrip; - int MaxTAC; +// Least Squares Fit of a Quadratic Curve to Data +// http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html - cmsHTRANSFORM hProofOutput; - - } BPCARGO, *LPBPCARGO; - - - -// Preserve black only if that is the only ink used static -int BlackPreservingGrayOnlySampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[], cmsFloat64Number y[]) { - BPCARGO* bp = (LPBPCARGO) Cargo; - - // If going across black only, keep black only - if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0; + double sum_y = 0, sum_yx = 0, sum_yx2 = 0; + double d, a, b, c; + int i; + cmsMAT3 m; + cmsVEC3 v, res; - // TAC does not apply because it is black ink! - Out[0] = Out[1] = Out[2] = 0; - Out[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams); - return 1; - } + if (n < 4) return 0; - // Keep normal transform for other colors - cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1); - return 1; -} + for (i=0; i < n; i++) { - + double xn = x[i]; + double yn = y[i]; -// Preserve all K plane. -static -int BlackPreservingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) -{ - - WORD LabK[4]; - double SumCMY, SumCMYK, Error; - cmsCIELab ColorimetricLab, BlackPreservingLab; - BPCARGO* bp = (LPBPCARGO) Cargo; + sum_x += xn; + sum_x2 += xn*xn; + sum_x3 += xn*xn*xn; + sum_x4 += xn*xn*xn*xn; - // Get the K across Tone curve - LabK[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams); - - // If going across black only, keep black only - if (In[0] == 0 && In[1] == 0 && In[2] == 0) { - - Out[0] = Out[1] = Out[2] = 0; - Out[3] = LabK[3]; - return 1; + sum_y += yn; + sum_yx += yn*xn; + sum_yx2 += yn*xn*xn; } - // Try the original transform, maybe K is already ok (valid on K=0) - cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1); - if (Out[3] == LabK[3]) return 1; - - - // No, mesure and keep Lab measurement for further usage - cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); - - // Is not black only and the transform doesn't keep black. - // Obtain the Lab of CMYK. After that we have Lab + K - cmsDoTransform(bp ->cmyk2Lab, In, LabK, 1); - - // Obtain the corresponding CMY using reverse interpolation. - // As a seed, we use the colorimetric CMY - cmsEvalLUTreverse(bp ->LabK2cmyk, LabK, Out, Out); - - // Estimate the error - cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); - Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); - - - // Apply TAC if needed - - SumCMY = Out[0] + Out[1] + Out[2]; - SumCMYK = SumCMY + Out[3]; - - if (SumCMYK > bp ->MaxTAC) { - - double Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); - if (Ratio < 0) - Ratio = 0; + _cmsVEC3init(&m.v[0], n, sum_x, sum_x2); + _cmsVEC3init(&m.v[1], sum_x, sum_x2, sum_x3); + _cmsVEC3init(&m.v[2], sum_x2, sum_x3, sum_x4); - Out[0] = (WORD) floor(Out[0] * Ratio + 0.5); // C - Out[1] = (WORD) floor(Out[1] * Ratio + 0.5); // M - Out[2] = (WORD) floor(Out[2] * Ratio + 0.5); // Y - } - - return 1; -} - - -// Sample whole gamut to estimate maximum TAC - -#ifdef _MSC_VER -#pragma warning(disable : 4100) -#endif + _cmsVEC3init(&v, sum_y, sum_yx, sum_yx2); -static -int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo) -{ - BPCARGO* bp = (LPBPCARGO) Cargo; - WORD RoundTrip[4]; - int Sum; - - cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); - - Sum = RoundTrip[0] + RoundTrip[1] + RoundTrip[2] + RoundTrip[3]; - - if (Sum > bp ->MaxTAC) - bp ->MaxTAC = Sum; - - return 1; -} + if (!_cmsMAT3solve(&res, &m, &v)) return 0; -// Estimate the maximum error -static -int BlackPreservingEstimateErrorSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) -{ - BPCARGO* bp = (LPBPCARGO) Cargo; - WORD ColorimetricOut[4]; - cmsCIELab ColorimetricLab, BlackPreservingLab; - double Error; - - if (In[0] == 0 && In[1] == 0 && In[2] == 0) return 1; - - cmsDoTransform(bp->cmyk2cmyk, In, ColorimetricOut, 1); - - cmsDoTransform(bp->hProofOutput, ColorimetricOut, &ColorimetricLab, 1); - cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); - - Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); - - if (Error > bp ->MaxError) - bp ->MaxError = Error; - - return 1; -} - -// Setup the K preservation strategy -int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n) -{ - int OldVal = GlobalBlackPreservationStrategy; - - if (n >= 0) - GlobalBlackPreservationStrategy = n; - - return OldVal; -} - -#pragma warning(disable: 4550) - -// Get a pointer to callback on depending of strategy -static -_cmsSAMPLER _cmsGetBlackPreservationSampler(void) -{ - switch (GlobalBlackPreservationStrategy) { - - case 0: return BlackPreservingGrayOnlySampler; - default: return BlackPreservingSampler; - } - -} + a = res.n[2]; + b = res.n[1]; + c = res.n[0]; -// This is the black-preserving devicelink generator -LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags) -{ - _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK; - BPCARGO Cargo; - LPLUT Grid; - DWORD LocalFlags; - cmsHPROFILE hLab = cmsCreateLabProfile(NULL); - int nGridPoints; - icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual - icSigAToB1Tag, // Relative colorimetric - icSigAToB2Tag, // Saturation - icSigAToB1Tag }; // Absolute colorimetric - // (Relative/WhitePoint) - - nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags); - - // Get a copy of inteserting flags for this kind of xform - LocalFlags = cmsFLAGS_NOTPRECALC; - if (p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) - LocalFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; - - // Fill in cargo struct - Cargo.cmyk2cmyk = hCMYK2CMYK; + if (fabs(a) < 1.0E-10) { - // Compute tone curve. - Cargo.KTone = _cmsBuildKToneCurve(hCMYK2CMYK, 256); - if (Cargo.KTone == NULL) return NULL; - cmsCalcL16Params(Cargo.KTone ->nEntries, &Cargo.KToneParams); - - - // Create a CMYK->Lab "normal" transform on input, without K-preservation - Cargo.cmyk2Lab = cmsCreateTransform(p ->InputProfile, TYPE_CMYK_16, - hLab, TYPE_Lab_16, p->Intent, LocalFlags); - - // We are going to use the reverse of proof direction - Cargo.LabK2cmyk = cmsReadICCLut(p->OutputProfile, Device2PCS[p->Intent]); - - // Is there any table available? - if (Cargo.LabK2cmyk == NULL) { - - Grid = NULL; - goto Cleanup; - } - - // Setup a roundtrip on output profile for TAC estimation - Cargo.hRoundTrip = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16, - p ->OutputProfile, TYPE_CMYK_16, p->Intent, cmsFLAGS_NOTPRECALC); - - - // Setup a proof CMYK->Lab on output - Cargo.hProofOutput = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16, - hLab, TYPE_Lab_DBL, p->Intent, LocalFlags); - - - // Create an empty LUT for holding K-preserving xform - Grid = cmsAllocLUT(); - if (!Grid) goto Cleanup; + return cmsmin(0, cmsmax(50, -c/b )); + } + else { - Grid = cmsAlloc3DGrid(Grid, nGridPoints, 4, 4); - - // Setup formatters - p -> FromInput = _cmsIdentifyInputFormat(p, TYPE_CMYK_16); - p -> ToOutput = _cmsIdentifyOutputFormat(p, TYPE_CMYK_16); - - - - // Step #1, estimate TAC - Cargo.MaxTAC = 0; - if (!cmsSample3DGrid(Grid, EstimateTAC, (LPVOID) &Cargo, 0)) { - - cmsFreeLUT(Grid); - Grid = NULL; - goto Cleanup; - } - - - // Step #2, compute approximation - if (!cmsSample3DGrid(Grid, _cmsGetBlackPreservationSampler(), (LPVOID) &Cargo, 0)) { - - cmsFreeLUT(Grid); - Grid = NULL; - goto Cleanup; - } - - // Step #3, estimate error - Cargo.MaxError = 0; - cmsSample3DGrid(Grid, BlackPreservingEstimateErrorSampler, (LPVOID) &Cargo, SAMPLER_INSPECT); - - -Cleanup: - - if (Cargo.cmyk2Lab) cmsDeleteTransform(Cargo.cmyk2Lab); - if (Cargo.hRoundTrip) cmsDeleteTransform(Cargo.hRoundTrip); - if (Cargo.hProofOutput) cmsDeleteTransform(Cargo.hProofOutput); - - if (hLab) cmsCloseProfile(hLab); - if (Cargo.KTone) cmsFreeGamma(Cargo.KTone); - if (Cargo.LabK2cmyk) cmsFreeLUT(Cargo.LabK2cmyk); - - return Grid; -} - - - -// Fix broken LUT. just to obtain other CMS compatibility + d = b*b - 4.0 * a * c; + if (d <= 0) { + return 0; + } + else { -static -void PatchLUT(LPLUT Grid, WORD At[], WORD Value[], - int nChannelsOut, int nChannelsIn) -{ - LPL16PARAMS p16 = &Grid -> CLut16params; - double px, py, pz, pw; - int x0, y0, z0, w0; - int i, index; - - - if (Grid ->wFlags & LUT_HASTL1) return; // There is a prelinearization - - px = ((double) At[0] * (p16->Domain)) / 65535.0; - py = ((double) At[1] * (p16->Domain)) / 65535.0; - pz = ((double) At[2] * (p16->Domain)) / 65535.0; - pw = ((double) At[3] * (p16->Domain)) / 65535.0; - - x0 = (int) floor(px); - y0 = (int) floor(py); - z0 = (int) floor(pz); - w0 = (int) floor(pw); - - if (nChannelsIn == 4) { - - if (((px - x0) != 0) || - ((py - y0) != 0) || - ((pz - z0) != 0) || - ((pw - w0) != 0)) return; // Not on exact node + double rt = (-b + sqrt(d)) / (2.0 * a); - index = p16 -> opta4 * x0 + - p16 -> opta3 * y0 + - p16 -> opta2 * z0 + - p16 -> opta1 * w0; - } - else - if (nChannelsIn == 3) { - - if (((px - x0) != 0) || - ((py - y0) != 0) || - ((pz - z0) != 0)) return; // Not on exact node - - index = p16 -> opta3 * x0 + - p16 -> opta2 * y0 + - p16 -> opta1 * z0; - } - else - if (nChannelsIn == 1) { - - if (((px - x0) != 0)) return; // Not on exact node - - index = p16 -> opta1 * x0; - } - else { - cmsSignalError(LCMS_ERRC_ABORTED, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); - return; - } - - for (i=0; i < nChannelsOut; i++) - Grid -> T[index + i] = Value[i]; + return cmsmax(0, cmsmin(50, rt)); + } + } } -LCMSBOOL _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p) +// Calculates the black point of a destination profile. +// This algorithm comes from the Adobe paper disclosing its black point compensation method. +cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) { + cmsColorSpaceSignature ColorSpace; + cmsHTRANSFORM hRoundTrip = NULL; + cmsCIELab InitialLab, destLab, Lab; + cmsFloat64Number inRamp[256], outRamp[256]; + cmsFloat64Number MinL, MaxL; + cmsBool NearlyStraightMidrange = TRUE; + cmsFloat64Number yRamp[256]; + cmsFloat64Number x[256], y[256]; + cmsFloat64Number lo, hi; + int n, l; + cmsProfileClassSignature devClass; - WORD *WhitePointIn, *WhitePointOut, *BlackPointIn, *BlackPointOut; - int nOuts, nIns; + // Make sure the device class is adequate + devClass = cmsGetDeviceClass(hProfile); + if (devClass == cmsSigLinkClass || + devClass == cmsSigAbstractClass || + devClass == cmsSigNamedColorClass) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Make sure intent is adequate + if (Intent != INTENT_PERCEPTUAL && + Intent != INTENT_RELATIVE_COLORIMETRIC && + Intent != INTENT_SATURATION) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } - if (!p -> DeviceLink) return FALSE; + // v4 + perceptual & saturation intents does have its own black point, and it is + // well specified enough to use it. Black point tag is deprecated in V4. + if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && + (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { + + // Matrix shaper share MRC & perceptual intents + if (cmsIsMatrixShaper(hProfile)) + return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); + + // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents + BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; + BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; + BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; + return TRUE; + } + + + // Check if the profile is lut based and gray, rgb or cmyk (7.2 in Adobe's document) + ColorSpace = cmsGetColorSpace(hProfile); + if (!cmsIsCLUT(hProfile, Intent, LCMS_USED_AS_OUTPUT ) || + (ColorSpace != cmsSigGrayData && + ColorSpace != cmsSigRgbData && + ColorSpace != cmsSigCmykData)) { - if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) return FALSE; - if ((p ->PreviewProfile != NULL) && - (p ->ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC)) return FALSE; + // In this case, handle as input case + return cmsDetectBlackPoint(BlackPoint, hProfile, Intent, dwFlags); + } + + // It is one of the valid cases!, use Adobe algorithm + + + // Set a first guess, that should work on good profiles. + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + + cmsCIEXYZ IniXYZ; + + // calculate initial Lab as source black point + if (!cmsDetectBlackPoint(&IniXYZ, hProfile, Intent, dwFlags)) { + return FALSE; + } + + // convert the XYZ to lab + cmsXYZ2Lab(NULL, &InitialLab, &IniXYZ); + + } else { + + // set the initial Lab to zero, that should be the black point for perceptual and saturation + InitialLab.L = 0; + InitialLab.a = 0; + InitialLab.b = 0; + } - if (!_cmsEndPointsBySpace(p -> EntryColorSpace, - &WhitePointIn, &BlackPointIn, &nIns)) return FALSE; + // Step 2 + // ====== + + // Create a roundtrip. Define a Transform BT for all x in L*a*b* + hRoundTrip = CreateRoundtripXForm(hProfile, Intent); + if (hRoundTrip == NULL) return FALSE; + + // Compute ramps + + for (l=0; l < 256; l++) { + + Lab.L = (cmsFloat64Number) (l * 100.0) / 255.0; + Lab.a = cmsmin(50, cmsmax(-50, InitialLab.a)); + Lab.b = cmsmin(50, cmsmax(-50, InitialLab.b)); + + cmsDoTransform(hRoundTrip, &Lab, &destLab, 1); + + inRamp[l] = Lab.L; + outRamp[l] = destLab.L; + } + + // Make monotonic + for (l = 254; l > 0; --l) { + outRamp[l] = cmsmin(outRamp[l], outRamp[l+1]); + } + + // Check + if (! (outRamp[0] < outRamp[255])) { + + cmsDeleteTransform(hRoundTrip); + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } - if (!_cmsEndPointsBySpace(p -> ExitColorSpace, - &WhitePointOut, &BlackPointOut, &nOuts)) return FALSE; + // Test for mid range straight (only on relative colorimetric) + NearlyStraightMidrange = TRUE; + MinL = outRamp[0]; MaxL = outRamp[255]; + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + + for (l=0; l < 256; l++) { + + if (! ((inRamp[l] <= MinL + 0.2 * (MaxL - MinL) ) || + (fabs(inRamp[l] - outRamp[l]) < 4.0 ))) + NearlyStraightMidrange = FALSE; + } + + // If the mid range is straight (as determined above) then the + // DestinationBlackPoint shall be the same as initialLab. + // Otherwise, the DestinationBlackPoint shall be determined + // using curve fitting. + if (NearlyStraightMidrange) { - // Fix white only + cmsLab2XYZ(NULL, BlackPoint, &InitialLab); + cmsDeleteTransform(hRoundTrip); + return TRUE; + } + } + + + // curve fitting: The round-trip curve normally looks like a nearly constant section at the black point, + // with a corner and a nearly straight line to the white point. + for (l=0; l < 256; l++) { + + yRamp[l] = (outRamp[l] - MinL) / (MaxL - MinL); + } + + // find the black point using the least squares error quadratic curve fitting + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + lo = 0.1; + hi = 0.5; + } + else { - PatchLUT(p -> DeviceLink, WhitePointIn, WhitePointOut, nOuts, nIns); - // PatchLUT(p -> DeviceLink, BlackPointIn, BlackPointOut, nOuts, nIns); + // Perceptual and saturation + lo = 0.03; + hi = 0.25; + } + + // Capture shadow points for the fitting. + n = 0; + for (l=0; l < 256; l++) { + + cmsFloat64Number ff = yRamp[l]; + + if (ff >= lo && ff < hi) { + x[n] = inRamp[l]; + y[n] = yRamp[l]; + n++; + } + } + - return TRUE; + // No suitable points + if (n < 3 ) { + cmsDeleteTransform(hRoundTrip); + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + + // fit and get the vertex of quadratic curve + Lab.L = RootOfLeastSquaresFitQuadraticCurve(n, x, y); + + if (Lab.L < 0.0) { // clip to zero L* if the vertex is negative + Lab.L = 0; + } + + Lab.a = InitialLab.a; + Lab.b = InitialLab.b; + + cmsLab2XYZ(NULL, BlackPoint, &Lab); + + cmsDeleteTransform(hRoundTrip); + return TRUE; } - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmssm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/cmssm.c Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,765 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// ------------------------------------------------------------------------ + +// Gamut boundary description by using Jan Morovic's Segment maxima method +// Many thanks to Jan for allowing me to use his algorithm. + +// r = C* +// alpha = Hab +// theta = L* + +#define SECTORS 16 // number of divisions in alpha and theta + +// Spherical coordinates +typedef struct { + + cmsFloat64Number r; + cmsFloat64Number alpha; + cmsFloat64Number theta; + +} cmsSpherical; + +typedef enum { + GP_EMPTY, + GP_SPECIFIED, + GP_MODELED + + } GDBPointType; + + +typedef struct { + + GDBPointType Type; + cmsSpherical p; // Keep also alpha & theta of maximum + +} cmsGDBPoint; + + +typedef struct { + + cmsContext ContextID; + cmsGDBPoint Gamut[SECTORS][SECTORS]; + +} cmsGDB; + + +// A line using the parametric form +// P = a + t*u +typedef struct { + + cmsVEC3 a; + cmsVEC3 u; + +} cmsLine; + + +// A plane using the parametric form +// Q = b + r*v + s*w +typedef struct { + + cmsVEC3 b; + cmsVEC3 v; + cmsVEC3 w; + +} cmsPlane; + + + +// -------------------------------------------------------------------------------------------- + +// ATAN2() which always returns degree positive numbers + +static +cmsFloat64Number _cmsAtan2(cmsFloat64Number y, cmsFloat64Number x) +{ + cmsFloat64Number a; + + // Deal with undefined case + if (x == 0.0 && y == 0.0) return 0; + + a = (atan2(y, x) * 180.0) / M_PI; + + while (a < 0) { + a += 360; + } + + return a; +} + +// Convert to spherical coordinates +static +void ToSpherical(cmsSpherical* sp, const cmsVEC3* v) +{ + + cmsFloat64Number L, a, b; + + L = v ->n[VX]; + a = v ->n[VY]; + b = v ->n[VZ]; + + sp ->r = sqrt( L*L + a*a + b*b ); + + if (sp ->r == 0) { + sp ->alpha = sp ->theta = 0; + return; + } + + sp ->alpha = _cmsAtan2(a, b); + sp ->theta = _cmsAtan2(sqrt(a*a + b*b), L); +} + + +// Convert to cartesian from spherical +static +void ToCartesian(cmsVEC3* v, const cmsSpherical* sp) +{ + cmsFloat64Number sin_alpha; + cmsFloat64Number cos_alpha; + cmsFloat64Number sin_theta; + cmsFloat64Number cos_theta; + cmsFloat64Number L, a, b; + + sin_alpha = sin((M_PI * sp ->alpha) / 180.0); + cos_alpha = cos((M_PI * sp ->alpha) / 180.0); + sin_theta = sin((M_PI * sp ->theta) / 180.0); + cos_theta = cos((M_PI * sp ->theta) / 180.0); + + a = sp ->r * sin_theta * sin_alpha; + b = sp ->r * sin_theta * cos_alpha; + L = sp ->r * cos_theta; + + v ->n[VX] = L; + v ->n[VY] = a; + v ->n[VZ] = b; +} + + +// Quantize sector of a spherical coordinate. Saturate 360, 180 to last sector +// The limits are the centers of each sector, so +static +void QuantizeToSector(const cmsSpherical* sp, int* alpha, int* theta) +{ + *alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) ); + *theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) ); + + if (*alpha >= SECTORS) + *alpha = SECTORS-1; + if (*theta >= SECTORS) + *theta = SECTORS-1; +} + + +// Line determined by 2 points +static +void LineOf2Points(cmsLine* line, cmsVEC3* a, cmsVEC3* b) +{ + + _cmsVEC3init(&line ->a, a ->n[VX], a ->n[VY], a ->n[VZ]); + _cmsVEC3init(&line ->u, b ->n[VX] - a ->n[VX], + b ->n[VY] - a ->n[VY], + b ->n[VZ] - a ->n[VZ]); +} + + +// Evaluate parametric line +static +void GetPointOfLine(cmsVEC3* p, const cmsLine* line, cmsFloat64Number t) +{ + p ->n[VX] = line ->a.n[VX] + t * line->u.n[VX]; + p ->n[VY] = line ->a.n[VY] + t * line->u.n[VY]; + p ->n[VZ] = line ->a.n[VZ] + t * line->u.n[VZ]; +} + + + +/* + Closest point in sector line1 to sector line2 (both are defined as 0 <=t <= 1) + http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm + + Copyright 2001, softSurfer (www.softsurfer.com) + This code may be freely used and modified for any purpose + providing that this copyright notice is included with it. + SoftSurfer makes no warranty for this code, and cannot be held + liable for any real or imagined damage resulting from its use. + Users of this code must verify correctness for their application. + +*/ + +static +cmsBool ClosestLineToLine(cmsVEC3* r, const cmsLine* line1, const cmsLine* line2) +{ + cmsFloat64Number a, b, c, d, e, D; + cmsFloat64Number sc, sN, sD; + //cmsFloat64Number tc; // left for future use + cmsFloat64Number tN, tD; + cmsVEC3 w0; + + _cmsVEC3minus(&w0, &line1 ->a, &line2 ->a); + + a = _cmsVEC3dot(&line1 ->u, &line1 ->u); + b = _cmsVEC3dot(&line1 ->u, &line2 ->u); + c = _cmsVEC3dot(&line2 ->u, &line2 ->u); + d = _cmsVEC3dot(&line1 ->u, &w0); + e = _cmsVEC3dot(&line2 ->u, &w0); + + D = a*c - b * b; // Denominator + sD = tD = D; // default sD = D >= 0 + + if (D < MATRIX_DET_TOLERANCE) { // the lines are almost parallel + + sN = 0.0; // force using point P0 on segment S1 + sD = 1.0; // to prevent possible division by 0.0 later + tN = e; + tD = c; + } + else { // get the closest points on the infinite lines + + sN = (b*e - c*d); + tN = (a*e - b*d); + + if (sN < 0.0) { // sc < 0 => the s=0 edge is visible + + sN = 0.0; + tN = e; + tD = c; + } + else if (sN > sD) { // sc > 1 => the s=1 edge is visible + sN = sD; + tN = e + b; + tD = c; + } + } + + if (tN < 0.0) { // tc < 0 => the t=0 edge is visible + + tN = 0.0; + // recompute sc for this edge + if (-d < 0.0) + sN = 0.0; + else if (-d > a) + sN = sD; + else { + sN = -d; + sD = a; + } + } + else if (tN > tD) { // tc > 1 => the t=1 edge is visible + + tN = tD; + + // recompute sc for this edge + if ((-d + b) < 0.0) + sN = 0; + else if ((-d + b) > a) + sN = sD; + else { + sN = (-d + b); + sD = a; + } + } + // finally do the division to get sc and tc + sc = (fabs(sN) < MATRIX_DET_TOLERANCE ? 0.0 : sN / sD); + //tc = (fabs(tN) < MATRIX_DET_TOLERANCE ? 0.0 : tN / tD); // left for future use. + + GetPointOfLine(r, line1, sc); + return TRUE; +} + + + +// ------------------------------------------------------------------ Wrapper + + +// Allocate & free structure +cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID) +{ + cmsGDB* gbd = (cmsGDB*) _cmsMallocZero(ContextID, sizeof(cmsGDB)); + if (gbd == NULL) return NULL; + + gbd -> ContextID = ContextID; + + return (cmsHANDLE) gbd; +} + + +void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + if (hGBD != NULL) + _cmsFree(gbd->ContextID, (void*) gbd); +} + + +// Auxiliary to retrieve a pointer to the segmentr containing the Lab value +static +cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp) +{ + cmsVEC3 v; + int alpha, theta; + + // Housekeeping + _cmsAssert(gbd != NULL); + _cmsAssert(Lab != NULL); + _cmsAssert(sp != NULL); + + // Center L* by subtracting half of its domain, that's 50 + _cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b); + + // Convert to spherical coordinates + ToSpherical(sp, &v); + + if (sp ->r < 0 || sp ->alpha < 0 || sp->theta < 0) { + cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, "spherical value out of range"); + return NULL; + } + + // On which sector it falls? + QuantizeToSector(sp, &alpha, &theta); + + if (alpha < 0 || theta < 0 || alpha >= SECTORS || theta >= SECTORS) { + cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, " quadrant out of range"); + return NULL; + } + + // Get pointer to the sector + return &gbd ->Gamut[theta][alpha]; +} + +// Add a point to gamut descriptor. Point to add is in Lab color space. +// GBD is centered on a=b=0 and L*=50 +cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* ptr; + cmsSpherical sp; + + + // Get pointer to the sector + ptr = GetPoint(gbd, Lab, &sp); + if (ptr == NULL) return FALSE; + + // If no samples at this sector, add it + if (ptr ->Type == GP_EMPTY) { + + ptr -> Type = GP_SPECIFIED; + ptr -> p = sp; + } + else { + + + // Substitute only if radius is greater + if (sp.r > ptr -> p.r) { + + ptr -> Type = GP_SPECIFIED; + ptr -> p = sp; + } + } + + return TRUE; +} + +// Check if a given point falls inside gamut +cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* ptr; + cmsSpherical sp; + + // Get pointer to the sector + ptr = GetPoint(gbd, Lab, &sp); + if (ptr == NULL) return FALSE; + + // If no samples at this sector, return no data + if (ptr ->Type == GP_EMPTY) return FALSE; + + // In gamut only if radius is greater + + return (sp.r <= ptr -> p.r); +} + +// ----------------------------------------------------------------------------------------------------------------------- + +// Find near sectors. The list of sectors found is returned on Close[]. +// The function returns the number of sectors as well. + +// 24 9 10 11 12 +// 23 8 1 2 13 +// 22 7 * 3 14 +// 21 6 5 4 15 +// 20 19 18 17 16 +// +// Those are the relative movements +// {-2,-2}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, +// {-2,-1}, {-1, -1}, {0, -1}, {+1, -1}, {+2, -1}, +// {-2, 0}, {-1, 0}, {0, 0}, {+1, 0}, {+2, 0}, +// {-2,+1}, {-1, +1}, {0, +1}, {+1, +1}, {+2, +1}, +// {-2,+2}, {-1, +2}, {0, +2}, {+1, +2}, {+2, +2}}; + + +static +const struct _spiral { + + int AdvX, AdvY; + + } Spiral[] = { {0, -1}, {+1, -1}, {+1, 0}, {+1, +1}, {0, +1}, {-1, +1}, + {-1, 0}, {-1, -1}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, + {+2, -1}, {+2, 0}, {+2, +1}, {+2, +2}, {+1, +2}, {0, +2}, + {-1, +2}, {-2, +2}, {-2, +1}, {-2, 0}, {-2, -1}, {-2, -2} }; + +#define NSTEPS (sizeof(Spiral) / sizeof(struct _spiral)) + +static +int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[]) +{ + int nSectors = 0; + int a, t; + cmsUInt32Number i; + cmsGDBPoint* pt; + + for (i=0; i < NSTEPS; i++) { + + a = alpha + Spiral[i].AdvX; + t = theta + Spiral[i].AdvY; + + // Cycle at the end + a %= SECTORS; + t %= SECTORS; + + // Cycle at the begin + if (a < 0) a = SECTORS + a; + if (t < 0) t = SECTORS + t; + + pt = &gbd ->Gamut[t][a]; + + if (pt -> Type != GP_EMPTY) { + + Close[nSectors++] = pt; + } + } + + return nSectors; +} + + +// Interpolate a missing sector. Method identifies whatever this is top, bottom or mid +static +cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta) +{ + cmsSpherical sp; + cmsVEC3 Lab; + cmsVEC3 Centre; + cmsLine ray; + int nCloseSectors; + cmsGDBPoint* Close[NSTEPS + 1]; + cmsSpherical closel, templ; + cmsLine edge; + int k, m; + + // Is that point already specified? + if (gbd ->Gamut[theta][alpha].Type != GP_EMPTY) return TRUE; + + // Fill close points + nCloseSectors = FindNearSectors(gbd, alpha, theta, Close); + + + // Find a central point on the sector + sp.alpha = (cmsFloat64Number) ((alpha + 0.5) * 360.0) / (SECTORS); + sp.theta = (cmsFloat64Number) ((theta + 0.5) * 180.0) / (SECTORS); + sp.r = 50.0; + + // Convert to Cartesian + ToCartesian(&Lab, &sp); + + // Create a ray line from centre to this point + _cmsVEC3init(&Centre, 50.0, 0, 0); + LineOf2Points(&ray, &Lab, &Centre); + + // For all close sectors + closel.r = 0.0; + closel.alpha = 0; + closel.theta = 0; + + for (k=0; k < nCloseSectors; k++) { + + for(m = k+1; m < nCloseSectors; m++) { + + cmsVEC3 temp, a1, a2; + + // A line from sector to sector + ToCartesian(&a1, &Close[k]->p); + ToCartesian(&a2, &Close[m]->p); + + LineOf2Points(&edge, &a1, &a2); + + // Find a line + ClosestLineToLine(&temp, &ray, &edge); + + // Convert to spherical + ToSpherical(&templ, &temp); + + + if ( templ.r > closel.r && + templ.theta >= (theta*180.0/SECTORS) && + templ.theta <= ((theta+1)*180.0/SECTORS) && + templ.alpha >= (alpha*360.0/SECTORS) && + templ.alpha <= ((alpha+1)*360.0/SECTORS)) { + + closel = templ; + } + } + } + + gbd ->Gamut[theta][alpha].p = closel; + gbd ->Gamut[theta][alpha].Type = GP_MODELED; + + return TRUE; + +} + + +// Interpolate missing parts. The algorithm fist computes slices at +// theta=0 and theta=Max. +cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags) +{ + int alpha, theta; + cmsGDB* gbd = (cmsGDB*) hGBD; + + _cmsAssert(hGBD != NULL); + + // Interpolate black + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE; + } + + // Interpolate white + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE; + } + + + // Interpolate Mid + for (theta = 1; theta < SECTORS; theta++) { + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE; + } + } + + // Done + return TRUE; + + cmsUNUSED_PARAMETER(dwFlags); +} + + + + +// -------------------------------------------------------------------------------------------------------- + +// Great for debug, but not suitable for real use + +#if 0 +cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname) +{ + FILE* fp; + int i, j; + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* pt; + + fp = fopen (fname, "wt"); + if (fp == NULL) + return FALSE; + + fprintf (fp, "#VRML V2.0 utf8\n"); + + // set the viewing orientation and distance + fprintf (fp, "DEF CamTest Group {\n"); + fprintf (fp, "\tchildren [\n"); + fprintf (fp, "\t\tDEF Cameras Group {\n"); + fprintf (fp, "\t\t\tchildren [\n"); + fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n"); + fprintf (fp, "\t\t\t\t\tposition 0 0 340\n"); + fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n"); + fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t]\n"); + fprintf (fp, "\t\t},\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + // Output the background stuff + fprintf (fp, "Background {\n"); + fprintf (fp, "\tskyColor [\n"); + fprintf (fp, "\t\t.5 .5 .5\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + // Output the shape stuff + fprintf (fp, "Transform {\n"); + fprintf (fp, "\tscale .3 .3 .3\n"); + fprintf (fp, "\tchildren [\n"); + + // Draw the axes as a shape: + fprintf (fp, "\t\tShape {\n"); + fprintf (fp, "\t\t\tappearance Appearance {\n"); + fprintf (fp, "\t\t\t\tmaterial Material {\n"); + fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); + fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n"); + fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n"); + fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); + fprintf (fp, "\t\t\t\t\tpoint [\n"); + fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n"); + fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0); + fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0); + fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t\tcoordIndex [\n"); + fprintf (fp, "\t\t\t\t\t0, 1, -1\n"); + fprintf (fp, "\t\t\t\t\t0, 2, -1\n"); + fprintf (fp, "\t\t\t\t\t0, 3, -1]\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t}\n"); + + + fprintf (fp, "\t\tShape {\n"); + fprintf (fp, "\t\t\tappearance Appearance {\n"); + fprintf (fp, "\t\t\t\tmaterial Material {\n"); + fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); + fprintf (fp, "\t\t\t\t\temissiveColor 1 1 1\n"); + fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t\tgeometry PointSet {\n"); + + // fill in the points here + fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); + fprintf (fp, "\t\t\t\t\tpoint [\n"); + + // We need to transverse all gamut hull. + for (i=0; i < SECTORS; i++) + for (j=0; j < SECTORS; j++) { + + cmsVEC3 v; + + pt = &gbd ->Gamut[i][j]; + ToCartesian(&v, &pt ->p); + + fprintf (fp, "\t\t\t\t\t%g %g %g", v.n[0]+50, v.n[1], v.n[2]); + + if ((j == SECTORS - 1) && (i == SECTORS - 1)) + fprintf (fp, "]\n"); + else + fprintf (fp, ",\n"); + + } + + fprintf (fp, "\t\t\t\t}\n"); + + + + // fill in the face colors + fprintf (fp, "\t\t\t\tcolor Color {\n"); + fprintf (fp, "\t\t\t\t\tcolor [\n"); + + for (i=0; i < SECTORS; i++) + for (j=0; j < SECTORS; j++) { + + cmsVEC3 v; + + pt = &gbd ->Gamut[i][j]; + + + ToCartesian(&v, &pt ->p); + + + if (pt ->Type == GP_EMPTY) + fprintf (fp, "\t\t\t\t\t%g %g %g", 0.0, 0.0, 0.0); + else + if (pt ->Type == GP_MODELED) + fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5); + else { + fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, 1.0, 1.0); + + } + + if ((j == SECTORS - 1) && (i == SECTORS - 1)) + fprintf (fp, "]\n"); + else + fprintf (fp, ",\n"); + } + fprintf (fp, "\t\t\t}\n"); + + + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t}\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + fclose (fp); + + return TRUE; +} +#endif + diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmstypes.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/cmstypes.c Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,5662 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Tag Serialization ----------------------------------------------------------------------------- +// This file implements every single tag and tag type as described in the ICC spec. Some types +// have been deprecated, like ncl and Data. There is no implementation for those types as there +// are no profiles holding them. The programmer can also extend this list by defining his own types +// by using the appropriate plug-in. There are three types of plug ins regarding that. First type +// allows to define new tags using any existing type. Next plug-in type allows to define new types +// and the third one is very specific: allows to extend the number of elements in the multiprocessing +// elements special type. +//-------------------------------------------------------------------------------------------------- + +// Some broken types +#define cmsCorbisBrokenXYZtype ((cmsTagTypeSignature) 0x17A505B8) +#define cmsMonacoBrokenCurveType ((cmsTagTypeSignature) 0x9478ee00) + +// This is the linked list that keeps track of the defined types +typedef struct _cmsTagTypeLinkedList_st { + + cmsTagTypeHandler Handler; + struct _cmsTagTypeLinkedList_st* Next; + +} _cmsTagTypeLinkedList; + +// Some macros to define callbacks. +#define READ_FN(x) Type_##x##_Read +#define WRITE_FN(x) Type_##x##_Write +#define FREE_FN(x) Type_##x##_Free +#define DUP_FN(x) Type_##x##_Dup + +// Helper macro to define a handler. Callbacks do have a fixed naming convention. +#define TYPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), DUP_FN(x), FREE_FN(x), NULL, 0 } + +// Helper macro to define a MPE handler. Callbacks do have a fixed naming convention +#define TYPE_MPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), GenericMPEdup, GenericMPEfree, NULL, 0 } + +// Infinites +#define MINUS_INF (-1E22F) +#define PLUS_INF (+1E22F) + + +// Register a new type handler. This routine is shared between normal types and MPE. LinkedList points to the optional list head +static +cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos) +{ + cmsPluginTagType* Plugin = (cmsPluginTagType*) Data; + _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(id, pos); + _cmsTagTypeLinkedList *pt; + + // Calling the function with NULL as plug-in would unregister the plug in. + if (Data == NULL) { + + // There is no need to set free the memory, as pool is destroyed as a whole. + ctx ->TagTypes = NULL; + return TRUE; + } + + // Registering happens in plug-in memory pool. + pt = (_cmsTagTypeLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagTypeLinkedList)); + if (pt == NULL) return FALSE; + + pt ->Handler = Plugin ->Handler; + pt ->Next = ctx ->TagTypes; + + ctx ->TagTypes = pt; + + return TRUE; +} + +// Return handler for a given type or NULL if not found. Shared between normal types and MPE. It first tries the additons +// made by plug-ins and then the built-in defaults. +static +cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList) +{ + _cmsTagTypeLinkedList* pt; + + for (pt = PluginLinkedList; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Handler.Signature) return &pt ->Handler; + } + + for (pt = DefaultLinkedList; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Handler.Signature) return &pt ->Handler; + } + + return NULL; +} + + +// Auxiliary to convert UTF-32 to UTF-16 in some cases +static +cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + _cmsAssert(!(Array == NULL && n > 0)); + + for (i=0; i < n; i++) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Array[i])) return FALSE; + } + + return TRUE; +} + +// Auxiliary to read an array of wchar_t +static +cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array) +{ + cmsUInt32Number i; + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + for (i=0; i < n; i++) { + + if (Array != NULL) { + + if (!_cmsReadUInt16Number(io, &tmp)) return FALSE; + Array[i] = (wchar_t) tmp; + } + else { + if (!_cmsReadUInt16Number(io, NULL)) return FALSE; + } + + } + return TRUE; +} + +// To deal with position tables +typedef cmsBool (* PositionTableEntryFn)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag); + +// Helper function to deal with position tables as described in ICC spec 4.3 +// A table of n elements is readed, where first comes n records containing offsets and sizes and +// then a block containing the data itself. This allows to reuse same data in more than one entry +static +cmsBool ReadPositionTable(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number Count, + cmsUInt32Number BaseOffset, + void *Cargo, + PositionTableEntryFn ElementFn) +{ + cmsUInt32Number i; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; + cmsUInt32Number currentPosition; + + currentPosition = io->Tell(io); + + // Verify there is enough space left to read at least two cmsUInt32Number items for Count items. + if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count) + return FALSE; + + // Let's take the offsets to each element + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + for (i=0; i < Count; i++) { + + if (!_cmsReadUInt32Number(io, &ElementOffsets[i])) goto Error; + if (!_cmsReadUInt32Number(io, &ElementSizes[i])) goto Error; + + ElementOffsets[i] += BaseOffset; + } + + // Seek to each element and read it + for (i=0; i < Count; i++) { + + if (!io -> Seek(io, ElementOffsets[i])) goto Error; + + // This is the reader callback + if (!ElementFn(self, io, Cargo, i, ElementSizes[i])) goto Error; + } + + // Success + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return FALSE; +} + +// Same as anterior, but for write position tables +static +cmsBool WritePositionTable(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number SizeOfTag, + cmsUInt32Number Count, + cmsUInt32Number BaseOffset, + void *Cargo, + PositionTableEntryFn ElementFn) +{ + cmsUInt32Number i; + cmsUInt32Number DirectoryPos, CurrentPos, Before; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; + + // Create table + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + // Keep starting position of curve offsets + DirectoryPos = io ->Tell(io); + + // Write a fake directory to be filled latter on + for (i=0; i < Count; i++) { + + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size + } + + // Write each element. Keep track of the size as well. + for (i=0; i < Count; i++) { + + Before = io ->Tell(io); + ElementOffsets[i] = Before - BaseOffset; + + // Callback to write... + if (!ElementFn(self, io, Cargo, i, SizeOfTag)) goto Error; + + // Now the size + ElementSizes[i] = io ->Tell(io) - Before; + } + + // Write the directory + CurrentPos = io ->Tell(io); + if (!io ->Seek(io, DirectoryPos)) goto Error; + + for (i=0; i < Count; i++) { + if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; + if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; + } + + if (!io ->Seek(io, CurrentPos)) goto Error; + + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return FALSE; +} + + +// ******************************************************************************** +// Type XYZ. Only one value is allowed +// ******************************************************************************** + +//The XYZType contains an array of three encoded values for the XYZ tristimulus +//values. Tristimulus values must be non-negative. The signed encoding allows for +//implementation optimizations by minimizing the number of fixed formats. + + +static +void *Type_XYZ_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsCIEXYZ* xyz; + + *nItems = 0; + xyz = (cmsCIEXYZ*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIEXYZ)); + if (xyz == NULL) return NULL; + + if (!_cmsReadXYZNumber(io, xyz)) { + _cmsFree(self ->ContextID, xyz); + return NULL; + } + + *nItems = 1; + return (void*) xyz; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_XYZ_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + return _cmsWriteXYZNumber(io, (cmsCIEXYZ*) Ptr); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_XYZ_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIEXYZ)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_XYZ_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +static +cmsTagTypeSignature DecideXYZtype(cmsFloat64Number ICCVersion, const void *Data) +{ + return cmsSigXYZType; + + cmsUNUSED_PARAMETER(ICCVersion); + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type chromaticity. Only one value is allowed +// ******************************************************************************** +// The chromaticity tag type provides basic chromaticity data and type of +// phosphors or colorants of a monitor to applications and utilities. + +static +void *Type_Chromaticity_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsCIExyYTRIPLE* chrm; + cmsUInt16Number nChans, Table; + + *nItems = 0; + chrm = (cmsCIExyYTRIPLE*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIExyYTRIPLE)); + if (chrm == NULL) return NULL; + + if (!_cmsReadUInt16Number(io, &nChans)) goto Error; + + // Let's recover from a bug introduced in early versions of lcms1 + if (nChans == 0 && SizeOfTag == 32) { + + if (!_cmsReadUInt16Number(io, NULL)) goto Error; + if (!_cmsReadUInt16Number(io, &nChans)) goto Error; + } + + if (nChans != 3) goto Error; + + if (!_cmsReadUInt16Number(io, &Table)) goto Error; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Red.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Red.y)) goto Error; + + chrm ->Red.Y = 1.0; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Green.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Green.y)) goto Error; + + chrm ->Green.Y = 1.0; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.y)) goto Error; + + chrm ->Blue.Y = 1.0; + + *nItems = 1; + return (void*) chrm; + +Error: + _cmsFree(self ->ContextID, (void*) chrm); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io) +{ + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(x))) return FALSE; + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(y))) return FALSE; + + return TRUE; +} + +static +cmsBool Type_Chromaticity_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsCIExyYTRIPLE* chrm = (cmsCIExyYTRIPLE*) Ptr; + + if (!_cmsWriteUInt16Number(io, 3)) return FALSE; // nChannels + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Table + + if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, io)) return FALSE; + if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, io)) return FALSE; + if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, io)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Chromaticity_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIExyYTRIPLE)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Chromaticity_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigColorantOrderType +// ******************************************************************************** + +// This is an optional tag which specifies the laydown order in which colorants will +// be printed on an n-colorant device. The laydown order may be the same as the +// channel generation order listed in the colorantTableTag or the channel order of a +// colour space such as CMYK, in which case this tag is not needed. When this is not +// the case (for example, ink-towers sometimes use the order KCMY), this tag may be +// used to specify the laydown order of the colorants. + + +static +void *Type_ColorantOrderType_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number* ColorantOrder; + cmsUInt32Number Count; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + if (Count > cmsMAXCHANNELS) return NULL; + + ColorantOrder = (cmsUInt8Number*) _cmsCalloc(self ->ContextID, cmsMAXCHANNELS, sizeof(cmsUInt8Number)); + if (ColorantOrder == NULL) return NULL; + + // We use FF as end marker + memset(ColorantOrder, 0xFF, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); + + if (io ->Read(io, ColorantOrder, sizeof(cmsUInt8Number), Count) != Count) { + + _cmsFree(self ->ContextID, (void*) ColorantOrder); + return NULL; + } + + *nItems = 1; + return (void*) ColorantOrder; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_ColorantOrderType_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt8Number* ColorantOrder = (cmsUInt8Number*) Ptr; + cmsUInt32Number i, sz, Count; + + // Get the length + for (Count=i=0; i < cmsMAXCHANNELS; i++) { + if (ColorantOrder[i] != 0xFF) Count++; + } + + if (!_cmsWriteUInt32Number(io, Count)) return FALSE; + + sz = Count * sizeof(cmsUInt8Number); + if (!io -> Write(io, sz, ColorantOrder)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_ColorantOrderType_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigS15Fixed16ArrayType +// ******************************************************************************** +// This type represents an array of generic 4-byte/32-bit fixed point quantity. +// The number of values is determined from the size of the tag. + +static +void *Type_S15Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsFloat64Number* array_double; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt32Number); + array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); + if (array_double == NULL) return NULL; + + for (i=0; i < n; i++) { + + if (!_cmsRead15Fixed16Number(io, &array_double[i])) { + + _cmsFree(self ->ContextID, array_double); + return NULL; + } + } + + *nItems = n; + return (void*) array_double; +} + +static +cmsBool Type_S15Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; + cmsUInt32Number i; + + for (i=0; i < nItems; i++) { + + if (!_cmsWrite15Fixed16Number(io, Value[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_S15Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); +} + + +static +void Type_S15Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigU16Fixed16ArrayType +// ******************************************************************************** +// This type represents an array of generic 4-byte/32-bit quantity. +// The number of values is determined from the size of the tag. + + +static +void *Type_U16Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsFloat64Number* array_double; + cmsUInt32Number v; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt32Number); + array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); + if (array_double == NULL) return NULL; + + for (i=0; i < n; i++) { + + if (!_cmsReadUInt32Number(io, &v)) { + _cmsFree(self ->ContextID, (void*) array_double); + return NULL; + } + + // Convert to cmsFloat64Number + array_double[i] = (cmsFloat64Number) (v / 65536.0); + } + + *nItems = n; + return (void*) array_double; +} + +static +cmsBool Type_U16Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; + cmsUInt32Number i; + + for (i=0; i < nItems; i++) { + + cmsUInt32Number v = (cmsUInt32Number) floor(Value[i]*65536.0 + 0.5); + + if (!_cmsWriteUInt32Number(io, v)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_U16Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); +} + +static +void Type_U16Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigSignatureType +// ******************************************************************************** +// +// The signatureType contains a four-byte sequence, Sequences of less than four +// characters are padded at the end with spaces, 20h. +// Typically this type is used for registered tags that can be displayed on many +// development systems as a sequence of four characters. + +static +void *Type_Signature_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSignature* SigPtr = (cmsSignature*) _cmsMalloc(self ->ContextID, sizeof(cmsSignature)); + if (SigPtr == NULL) return NULL; + + if (!_cmsReadUInt32Number(io, SigPtr)) return NULL; + *nItems = 1; + + return SigPtr; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_Signature_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSignature* SigPtr = (cmsSignature*) Ptr; + + return _cmsWriteUInt32Number(io, *SigPtr); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Signature_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsSignature)); +} + +static +void Type_Signature_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigTextType +// ******************************************************************************** +// +// The textType is a simple text structure that contains a 7-bit ASCII text string. +// The length of the string is obtained by subtracting 8 from the element size portion +// of the tag itself. This string must be terminated with a 00h byte. + +static +void *Type_Text_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + char* Text = NULL; + cmsMLU* mlu = NULL; + + // Create a container + mlu = cmsMLUalloc(self ->ContextID, 1); + if (mlu == NULL) return NULL; + + *nItems = 0; + + // We need to store the "\0" at the end, so +1 + if (SizeOfTag == UINT_MAX) goto Error; + + Text = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); + if (Text == NULL) goto Error; + + if (io -> Read(io, Text, sizeof(char), SizeOfTag) != SizeOfTag) goto Error; + + // Make sure text is properly ended + Text[SizeOfTag] = 0; + *nItems = 1; + + // Keep the result + if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; + + _cmsFree(self ->ContextID, Text); + return (void*) mlu; + +Error: + if (mlu != NULL) + cmsMLUfree(mlu); + if (Text != NULL) + _cmsFree(self ->ContextID, Text); + + return NULL; +} + +// The conversion implies to choose a language. So, we choose the actual language. +static +cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + cmsUInt32Number size; + cmsBool rc; + char* Text; + + // Get the size of the string. Note there is an extra "\0" at the end + size = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); + if (size == 0) return FALSE; // Cannot be zero! + + // Create memory + Text = (char*) _cmsMalloc(self ->ContextID, size); + if (Text == NULL) return FALSE; + + cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size); + + // Write it, including separator + rc = io ->Write(io, size, Text); + + _cmsFree(self ->ContextID, Text); + return rc; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_Text_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_Text_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + cmsMLUfree(mlu); + return; + + cmsUNUSED_PARAMETER(self); +} + +static +cmsTagTypeSignature DecideTextType(cmsFloat64Number ICCVersion, const void *Data) +{ + if (ICCVersion >= 4.0) + return cmsSigMultiLocalizedUnicodeType; + + return cmsSigTextType; + + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type cmsSigDataType +// ******************************************************************************** + +// General purpose data type +static +void *Type_Data_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCData* BinData; + cmsUInt32Number LenOfData; + + *nItems = 0; + + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + + LenOfData = SizeOfTag - sizeof(cmsUInt32Number); + if (LenOfData > INT_MAX) return NULL; + + BinData = (cmsICCData*) _cmsMalloc(self ->ContextID, sizeof(cmsICCData) + LenOfData - 1); + if (BinData == NULL) return NULL; + + BinData ->len = LenOfData; + if (!_cmsReadUInt32Number(io, &BinData->flag)) { + _cmsFree(self ->ContextID, BinData); + return NULL; + } + + if (io -> Read(io, BinData ->data, sizeof(cmsUInt8Number), LenOfData) != LenOfData) { + + _cmsFree(self ->ContextID, BinData); + return NULL; + } + + *nItems = 1; + + return (void*) BinData; +} + + +static +cmsBool Type_Data_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCData* BinData = (cmsICCData*) Ptr; + + if (!_cmsWriteUInt32Number(io, BinData ->flag)) return FALSE; + + return io ->Write(io, BinData ->len, BinData ->data); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Data_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsICCData* BinData = (cmsICCData*) Ptr; + + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCData) + BinData ->len - 1); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Data_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigTextDescriptionType +// ******************************************************************************** + +static +void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + char* Text = NULL; + cmsMLU* mlu = NULL; + cmsUInt32Number AsciiCount; + cmsUInt32Number i, UnicodeCode, UnicodeCount; + cmsUInt16Number ScriptCodeCode, Dummy; + cmsUInt8Number ScriptCodeCount; + + *nItems = 0; + + // One dword should be there + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + + // Read len of ASCII + if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Check for size + if (SizeOfTag < AsciiCount) return NULL; + + // All seems Ok, allocate the container + mlu = cmsMLUalloc(self ->ContextID, 1); + if (mlu == NULL) return NULL; + + // As many memory as size of tag + Text = (char*) _cmsMalloc(self ->ContextID, AsciiCount + 1); + if (Text == NULL) goto Error; + + // Read it + if (io ->Read(io, Text, sizeof(char), AsciiCount) != AsciiCount) goto Error; + SizeOfTag -= AsciiCount; + + // Make sure there is a terminator + Text[AsciiCount] = 0; + + // Set the MLU entry. From here we can be tolerant to wrong types + if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; + _cmsFree(self ->ContextID, (void*) Text); + Text = NULL; + + // Skip Unicode code + if (SizeOfTag < 2* sizeof(cmsUInt32Number)) goto Done; + if (!_cmsReadUInt32Number(io, &UnicodeCode)) goto Done; + if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done; + SizeOfTag -= 2* sizeof(cmsUInt32Number); + + if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done; + + for (i=0; i < UnicodeCount; i++) { + if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done; + } + SizeOfTag -= UnicodeCount*sizeof(cmsUInt16Number); + + // Skip ScriptCode code if present. Some buggy profiles does have less + // data that stricttly required. We need to skip it as this type may come + // embedded in other types. + + if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) { + + if (!_cmsReadUInt16Number(io, &ScriptCodeCode)) goto Done; + if (!_cmsReadUInt8Number(io, &ScriptCodeCount)) goto Done; + + // Skip rest of tag + for (i=0; i < 67; i++) { + if (!io ->Read(io, &Dummy, sizeof(cmsUInt8Number), 1)) goto Error; + } + } + +Done: + + *nItems = 1; + return mlu; + +Error: + if (Text) _cmsFree(self ->ContextID, (void*) Text); + if (mlu) cmsMLUfree(mlu); + return NULL; +} + + +// This tag can come IN UNALIGNED SIZE. In order to prevent issues, we force zeros on description to align it +static +cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + char *Text = NULL; + wchar_t *Wide = NULL; + cmsUInt32Number len, len_text, len_tag_requirement, len_aligned; + cmsBool rc = FALSE; + char Filler[68]; + + // Used below for writing zeroes + memset(Filler, 0, sizeof(Filler)); + + // Get the len of string + len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); + + // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data + //(see clause 4.1 for the definition of “aligned”). Because the Unicode language + // code and Unicode count immediately follow the ASCII description, their + // alignment is not correct if the ASCII count is not a multiple of four. The + // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and + // writing software must be written carefully in order to handle these alignment + // problems. + // + // The above last sentence suggest to handle alignment issues in the + // parser. The provided example (Table 69 on Page 60) makes this clear. + // The padding only in the ASCII count is not sufficient for a aligned tag + // size, with the same text size in ASCII and Unicode. + + // Null strings + if (len <= 0) { + + Text = (char*) _cmsDupMem(self ->ContextID, "", sizeof(char)); + Wide = (wchar_t*) _cmsDupMem(self ->ContextID, L"", sizeof(wchar_t)); + } + else { + // Create independent buffers + Text = (char*) _cmsCalloc(self ->ContextID, len, sizeof(char)); + if (Text == NULL) goto Error; + + Wide = (wchar_t*) _cmsCalloc(self ->ContextID, len, sizeof(wchar_t)); + if (Wide == NULL) goto Error; + + // Get both representations. + cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, len * sizeof(char)); + cmsMLUgetWide(mlu, cmsNoLanguage, cmsNoCountry, Wide, len * sizeof(wchar_t)); + } + + // Tell the real text len including the null terminator and padding + len_text = (cmsUInt32Number) strlen(Text) + 1; + // Compute an total tag size requirement + len_tag_requirement = (8+4+len_text+4+4+2*len_text+2+1+67); + len_aligned = _cmsALIGNLONG(len_tag_requirement); + + // * cmsUInt32Number count; * Description length + // * cmsInt8Number desc[count] * NULL terminated ascii string + // * cmsUInt32Number ucLangCode; * UniCode language code + // * cmsUInt32Number ucCount; * UniCode description length + // * cmsInt16Number ucDesc[ucCount];* The UniCode description + // * cmsUInt16Number scCode; * ScriptCode code + // * cmsUInt8Number scCount; * ScriptCode count + // * cmsInt8Number scDesc[67]; * ScriptCode Description + + if (!_cmsWriteUInt32Number(io, len_text)) goto Error; + if (!io ->Write(io, len_text, Text)) goto Error; + + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // ucLanguageCode + + if (!_cmsWriteUInt32Number(io, len_text)) goto Error; + // Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t) + if (!_cmsWriteWCharArray(io, len_text, Wide)) goto Error; + + // ScriptCode Code & count (unused) + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + if (!_cmsWriteUInt8Number(io, 0)) goto Error; + + if (!io ->Write(io, 67, Filler)) goto Error; + + // possibly add pad at the end of tag + if(len_aligned - len_tag_requirement > 0) + if (!io ->Write(io, len_aligned - len_tag_requirement, Filler)) goto Error; + + rc = TRUE; + +Error: + if (Text) _cmsFree(self ->ContextID, Text); + if (Wide) _cmsFree(self ->ContextID, Wide); + + return rc; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_Text_Description_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_Text_Description_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + + cmsMLUfree(mlu); + return; + + cmsUNUSED_PARAMETER(self); +} + + +static +cmsTagTypeSignature DecideTextDescType(cmsFloat64Number ICCVersion, const void *Data) +{ + if (ICCVersion >= 4.0) + return cmsSigMultiLocalizedUnicodeType; + + return cmsSigTextDescriptionType; + + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type cmsSigCurveType +// ******************************************************************************** + +static +void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number Count; + cmsToneCurve* NewGamma; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + switch (Count) { + + case 0: // Linear. + { + cmsFloat64Number SingleGamma = 1.0; + + NewGamma = cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); + if (!NewGamma) return NULL; + *nItems = 1; + return NewGamma; + } + + case 1: // Specified as the exponent of gamma function + { + cmsUInt16Number SingleGammaFixed; + cmsFloat64Number SingleGamma; + + if (!_cmsReadUInt16Number(io, &SingleGammaFixed)) return NULL; + SingleGamma = _cms8Fixed8toDouble(SingleGammaFixed); + + *nItems = 1; + return cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); + } + + default: // Curve + + if (Count > 0x7FFF) + return NULL; // This is to prevent bad guys for doing bad things + + NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL); + if (!NewGamma) return NULL; + + if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) { + cmsFreeToneCurve(NewGamma); + return NULL; + } + + *nItems = 1; + return NewGamma; + } + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Curve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Ptr; + + if (Curve ->nSegments == 1 && Curve ->Segments[0].Type == 1) { + + // Single gamma, preserve number + cmsUInt16Number SingleGammaFixed = _cmsDoubleTo8Fixed8(Curve ->Segments[0].Params[0]); + + if (!_cmsWriteUInt32Number(io, 1)) return FALSE; + if (!_cmsWriteUInt16Number(io, SingleGammaFixed)) return FALSE; + return TRUE; + + } + + if (!_cmsWriteUInt32Number(io, Curve ->nEntries)) return FALSE; + return _cmsWriteUInt16Array(io, Curve ->nEntries, Curve ->Table16); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Curve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_Curve_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsToneCurve* gamma = (cmsToneCurve*) Ptr; + + cmsFreeToneCurve(gamma); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigParametricCurveType +// ******************************************************************************** + + +// Decide which curve type to use on writing +static +cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Data; + + if (ICCVersion < 4.0) return cmsSigCurveType; + if (Curve ->nSegments != 1) return cmsSigCurveType; // Only 1-segment curves can be saved as parametric + if (Curve ->Segments[0].Type < 0) return cmsSigCurveType; // Only non-inverted curves + if (Curve ->Segments[0].Type > 5) return cmsSigCurveType; // Only ICC parametric curves + + return cmsSigParametricCurveType; +} + +static +void *Type_ParametricCurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + static const int ParamsByType[] = { 1, 3, 4, 5, 7 }; + cmsFloat64Number Params[10]; + cmsUInt16Number Type; + int i, n; + cmsToneCurve* NewGamma; + + if (!_cmsReadUInt16Number(io, &Type)) return NULL; + if (!_cmsReadUInt16Number(io, NULL)) return NULL; // Reserved + + if (Type > 4) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown parametric curve type '%d'", Type); + return NULL; + } + + memset(Params, 0, sizeof(Params)); + n = ParamsByType[Type]; + + for (i=0; i < n; i++) { + + if (!_cmsRead15Fixed16Number(io, &Params[i])) return NULL; + } + + NewGamma = cmsBuildParametricToneCurve(self ->ContextID, Type+1, Params); + + *nItems = 1; + return NewGamma; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_ParametricCurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Ptr; + int i, nParams, typen; + static const int ParamsByType[] = { 0, 1, 3, 4, 5, 7 }; + + typen = Curve -> Segments[0].Type; + + if (Curve ->nSegments > 1 || typen < 1) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Multisegment or Inverted parametric curves cannot be written"); + return FALSE; + } + + if (typen > 5) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported parametric curve"); + return FALSE; + } + + nParams = ParamsByType[typen]; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) (Curve ->Segments[0].Type - 1))) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Reserved + + for (i=0; i < nParams; i++) { + + if (!_cmsWrite15Fixed16Number(io, Curve -> Segments[0].Params[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_ParametricCurve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsToneCurve* gamma = (cmsToneCurve*) Ptr; + + cmsFreeToneCurve(gamma); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigDateTimeType +// ******************************************************************************** + +// A 12-byte value representation of the time and date, where the byte usage is assigned +// as specified in table 1. The actual values are encoded as 16-bit unsigned integers +// (uInt16Number - see 5.1.6). +// +// All the dateTimeNumber values in a profile shall be in Coordinated Universal Time +// (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local +// time to UTC when setting these values. Programmes that display these values may show +// the dateTimeNumber as UTC, show the equivalent local time (at current locale), or +// display both UTC and local versions of the dateTimeNumber. + +static +void *Type_DateTime_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsDateTimeNumber timestamp; + struct tm * NewDateTime; + + *nItems = 0; + NewDateTime = (struct tm*) _cmsMalloc(self ->ContextID, sizeof(struct tm)); + if (NewDateTime == NULL) return NULL; + + if (io->Read(io, ×tamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL; + + _cmsDecodeDateTimeNumber(×tamp, NewDateTime); + + *nItems = 1; + return NewDateTime; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_DateTime_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + struct tm * DateTime = (struct tm*) Ptr; + cmsDateTimeNumber timestamp; + + _cmsEncodeDateTimeNumber(×tamp, DateTime); + if (!io ->Write(io, sizeof(cmsDateTimeNumber), ×tamp)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_DateTime_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(struct tm)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_DateTime_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + + +// ******************************************************************************** +// Type icMeasurementType +// ******************************************************************************** + +/* +The measurementType information refers only to the internal profile data and is +meant to provide profile makers an alternative to the default measurement +specifications. +*/ + +static +void *Type_Measurement_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCMeasurementConditions mc; + + + memset(&mc, 0, sizeof(mc)); + + if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL; + if (!_cmsReadXYZNumber(io, &mc.Backing)) return NULL; + if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL; + if (!_cmsRead15Fixed16Number(io, &mc.Flare)) return NULL; + if (!_cmsReadUInt32Number(io, &mc.IlluminantType)) return NULL; + + *nItems = 1; + return _cmsDupMem(self ->ContextID, &mc, sizeof(cmsICCMeasurementConditions)); + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Measurement_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCMeasurementConditions* mc =(cmsICCMeasurementConditions*) Ptr; + + if (!_cmsWriteUInt32Number(io, mc->Observer)) return FALSE; + if (!_cmsWriteXYZNumber(io, &mc->Backing)) return FALSE; + if (!_cmsWriteUInt32Number(io, mc->Geometry)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, mc->Flare)) return FALSE; + if (!_cmsWriteUInt32Number(io, mc->IlluminantType)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Measurement_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCMeasurementConditions)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Measurement_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigMultiLocalizedUnicodeType +// ******************************************************************************** +// +// Do NOT trust SizeOfTag as there is an issue on the definition of profileSequenceDescTag. See the TechNote from +// Max Derhak and Rohit Patil about this: basically the size of the string table should be guessed and cannot be +// taken from the size of tag if this tag is embedded as part of bigger structures (profileSequenceDescTag, for instance) +// + +static +void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsMLU* mlu; + cmsUInt32Number Count, RecLen, NumOfWchar; + cmsUInt32Number SizeOfHeader; + cmsUInt32Number Len, Offset; + cmsUInt32Number i; + wchar_t* Block; + cmsUInt32Number BeginOfThisString, EndOfThisString, LargestPosition; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + if (!_cmsReadUInt32Number(io, &RecLen)) return NULL; + + if (RecLen != 12) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "multiLocalizedUnicodeType of len != 12 is not supported."); + return NULL; + } + + mlu = cmsMLUalloc(self ->ContextID, Count); + if (mlu == NULL) return NULL; + + mlu ->UsedEntries = Count; + + SizeOfHeader = 12 * Count + sizeof(_cmsTagBase); + LargestPosition = 0; + + for (i=0; i < Count; i++) { + + if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Language)) goto Error; + if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Country)) goto Error; + + // Now deal with Len and offset. + if (!_cmsReadUInt32Number(io, &Len)) goto Error; + if (!_cmsReadUInt32Number(io, &Offset)) goto Error; + + // Check for overflow + if (Offset < (SizeOfHeader + 8)) goto Error; + if (((Offset + Len) < Len) || ((Offset + Len) > SizeOfTag + 8)) goto Error; + + // True begin of the string + BeginOfThisString = Offset - SizeOfHeader - 8; + + // Ajust to wchar_t elements + mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + + // To guess maximum size, add offset + len + EndOfThisString = BeginOfThisString + Len; + if (EndOfThisString > LargestPosition) + LargestPosition = EndOfThisString; + } + + // Now read the remaining of tag and fill all strings. Subtract the directory + SizeOfTag = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + if (SizeOfTag == 0) + { + Block = NULL; + NumOfWchar = 0; + + } + else + { + Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag); + if (Block == NULL) goto Error; + NumOfWchar = SizeOfTag / sizeof(wchar_t); + if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error; + } + + mlu ->MemPool = Block; + mlu ->PoolSize = SizeOfTag; + mlu ->PoolUsed = SizeOfTag; + + *nItems = 1; + return (void*) mlu; + +Error: + if (mlu) cmsMLUfree(mlu); + return NULL; +} + +static +cmsBool Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu =(cmsMLU*) Ptr; + cmsUInt32Number HeaderSize; + cmsUInt32Number Len, Offset; + cmsUInt32Number i; + + if (Ptr == NULL) { + + // Empty placeholder + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 12)) return FALSE; + return TRUE; + } + + if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE; + if (!_cmsWriteUInt32Number(io, 12)) return FALSE; + + HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase); + + for (i=0; i < mlu ->UsedEntries; i++) { + + Len = mlu ->Entries[i].Len; + Offset = mlu ->Entries[i].StrW; + + Len = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t); + Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8; + + if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE; + if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country)) return FALSE; + if (!_cmsWriteUInt32Number(io, Len)) return FALSE; + if (!_cmsWriteUInt32Number(io, Offset)) return FALSE; + } + + if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*) mlu ->MemPool)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLUfree((cmsMLU*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigLut8Type +// ******************************************************************************** + +// Decide which LUT type to use on writing +static +cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsPipeline* Lut = (cmsPipeline*) Data; + + if (ICCVersion < 4.0) { + if (Lut ->SaveAs8Bits) return cmsSigLut8Type; + return cmsSigLut16Type; + } + else { + return cmsSigLutAtoBType; + } +} + +static +cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsPipeline* Lut = (cmsPipeline*) Data; + + if (ICCVersion < 4.0) { + if (Lut ->SaveAs8Bits) return cmsSigLut8Type; + return cmsSigLut16Type; + } + else { + return cmsSigLutBtoAType; + } +} + +/* +This structure represents a colour transform using tables of 8-bit precision. +This type contains four processing elements: a 3 by 3 matrix (which shall be +the identity matrix unless the input colour space is XYZ), a set of one dimensional +input tables, a multidimensional lookup table, and a set of one dimensional output +tables. Data is processed using these elements via the following sequence: +(matrix) -> (1d input tables) -> (multidimensional lookup table - CLUT) -> (1d output tables) + +Byte Position Field Length (bytes) Content Encoded as... +8 1 Number of Input Channels (i) uInt8Number +9 1 Number of Output Channels (o) uInt8Number +10 1 Number of CLUT grid points (identical for each side) (g) uInt8Number +11 1 Reserved for padding (fill with 00h) + +12..15 4 Encoded e00 parameter s15Fixed16Number +*/ + + +// Read 8 bit tables as gamma functions +static +cmsBool Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, cmsUInt32Number nChannels) +{ + cmsUInt8Number* Temp = NULL; + cmsUInt32Number i, j; + cmsToneCurve* Tables[cmsMAXCHANNELS]; + + if (nChannels > cmsMAXCHANNELS) return FALSE; + if (nChannels <= 0) return FALSE; + + memset(Tables, 0, sizeof(Tables)); + + Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256); + if (Temp == NULL) return FALSE; + + for (i=0; i < nChannels; i++) { + Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); + if (Tables[i] == NULL) goto Error; + } + + for (i=0; i < nChannels; i++) { + + if (io ->Read(io, Temp, 256, 1) != 1) goto Error; + + for (j=0; j < 256; j++) + Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]); + } + + _cmsFree(ContextID, Temp); + Temp = NULL; + + if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) + goto Error; + + for (i=0; i < nChannels; i++) + cmsFreeToneCurve(Tables[i]); + + return TRUE; + +Error: + for (i=0; i < nChannels; i++) { + if (Tables[i]) cmsFreeToneCurve(Tables[i]); + } + + if (Temp) _cmsFree(ContextID, Temp); + return FALSE; +} + + +static +cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables) +{ + int j; + cmsUInt32Number i; + cmsUInt8Number val; + + for (i=0; i < n; i++) { + + if (Tables) { + + // Usual case of identity curves + if ((Tables ->TheCurves[i]->nEntries == 2) && + (Tables->TheCurves[i]->Table16[0] == 0) && + (Tables->TheCurves[i]->Table16[1] == 65535)) { + + for (j=0; j < 256; j++) { + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE; + } + } + else + if (Tables ->TheCurves[i]->nEntries != 256) { + cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization"); + return FALSE; + } + else + for (j=0; j < 256; j++) { + + val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]); + + if (!_cmsWriteUInt8Number(io, val)) return FALSE; + } + } + } + return TRUE; +} + + +// Check overflow +static +cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b) +{ + cmsUInt32Number rv = 1, rc; + + if (a == 0) return 0; + if (n == 0) return 0; + + for (; b > 0; b--) { + + rv *= a; + + // Check for overflow + if (rv > UINT_MAX / a) return (cmsUInt32Number) -1; + + } + + rc = rv * n; + + if (rv != rc / n) return (cmsUInt32Number) -1; + return rc; +} + + +// That will create a MPE LUT with Matrix, pre tables, CLUT and post tables. +// 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust +// PCS on BToAxx tags and AtoB if abstract. We need to fix input direction. + +static +void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; + cmsUInt8Number* Temp = NULL; + cmsPipeline* NewLUT = NULL; + cmsUInt32Number nTabSize, i; + cmsFloat64Number Matrix[3*3]; + + *nItems = 0; + + if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error; + if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error; + if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error; + + if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + + // Padding + if (!_cmsReadUInt8Number(io, NULL)) goto Error; + + // Do some checking + if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; + if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; + + // Allocates an empty Pipeline + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); + if (NewLUT == NULL) goto Error; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; + + + // Only operates if not identity... + if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) + goto Error; + } + + // Get input tables + if (!Read8bitTables(self ->ContextID, io, NewLUT, InputChannels)) goto Error; + + // Get 3D CLUT. Check the overflow.... + nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) goto Error; + if (nTabSize > 0) { + + cmsUInt16Number *PtrW, *T; + + PtrW = T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); + if (T == NULL) goto Error; + + Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize); + if (Temp == NULL) { + _cmsFree(self ->ContextID, T); + goto Error; + } + + if (io ->Read(io, Temp, nTabSize, 1) != 1) { + _cmsFree(self ->ContextID, T); + _cmsFree(self ->ContextID, Temp); + goto Error; + } + + for (i = 0; i < nTabSize; i++) { + + *PtrW++ = FROM_8_TO_16(Temp[i]); + } + _cmsFree(self ->ContextID, Temp); + Temp = NULL; + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) { + _cmsFree(self ->ContextID, T); + goto Error; + } + _cmsFree(self ->ContextID, T); + } + + + // Get output tables + if (!Read8bitTables(self ->ContextID, io, NewLUT, OutputChannels)) goto Error; + + *nItems = 1; + return NewLUT; + +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin. +static +cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number j, nTabSize; + cmsUInt8Number val; + cmsPipeline* NewLUT = (cmsPipeline*) Ptr; + cmsStage* mpe; + _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; + _cmsStageMatrixData* MatMPE = NULL; + _cmsStageCLutData* clut = NULL; + cmsUInt32Number clutPoints; + + // Disassemble the LUT into components. + mpe = NewLUT -> Elements; + if (mpe ->Type == cmsSigMatrixElemType) { + + MatMPE = (_cmsStageMatrixData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { + clut = (_cmsStageCLutData*) mpe -> Data; + mpe = mpe ->Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + // That should be all + if (mpe != NULL) { + cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8"); + return FALSE; + } + + + if (clut == NULL) + clutPoints = 0; + else + clutPoints = clut->Params->nSamples[0]; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding + + + if (MatMPE != NULL) { + + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE; + + } + else { + + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + } + + // The prelinearization table + if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE; + + nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels); + if (nTabSize == (cmsUInt32Number) -1) return FALSE; + if (nTabSize > 0) { + + // The 3D CLUT. + if (clut != NULL) { + + for (j=0; j < nTabSize; j++) { + + val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]); + if (!_cmsWriteUInt8Number(io, val)) return FALSE; + } + } + } + + // The postlinearization table + if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// ******************************************************************************** +// Type cmsSigLut16Type +// ******************************************************************************** + +// Read 16 bit tables as gamma functions +static +cmsBool Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, + cmsUInt32Number nChannels, cmsUInt32Number nEntries) +{ + cmsUInt32Number i; + cmsToneCurve* Tables[cmsMAXCHANNELS]; + + // Maybe an empty table? (this is a lcms extension) + if (nEntries <= 0) return TRUE; + + // Check for malicious profiles + if (nEntries < 2) return FALSE; + if (nChannels > cmsMAXCHANNELS) return FALSE; + + // Init table to zero + memset(Tables, 0, sizeof(Tables)); + + for (i=0; i < nChannels; i++) { + + Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL); + if (Tables[i] == NULL) goto Error; + + if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error; + } + + + // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code) + if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) + goto Error; + + for (i=0; i < nChannels; i++) + cmsFreeToneCurve(Tables[i]); + + return TRUE; + +Error: + for (i=0; i < nChannels; i++) { + if (Tables[i]) cmsFreeToneCurve(Tables[i]); + } + + return FALSE; +} + +static +cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables) +{ + cmsUInt32Number j; + cmsUInt32Number i; + cmsUInt16Number val; + cmsUInt32Number nEntries; + + _cmsAssert(Tables != NULL); + + nEntries = Tables->TheCurves[0]->nEntries; + + for (i=0; i < Tables ->nCurves; i++) { + + for (j=0; j < nEntries; j++) { + + val = Tables->TheCurves[i]->Table16[j]; + if (!_cmsWriteUInt16Number(io, val)) return FALSE; + } + } + return TRUE; + + cmsUNUSED_PARAMETER(ContextID); +} + +static +void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; + cmsPipeline* NewLUT = NULL; + cmsUInt32Number nTabSize; + cmsFloat64Number Matrix[3*3]; + cmsUInt16Number InputEntries, OutputEntries; + + *nItems = 0; + + if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL; + if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL; + if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL; // 255 maximum + + // Padding + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + + // Do some checking + if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; + if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); + if (NewLUT == NULL) goto Error; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; + + + // Only operates on 3 channels + if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) + goto Error; + } + + if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error; + if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error; + + if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error; + if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + + // Get input tables + if (!Read16bitTables(self ->ContextID, io, NewLUT, InputChannels, InputEntries)) goto Error; + + // Get 3D CLUT + nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) goto Error; + if (nTabSize > 0) { + + cmsUInt16Number *T; + + T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); + if (T == NULL) goto Error; + + if (!_cmsReadUInt16Array(io, nTabSize, T)) { + _cmsFree(self ->ContextID, T); + goto Error; + } + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) { + _cmsFree(self ->ContextID, T); + goto Error; + } + _cmsFree(self ->ContextID, T); + } + + + // Get output tables + if (!Read16bitTables(self ->ContextID, io, NewLUT, OutputChannels, OutputEntries)) goto Error; + + *nItems = 1; + return NewLUT; + +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin. +// Some empty defaults are created for missing parts + +static +cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number nTabSize; + cmsPipeline* NewLUT = (cmsPipeline*) Ptr; + cmsStage* mpe; + _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; + _cmsStageMatrixData* MatMPE = NULL; + _cmsStageCLutData* clut = NULL; + cmsUInt32Number i, InputChannels, OutputChannels, clutPoints; + + // Disassemble the LUT into components. + mpe = NewLUT -> Elements; + if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) { + + MatMPE = (_cmsStageMatrixData*) mpe ->Data; + mpe = mpe -> Next; + } + + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { + clut = (_cmsStageCLutData*) mpe -> Data; + mpe = mpe ->Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + // That should be all + if (mpe != NULL) { + cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16"); + return FALSE; + } + + InputChannels = cmsPipelineInputChannels(NewLUT); + OutputChannels = cmsPipelineOutputChannels(NewLUT); + + if (clut == NULL) + clutPoints = 0; + else + clutPoints = clut->Params->nSamples[0]; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding + + + if (MatMPE != NULL) { + + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE; + } + else { + + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + } + + + if (PreMPE != NULL) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE; + } else { + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + } + + if (PostMPE != NULL) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE; + } else { + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + + } + + // The prelinearization table + + if (PreMPE != NULL) { + if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE; + } + else { + for (i=0; i < InputChannels; i++) { + + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; + } + } + + nTabSize = uipow(OutputChannels, clutPoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) return FALSE; + if (nTabSize > 0) { + // The 3D CLUT. + if (clut != NULL) { + if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE; + } + } + + // The postlinearization table + if (PostMPE != NULL) { + if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE; + } + else { + for (i=0; i < OutputChannels; i++) { + + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigLutAToBType +// ******************************************************************************** + + +// V4 stuff. Read matrix for LutAtoB and LutBtoA + +static +cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset) +{ + cmsFloat64Number dMat[3*3]; + cmsFloat64Number dOff[3]; + cmsStage* Mat; + + // Go to address + if (!io -> Seek(io, Offset)) return NULL; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL; + + if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL; + + Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff); + + return Mat; +} + + + + +// V4 stuff. Read CLUT part for LutAtoB and LutBtoA + +static +cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, + cmsUInt32Number Offset, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) +{ + cmsUInt8Number gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension. + cmsUInt32Number GridPoints[cmsMAXCHANNELS], i; + cmsUInt8Number Precision; + cmsStage* CLUT; + _cmsStageCLutData* Data; + + if (!io -> Seek(io, Offset)) return NULL; + if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL; + + + for (i=0; i < cmsMAXCHANNELS; i++) { + + if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least + GridPoints[i] = gridPoints8[i]; + } + + if (!_cmsReadUInt8Number(io, &Precision)) return NULL; + + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + + CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL); + if (CLUT == NULL) return NULL; + + Data = (_cmsStageCLutData*) CLUT ->Data; + + // Precision can be 1 or 2 bytes + if (Precision == 1) { + + cmsUInt8Number v; + + for (i=0; i < Data ->nEntries; i++) { + + if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) { + cmsStageFree(CLUT); + return NULL; + } + Data ->Tab.T[i] = FROM_8_TO_16(v); + } + + } + else + if (Precision == 2) { + + if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) { + cmsStageFree(CLUT); + return NULL; + } + } + else { + cmsStageFree(CLUT); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); + return NULL; + } + + return CLUT; +} + +static +cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) +{ + cmsTagTypeSignature BaseType; + cmsUInt32Number nItems; + + BaseType = _cmsReadTypeBase(io); + switch (BaseType) { + + case cmsSigCurveType: + return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0); + + case cmsSigParametricCurveType: + return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0); + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) BaseType); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); + } + return NULL; + } +} + + +// Read a set of curves from specific offset +static +cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves) +{ + cmsToneCurve* Curves[cmsMAXCHANNELS]; + cmsUInt32Number i; + cmsStage* Lin = NULL; + + if (nCurves > cmsMAXCHANNELS) return FALSE; + + if (!io -> Seek(io, Offset)) return FALSE; + + for (i=0; i < nCurves; i++) + Curves[i] = NULL; + + for (i=0; i < nCurves; i++) { + + Curves[i] = ReadEmbeddedCurve(self, io); + if (Curves[i] == NULL) goto Error; + if (!_cmsReadAlignment(io)) goto Error; + + } + + Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves); + +Error: + for (i=0; i < nCurves; i++) + cmsFreeToneCurve(Curves[i]); + + return Lin; +} + + +// LutAtoB type + +// This structure represents a colour transform. The type contains up to five processing +// elements which are stored in the AtoBTag tag in the following order: a set of one +// dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves, +// a multidimensional lookup table, and a set of one dimensional output curves. +// Data are processed using these elements via the following sequence: +// +//("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves). +// +/* +It is possible to use any or all of these processing elements. At least one processing element +must be included.Only the following combinations are allowed: + +B +M - Matrix - B +A - CLUT - B +A - CLUT - M - Matrix - B + +*/ + +static +void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number BaseOffset; + cmsUInt8Number inputChan; // Number of input channels + cmsUInt8Number outputChan; // Number of output channels + cmsUInt32Number offsetB; // Offset to first "B" curve + cmsUInt32Number offsetMat; // Offset to matrix + cmsUInt32Number offsetM; // Offset to first "M" curve + cmsUInt32Number offsetC; // Offset to CLUT + cmsUInt32Number offsetA; // Offset to first "A" curve + cmsPipeline* NewLUT = NULL; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; + if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; + + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; + + if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; + if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); + if (NewLUT == NULL) return NULL; + + if (offsetA!= 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan))) + goto Error; + } + + if (offsetC != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) + goto Error; + } + + if (offsetM != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan))) + goto Error; + } + + if (offsetMat != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) + goto Error; + } + + if (offsetB != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan))) + goto Error; + } + + *nItems = 1; + return NewLUT; +Error: + cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// Write a set of curves +static +cmsBool WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe) +{ + _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data; + + // Write the Matrix + if (!_cmsWrite15Fixed16Number(io, m -> Double[0])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[1])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[2])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[3])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[4])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[5])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[6])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[7])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[8])) return FALSE; + + if (m ->Offset != NULL) { + + if (!_cmsWrite15Fixed16Number(io, m -> Offset[0])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Offset[1])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Offset[2])) return FALSE; + } + else { + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + + } + + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + + +// Write a set of curves +static +cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe) +{ + cmsUInt32Number i, n; + cmsTagTypeSignature CurrentType; + cmsToneCurve** Curves; + + + n = cmsStageOutputChannels(mpe); + Curves = _cmsStageGetPtrToCurveSet(mpe); + + for (i=0; i < n; i++) { + + // If this is a table-based curve, use curve type even on V4 + CurrentType = Type; + + if ((Curves[i] ->nSegments == 0)|| + ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) ) + CurrentType = cmsSigCurveType; + else + if (Curves[i] ->Segments[0].Type < 0) + CurrentType = cmsSigCurveType; + + if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE; + + switch (CurrentType) { + + case cmsSigCurveType: + if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE; + break; + + case cmsSigParametricCurveType: + if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE; + break; + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) Type); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); + } + return FALSE; + } + + if (!_cmsWriteAlignment(io)) return FALSE; + } + + + return TRUE; +} + + +static +cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number Precision, cmsStage* mpe) +{ + cmsUInt8Number gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension. + cmsUInt32Number i; + _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data; + + if (CLUT ->HasFloatValues) { + cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only"); + return FALSE; + } + + memset(gridPoints, 0, sizeof(gridPoints)); + for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++) + gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i]; + + if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + + // Precision can be 1 or 2 bytes + if (Precision == 1) { + + for (i=0; i < CLUT->nEntries; i++) { + + if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE; + } + } + else + if (Precision == 2) { + + if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE; + } + else { + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); + return FALSE; + } + + if (!_cmsWriteAlignment(io)) return FALSE; + + return TRUE; +} + + + + +static +cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsPipeline* Lut = (cmsPipeline*) Ptr; + cmsUInt32Number inputChan, outputChan; + cmsStage *A = NULL, *B = NULL, *M = NULL; + cmsStage * Matrix = NULL; + cmsStage * CLUT = NULL; + cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; + cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; + + // Get the base for all offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (Lut ->Elements != NULL) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, + cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) { + + cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB"); + return FALSE; + } + + // Get input, output channels + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + + // Write channel count + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + + // Keep directory to be filled latter + DirectoryPos = io ->Tell(io); + + // Write the directory + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + + if (A != NULL) { + + offsetA = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; + } + + if (CLUT != NULL) { + offsetC = io ->Tell(io) - BaseOffset; + if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE; + + } + if (M != NULL) { + + offsetM = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; + } + + if (Matrix != NULL) { + offsetMat = io ->Tell(io) - BaseOffset; + if (!WriteMatrix(self, io, Matrix)) return FALSE; + } + + if (B != NULL) { + + offsetB = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; + } + + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) return FALSE; + + if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; + + if (!io ->Seek(io, CurrentPos)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// LutBToA type + +static +void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number inputChan; // Number of input channels + cmsUInt8Number outputChan; // Number of output channels + cmsUInt32Number BaseOffset; // Actual position in file + cmsUInt32Number offsetB; // Offset to first "B" curve + cmsUInt32Number offsetMat; // Offset to matrix + cmsUInt32Number offsetM; // Offset to first "M" curve + cmsUInt32Number offsetC; // Offset to CLUT + cmsUInt32Number offsetA; // Offset to first "A" curve + cmsPipeline* NewLUT = NULL; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; + if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; + + if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; + if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; + + // Padding + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); + if (NewLUT == NULL) return NULL; + + if (offsetB != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, inputChan))) + goto Error; + } + + if (offsetMat != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) + goto Error; + } + + if (offsetM != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, inputChan))) + goto Error; + } + + if (offsetC != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) + goto Error; + } + + if (offsetA!= 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, outputChan))) + goto Error; + } + + *nItems = 1; + return NewLUT; +Error: + cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +/* +B +B - Matrix - M +B - CLUT - A +B - Matrix - M - CLUT - A +*/ + +static +cmsBool Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsPipeline* Lut = (cmsPipeline*) Ptr; + cmsUInt32Number inputChan, outputChan; + cmsStage *A = NULL, *B = NULL, *M = NULL; + cmsStage *Matrix = NULL; + cmsStage *CLUT = NULL; + cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; + cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &B, &Matrix, &M)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &CLUT, &A)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &Matrix, &M, &CLUT, &A)) { + cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutBToA"); + return FALSE; + } + + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + + DirectoryPos = io ->Tell(io); + + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + + if (A != NULL) { + + offsetA = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; + } + + if (CLUT != NULL) { + offsetC = io ->Tell(io) - BaseOffset; + if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE; + + } + if (M != NULL) { + + offsetM = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; + } + + if (Matrix != NULL) { + offsetMat = io ->Tell(io) - BaseOffset; + if (!WriteMatrix(self, io, Matrix)) return FALSE; + } + + if (B != NULL) { + + offsetB = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; + } + + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) return FALSE; + + if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; + + if (!io ->Seek(io, CurrentPos)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + + +static +void* Type_LUTB2A_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUTB2A_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + + +// ******************************************************************************** +// Type cmsSigColorantTableType +// ******************************************************************************** +/* +The purpose of this tag is to identify the colorants used in the profile by a +unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous +value. The first colorant listed is the colorant of the first device channel of +a lut tag. The second colorant listed is the colorant of the second device channel +of a lut tag, and so on. +*/ + +static +void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number i, Count; + cmsNAMEDCOLORLIST* List; + char Name[34]; + cmsUInt16Number PCS[3]; + + + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + if (Count > cmsMAXCHANNELS) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many colorants '%d'", Count); + return NULL; + } + + List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", ""); + for (i=0; i < Count; i++) { + + if (io ->Read(io, Name, 32, 1) != 1) goto Error; + Name[32] = 0; + + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + + if (!cmsAppendNamedColor(List, Name, PCS, NULL)) goto Error; + + } + + *nItems = 1; + return List; + +Error: + *nItems = 0; + cmsFreeNamedColorList(List); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + + +// Saves a colorant table. It is using the named color structure for simplicity sake +static +cmsBool Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr; + cmsUInt32Number i, nColors; + + nColors = cmsNamedColorCount(NamedColorList); + + if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; + + for (i=0; i < nColors; i++) { + + char root[cmsMAX_PATH]; + cmsUInt16Number PCS[3]; + + memset(root, 0, sizeof(root)); + + if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0; + root[32] = 0; + + if (!io ->Write(io, 32, root)) return FALSE; + if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_ColorantTable_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr; + return (void*) cmsDupNamedColorList(nc); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigNamedColor2Type +// ******************************************************************************** +// +//The namedColor2Type is a count value and array of structures that provide color +//coordinates for 7-bit ASCII color names. For each named color, a PCS and optional +//device representation of the color are given. Both representations are 16-bit values. +//The device representation corresponds to the header’s “color space of data” field. +//This representation should be consistent with the “number of device components” +//field in the namedColor2Type. If this field is 0, device coordinates are not provided. +//The PCS representation corresponds to the header’s PCS field. The PCS representation +//is always provided. Color names are fixed-length, 32-byte fields including null +//termination. In order to maintain maximum portability, it is strongly recommended +//that special characters of the 7-bit ASCII set not be used. + +static +void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + + cmsUInt32Number vendorFlag; // Bottom 16 bits for ICC use + cmsUInt32Number count; // Count of named colors + cmsUInt32Number nDeviceCoords; // Num of device coordinates + char prefix[32]; // Prefix for each color name + char suffix[32]; // Suffix for each color name + cmsNAMEDCOLORLIST* v; + cmsUInt32Number i; + + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &vendorFlag)) return NULL; + if (!_cmsReadUInt32Number(io, &count)) return NULL; + if (!_cmsReadUInt32Number(io, &nDeviceCoords)) return NULL; + + if (io -> Read(io, prefix, 32, 1) != 1) return NULL; + if (io -> Read(io, suffix, 32, 1) != 1) return NULL; + + prefix[31] = suffix[31] = 0; + + v = cmsAllocNamedColorList(self ->ContextID, count, nDeviceCoords, prefix, suffix); + if (v == NULL) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many named colors '%d'", count); + return NULL; + } + + if (nDeviceCoords > cmsMAXCHANNELS) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords); + goto Error; + } + for (i=0; i < count; i++) { + + cmsUInt16Number PCS[3]; + cmsUInt16Number Colorant[cmsMAXCHANNELS]; + char Root[33]; + + memset(Colorant, 0, sizeof(Colorant)); + if (io -> Read(io, Root, 32, 1) != 1) goto Error; + Root[32] = 0; // To prevent exploits + + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error; + + if (!cmsAppendNamedColor(v, Root, PCS, Colorant)) goto Error; + } + + *nItems = 1; + return (void*) v ; + +Error: + cmsFreeNamedColorList(v); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// Saves a named color list into a named color profile +static +cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr; + char prefix[33]; // Prefix for each color name + char suffix[33]; // Suffix for each color name + cmsUInt32Number i, nColors; + + nColors = cmsNamedColorCount(NamedColorList); + + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; + if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE; + + strncpy(prefix, (const char*) NamedColorList->Prefix, 32); + strncpy(suffix, (const char*) NamedColorList->Suffix, 32); + + suffix[32] = prefix[32] = 0; + + if (!io ->Write(io, 32, prefix)) return FALSE; + if (!io ->Write(io, 32, suffix)) return FALSE; + + for (i=0; i < nColors; i++) { + + cmsUInt16Number PCS[3]; + cmsUInt16Number Colorant[cmsMAXCHANNELS]; + char Root[cmsMAX_PATH]; + + if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0; + Root[32] = 0; + if (!io ->Write(io, 32 , Root)) return FALSE; + if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE; + if (!_cmsWriteUInt16Array(io, NamedColorList ->ColorantCount, Colorant)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_NamedColor_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr; + + return (void*) cmsDupNamedColorList(nc); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_NamedColor_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigProfileSequenceDescType +// ******************************************************************************** + +// This type is an array of structures, each of which contains information from the +// header fields and tags from the original profiles which were combined to create +// the final profile. The order of the structures is the order in which the profiles +// were combined and includes a structure for the final profile. This provides a +// description of the profile sequence from source to destination, +// typically used with the DeviceLink profile. + +static +cmsBool ReadEmbeddedText(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU** mlu, cmsUInt32Number SizeOfTag) +{ + cmsTagTypeSignature BaseType; + cmsUInt32Number nItems; + + BaseType = _cmsReadTypeBase(io); + + switch (BaseType) { + + case cmsSigTextType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*)Type_Text_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + case cmsSigTextDescriptionType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*) Type_Text_Description_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + /* + TBD: Size is needed for MLU, and we have no idea on which is the available size + */ + + case cmsSigMultiLocalizedUnicodeType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + default: return FALSE; + } +} + + +static +void *Type_ProfileSequenceDesc_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq; + cmsUInt32Number i, Count; + + *nItems = 0; + + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + + OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); + if (OutSeq == NULL) return NULL; + + OutSeq ->n = Count; + + // Get structures as well + + for (i=0; i < Count; i++) { + + cmsPSEQDESC* sec = &OutSeq -> seq[i]; + + if (!_cmsReadUInt32Number(io, &sec ->deviceMfg)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!_cmsReadUInt32Number(io, &sec ->deviceModel)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!_cmsReadUInt64Number(io, &sec ->attributes)) goto Error; + if (SizeOfTag < sizeof(cmsUInt64Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt64Number); + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number *)&sec ->technology)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!ReadEmbeddedText(self, io, &sec ->Manufacturer, SizeOfTag)) goto Error; + if (!ReadEmbeddedText(self, io, &sec ->Model, SizeOfTag)) goto Error; + } + + *nItems = 1; + return OutSeq; + +Error: + cmsFreeProfileSequenceDescription(OutSeq); + return NULL; +} + + +// Aux--Embed a text description type. It can be of type text description or multilocalized unicode +// and it depends of the version number passed on cmsTagDescriptor structure instead of stack +static +cmsBool SaveDescription(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* Text) +{ + if (self ->ICCVersion < 0x4000000) { + + if (!_cmsWriteTypeBase(io, cmsSigTextDescriptionType)) return FALSE; + return Type_Text_Description_Write(self, io, Text, 1); + } + else { + if (!_cmsWriteTypeBase(io, cmsSigMultiLocalizedUnicodeType)) return FALSE; + return Type_MLU_Write(self, io, Text, 1); + } +} + + +static +cmsBool Type_ProfileSequenceDesc_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSEQ* Seq = (cmsSEQ*) Ptr; + cmsUInt32Number i; + + if (!_cmsWriteUInt32Number(io, Seq->n)) return FALSE; + + for (i=0; i < Seq ->n; i++) { + + cmsPSEQDESC* sec = &Seq -> seq[i]; + + if (!_cmsWriteUInt32Number(io, sec ->deviceMfg)) return FALSE; + if (!_cmsWriteUInt32Number(io, sec ->deviceModel)) return FALSE; + if (!_cmsWriteUInt64Number(io, &sec ->attributes)) return FALSE; + if (!_cmsWriteUInt32Number(io, sec ->technology)) return FALSE; + + if (!SaveDescription(self, io, sec ->Manufacturer)) return FALSE; + if (!SaveDescription(self, io, sec ->Model)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_ProfileSequenceDesc_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ProfileSequenceDesc_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigProfileSequenceIdType +// ******************************************************************************** +/* +In certain workflows using ICC Device Link Profiles, it is necessary to identify the +original profiles that were combined to create the Device Link Profile. +This type is an array of structures, each of which contains information for +identification of a profile used in a sequence +*/ + + +static +cmsBool ReadSeqID(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq = (cmsSEQ*) Cargo; + cmsPSEQDESC* seq = &OutSeq ->seq[n]; + + if (io -> Read(io, seq ->ProfileID.ID8, 16, 1) != 1) return FALSE; + if (!ReadEmbeddedText(self, io, &seq ->Description, SizeOfTag)) return FALSE; + + return TRUE; +} + + + +static +void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq; + cmsUInt32Number Count; + cmsUInt32Number BaseOffset; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Get table count + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Allocate an empty structure + OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); + if (OutSeq == NULL) return NULL; + + + // Read the position table + if (!ReadPositionTable(self, io, Count, BaseOffset, OutSeq, ReadSeqID)) { + + cmsFreeProfileSequenceDescription(OutSeq); + return NULL; + } + + // Success + *nItems = 1; + return OutSeq; + +} + + +static +cmsBool WriteSeqID(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsSEQ* Seq = (cmsSEQ*) Cargo; + + if (!io ->Write(io, 16, Seq ->seq[n].ProfileID.ID8)) return FALSE; + + // Store here the MLU + if (!SaveDescription(self, io, Seq ->seq[n].Description)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_ProfileSequenceId_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSEQ* Seq = (cmsSEQ*) Ptr; + cmsUInt32Number BaseOffset; + + // Keep the base offset + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // This is the table count + if (!_cmsWriteUInt32Number(io, Seq ->n)) return FALSE; + + // This is the position table and content + if (!WritePositionTable(self, io, 0, Seq ->n, BaseOffset, Seq, WriteSeqID)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_ProfileSequenceId_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ProfileSequenceId_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigUcrBgType +// ******************************************************************************** +/* +This type contains curves representing the under color removal and black +generation and a text string which is a general description of the method used +for the ucr/bg. +*/ + +static +void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); + cmsUInt32Number CountUcr, CountBg; + char* ASCIIString; + + *nItems = 0; + if (n == NULL) return NULL; + + // First curve is Under color removal + if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL); + if (n ->Ucr == NULL) return NULL; + + if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= CountUcr * sizeof(cmsUInt16Number); + + // Second curve is Black generation + if (!_cmsReadUInt32Number(io, &CountBg)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL); + if (n ->Bg == NULL) return NULL; + if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL; + if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL; + SizeOfTag -= CountBg * sizeof(cmsUInt16Number); + if (SizeOfTag == UINT_MAX) return NULL; + + // Now comes the text. The length is specified by the tag size + n ->Desc = cmsMLUalloc(self ->ContextID, 1); + if (n ->Desc == NULL) return NULL; + + ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); + if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL; + ASCIIString[SizeOfTag] = 0; + cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString); + _cmsFree(self ->ContextID, ASCIIString); + + *nItems = 1; + return (void*) n; +} + +static +cmsBool Type_UcrBg_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUcrBg* Value = (cmsUcrBg*) Ptr; + cmsUInt32Number TextSize; + char* Text; + + // First curve is Under color removal + if (!_cmsWriteUInt32Number(io, Value ->Ucr ->nEntries)) return FALSE; + if (!_cmsWriteUInt16Array(io, Value ->Ucr ->nEntries, Value ->Ucr ->Table16)) return FALSE; + + // Then black generation + if (!_cmsWriteUInt32Number(io, Value ->Bg ->nEntries)) return FALSE; + if (!_cmsWriteUInt16Array(io, Value ->Bg ->nEntries, Value ->Bg ->Table16)) return FALSE; + + // Now comes the text. The length is specified by the tag size + TextSize = cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, NULL, 0); + Text = (char*) _cmsMalloc(self ->ContextID, TextSize); + if (cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, Text, TextSize) != TextSize) return FALSE; + + if (!io ->Write(io, TextSize, Text)) return FALSE; + _cmsFree(self ->ContextID, Text); + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_UcrBg_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsUcrBg* Src = (cmsUcrBg*) Ptr; + cmsUcrBg* NewUcrBg = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); + + if (NewUcrBg == NULL) return NULL; + + NewUcrBg ->Bg = cmsDupToneCurve(Src ->Bg); + NewUcrBg ->Ucr = cmsDupToneCurve(Src ->Ucr); + NewUcrBg ->Desc = cmsMLUdup(Src ->Desc); + + return (void*) NewUcrBg; + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_UcrBg_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsUcrBg* Src = (cmsUcrBg*) Ptr; + + if (Src ->Ucr) cmsFreeToneCurve(Src ->Ucr); + if (Src ->Bg) cmsFreeToneCurve(Src ->Bg); + if (Src ->Desc) cmsMLUfree(Src ->Desc); + + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigCrdInfoType +// ******************************************************************************** + +/* +This type contains the PostScript product name to which this profile corresponds +and the names of the companion CRDs. Recall that a single profile can generate +multiple CRDs. It is implemented as a MLU being the language code "PS" and then +country varies for each element: + + nm: PostScript product name + #0: Rendering intent 0 CRD name + #1: Rendering intent 1 CRD name + #2: Rendering intent 2 CRD name + #3: Rendering intent 3 CRD name +*/ + + + +// Auxiliary, read an string specified as count + string +static +cmsBool ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section) +{ + cmsUInt32Number Count; + char* Text; + + if (*SizeOfTag < sizeof(cmsUInt32Number)) return FALSE; + + if (!_cmsReadUInt32Number(io, &Count)) return FALSE; + + if (Count > UINT_MAX - sizeof(cmsUInt32Number)) return FALSE; + if (*SizeOfTag < Count + sizeof(cmsUInt32Number)) return FALSE; + + Text = (char*) _cmsMalloc(self ->ContextID, Count+1); + if (Text == NULL) return FALSE; + + if (io ->Read(io, Text, sizeof(cmsUInt8Number), Count) != Count) { + _cmsFree(self ->ContextID, Text); + return FALSE; + } + + Text[Count] = 0; + + cmsMLUsetASCII(mlu, "PS", Section, Text); + _cmsFree(self ->ContextID, Text); + + *SizeOfTag -= (Count + sizeof(cmsUInt32Number)); + return TRUE; +} + +static +cmsBool WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section) +{ + cmsUInt32Number TextSize; + char* Text; + + TextSize = cmsMLUgetASCII(mlu, "PS", Section, NULL, 0); + Text = (char*) _cmsMalloc(self ->ContextID, TextSize); + + if (!_cmsWriteUInt32Number(io, TextSize)) return FALSE; + + if (cmsMLUgetASCII(mlu, "PS", Section, Text, TextSize) == 0) return FALSE; + + if (!io ->Write(io, TextSize, Text)) return FALSE; + _cmsFree(self ->ContextID, Text); + + return TRUE; +} + +static +void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5); + + *nItems = 0; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error; + + *nItems = 1; + return (void*) mlu; + +Error: + cmsMLUfree(mlu); + return NULL; + +} + +static +cmsBool Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + + cmsMLU* mlu = (cmsMLU*) Ptr; + + if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error; + + return TRUE; + +Error: + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_CrdInfo_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_CrdInfo_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsMLUfree((cmsMLU*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// ******************************************************************************** +// Type cmsSigScreeningType +// ******************************************************************************** +// +//The screeningType describes various screening parameters including screen +//frequency, screening angle, and spot shape. + +static +void *Type_Screening_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsScreening* sc = NULL; + cmsUInt32Number i; + + sc = (cmsScreening*) _cmsMallocZero(self ->ContextID, sizeof(cmsScreening)); + if (sc == NULL) return NULL; + + *nItems = 0; + + if (!_cmsReadUInt32Number(io, &sc ->Flag)) goto Error; + if (!_cmsReadUInt32Number(io, &sc ->nChannels)) goto Error; + + if (sc ->nChannels > cmsMAXCHANNELS - 1) + sc ->nChannels = cmsMAXCHANNELS - 1; + + for (i=0; i < sc ->nChannels; i++) { + + if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].Frequency)) goto Error; + if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].ScreenAngle)) goto Error; + if (!_cmsReadUInt32Number(io, &sc ->Channels[i].SpotShape)) goto Error; + } + + + *nItems = 1; + + return (void*) sc; + +Error: + if (sc != NULL) + _cmsFree(self ->ContextID, sc); + + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Screening_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsScreening* sc = (cmsScreening* ) Ptr; + cmsUInt32Number i; + + if (!_cmsWriteUInt32Number(io, sc ->Flag)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->nChannels)) return FALSE; + + for (i=0; i < sc ->nChannels; i++) { + + if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].Frequency)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].ScreenAngle)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->Channels[i].SpotShape)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Screening_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigViewingConditionsType +// ******************************************************************************** +// +//This type represents a set of viewing condition parameters including: +//CIE ’absolute’ illuminant white point tristimulus values and CIE ’absolute’ +//surround tristimulus values. + +static +void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCViewingConditions* vc = NULL; + + vc = (cmsICCViewingConditions*) _cmsMallocZero(self ->ContextID, sizeof(cmsICCViewingConditions)); + if (vc == NULL) return NULL; + + *nItems = 0; + + if (!_cmsReadXYZNumber(io, &vc ->IlluminantXYZ)) goto Error; + if (!_cmsReadXYZNumber(io, &vc ->SurroundXYZ)) goto Error; + if (!_cmsReadUInt32Number(io, &vc ->IlluminantType)) goto Error; + + *nItems = 1; + + return (void*) vc; + +Error: + if (vc != NULL) + _cmsFree(self ->ContextID, vc); + + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_ViewingConditions_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCViewingConditions* sc = (cmsICCViewingConditions* ) Ptr; + + if (!_cmsWriteXYZNumber(io, &sc ->IlluminantXYZ)) return FALSE; + if (!_cmsWriteXYZNumber(io, &sc ->SurroundXYZ)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->IlluminantType)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_ViewingConditions_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self->ContextID, Ptr, sizeof(cmsICCViewingConditions)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_ViewingConditions_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigMultiProcessElementType +// ******************************************************************************** + + +static +void* GenericMPEdup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsStageDup((cmsStage*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsStageFree((cmsStage*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// Each curve is stored in one or more curve segments, with break-points specified between curve segments. +// The first curve segment always starts at –Infinity, and the last curve segment always ends at +Infinity. The +// first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be +// specified either in terms of a formula, or by a sampled curve. + + +// Read an embedded segmented curve +static +cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) +{ + cmsCurveSegSignature ElementSig; + cmsUInt32Number i, j; + cmsUInt16Number nSegments; + cmsCurveSegment* Segments; + cmsToneCurve* Curve; + cmsFloat32Number PrevBreak = MINUS_INF; // - infinite + + // Take signature and channels for each element. + if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL; + + // That should be a segmented curve + if (ElementSig != cmsSigSegmentedCurve) return NULL; + + if (!_cmsReadUInt32Number(io, NULL)) return NULL; + if (!_cmsReadUInt16Number(io, &nSegments)) return NULL; + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (nSegments < 1) return NULL; + Segments = (cmsCurveSegment*) _cmsCalloc(self ->ContextID, nSegments, sizeof(cmsCurveSegment)); + if (Segments == NULL) return NULL; + + // Read breakpoints + for (i=0; i < (cmsUInt32Number) nSegments - 1; i++) { + + Segments[i].x0 = PrevBreak; + if (!_cmsReadFloat32Number(io, &Segments[i].x1)) goto Error; + PrevBreak = Segments[i].x1; + } + + Segments[nSegments-1].x0 = PrevBreak; + Segments[nSegments-1].x1 = PLUS_INF; // A big cmsFloat32Number number + + // Read segments + for (i=0; i < nSegments; i++) { + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) goto Error; + if (!_cmsReadUInt32Number(io, NULL)) goto Error; + + switch (ElementSig) { + + case cmsSigFormulaCurveSeg: { + + cmsUInt16Number Type; + cmsUInt32Number ParamsByType[] = {4, 5, 5 }; + + if (!_cmsReadUInt16Number(io, &Type)) goto Error; + if (!_cmsReadUInt16Number(io, NULL)) goto Error; + + Segments[i].Type = Type + 6; + if (Type > 2) goto Error; + + for (j=0; j < ParamsByType[Type]; j++) { + + cmsFloat32Number f; + if (!_cmsReadFloat32Number(io, &f)) goto Error; + Segments[i].Params[j] = f; + } + } + break; + + + case cmsSigSampledCurveSeg: { + cmsUInt32Number Count; + + if (!_cmsReadUInt32Number(io, &Count)) goto Error; + + Segments[i].nGridPoints = Count; + Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number)); + if (Segments[i].SampledPoints == NULL) goto Error; + + for (j=0; j < Count; j++) { + if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error; + } + } + break; + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String); + } + goto Error; + + } + } + + Curve = cmsBuildSegmentedToneCurve(self ->ContextID, nSegments, Segments); + + for (i=0; i < nSegments; i++) { + if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); + } + _cmsFree(self ->ContextID, Segments); + return Curve; + +Error: + if (Segments) { + for (i=0; i < nSegments; i++) { + if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); + } + _cmsFree(self ->ContextID, Segments); + } + return NULL; +} + + +static +cmsBool ReadMPECurve(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsToneCurve** GammaTables = ( cmsToneCurve**) Cargo; + + GammaTables[n] = ReadSegmentedCurve(self, io); + return (GammaTables[n] != NULL); + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +void *Type_MPEcurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe = NULL; + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number i, BaseOffset; + cmsToneCurve** GammaTables; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans != OutputChans) return NULL; + + GammaTables = (cmsToneCurve**) _cmsCalloc(self ->ContextID, InputChans, sizeof(cmsToneCurve*)); + if (GammaTables == NULL) return NULL; + + if (ReadPositionTable(self, io, InputChans, BaseOffset, GammaTables, ReadMPECurve)) { + + mpe = cmsStageAllocToneCurves(self ->ContextID, InputChans, GammaTables); + } + else { + mpe = NULL; + } + + for (i=0; i < InputChans; i++) { + if (GammaTables[i]) cmsFreeToneCurve(GammaTables[i]); + } + + _cmsFree(self ->ContextID, GammaTables); + *nItems = (mpe != NULL) ? 1U : 0; + return mpe; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// Write a single segmented curve. NO CHECK IS PERFORMED ON VALIDITY +static +cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g) +{ + cmsUInt32Number i, j; + cmsCurveSegment* Segments = g ->Segments; + cmsUInt32Number nSegments = g ->nSegments; + + if (!_cmsWriteUInt32Number(io, cmsSigSegmentedCurve)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) nSegments)) goto Error; + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + + // Write the break-points + for (i=0; i < nSegments - 1; i++) { + if (!_cmsWriteFloat32Number(io, Segments[i].x1)) goto Error; + } + + // Write the segments + for (i=0; i < g ->nSegments; i++) { + + cmsCurveSegment* ActualSeg = Segments + i; + + if (ActualSeg -> Type == 0) { + + // This is a sampled curve + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error; + + for (j=0; j < g ->Segments[i].nGridPoints; j++) { + if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error; + } + + } + else { + int Type; + cmsUInt32Number ParamsByType[] = { 4, 5, 5 }; + + // This is a formula-based + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigFormulaCurveSeg)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + + // We only allow 1, 2 and 3 as types + Type = ActualSeg ->Type - 6; + if (Type > 2 || Type < 0) goto Error; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Type)) goto Error; + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + + for (j=0; j < ParamsByType[Type]; j++) { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) ActualSeg ->Params[j])) goto Error; + } + } + + // It seems there is no need to align. Code is here, and for safety commented out + // if (!_cmsWriteAlignment(io)) goto Error; + } + + return TRUE; + +Error: + return FALSE; +} + + +static +cmsBool WriteMPECurve(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) Cargo; + + return WriteSegmentedCurve(io, Curves ->TheCurves[n]); + + cmsUNUSED_PARAMETER(SizeOfTag); + cmsUNUSED_PARAMETER(self); +} + +// Write a curve, checking first for validity +static +cmsBool Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number BaseOffset; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) mpe ->Data; + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Write the header. Since those are curves, input and output channels are same + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + + if (!WritePositionTable(self, io, 0, + mpe ->InputChannels, BaseOffset, Curves, WriteMPECurve)) return FALSE; + + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + + +// The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the +// matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array +// is organized as follows: +// array = [e11, e12, …, e1P, e21, e22, …, e2P, …, eQ1, eQ2, …, eQP, e1, e2, …, eQ] + +static +void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe; + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number nElems, i; + cmsFloat64Number* Matrix; + cmsFloat64Number* Offsets; + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + + // Input and output chans may be ANY (up to 0xffff), + // but we choose to limit to 16 channels for now + if (InputChans >= cmsMAXCHANNELS) return NULL; + if (OutputChans >= cmsMAXCHANNELS) return NULL; + + nElems = (cmsUInt32Number) InputChans * OutputChans; + + Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number)); + if (Matrix == NULL) return NULL; + + Offsets = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, OutputChans, sizeof(cmsFloat64Number)); + if (Offsets == NULL) { + + _cmsFree(self ->ContextID, Matrix); + return NULL; + } + + for (i=0; i < nElems; i++) { + + cmsFloat32Number v; + + if (!_cmsReadFloat32Number(io, &v)) { + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + return NULL; + } + Matrix[i] = v; + } + + + for (i=0; i < OutputChans; i++) { + + cmsFloat32Number v; + + if (!_cmsReadFloat32Number(io, &v)) { + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + return NULL; + } + Offsets[i] = v; + } + + + mpe = cmsStageAllocMatrix(self ->ContextID, OutputChans, InputChans, Matrix, Offsets); + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + + *nItems = 1; + + return mpe; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_MPEmatrix_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number i, nElems; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageMatrixData* Matrix = (_cmsStageMatrixData*) mpe ->Data; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE; + + nElems = mpe ->InputChannels * mpe ->OutputChannels; + + for (i=0; i < nElems; i++) { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Double[i])) return FALSE; + } + + + for (i=0; i < mpe ->OutputChannels; i++) { + + if (Matrix ->Offset == NULL) { + + if (!_cmsWriteFloat32Number(io, 0)) return FALSE; + } + else { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Offset[i])) return FALSE; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + + +static +void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe = NULL; + cmsUInt16Number InputChans, OutputChans; + cmsUInt8Number Dimensions8[16]; + cmsUInt32Number i, nMaxGrids, GridPoints[MAX_INPUT_DIMENSIONS]; + _cmsStageCLutData* clut; + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans == 0) goto Error; + if (OutputChans == 0) goto Error; + + if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16) + goto Error; + + // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number + nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? (cmsUInt32Number) MAX_INPUT_DIMENSIONS : InputChans; + + for (i = 0; i < nMaxGrids; i++) { + if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + GridPoints[i] = (cmsUInt32Number)Dimensions8[i]; + } + + // Allocate the true CLUT + mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL); + if (mpe == NULL) goto Error; + + // Read and sanitize the data + clut = (_cmsStageCLutData*) mpe ->Data; + for (i=0; i < clut ->nEntries; i++) { + + if (!_cmsReadFloat32Number(io, &clut->Tab.TFloat[i])) goto Error; + } + + *nItems = 1; + return mpe; + +Error: + *nItems = 0; + if (mpe != NULL) cmsStageFree(mpe); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// Write a CLUT in floating point +static +cmsBool Type_MPEclut_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt8Number Dimensions8[16]; // 16 because the spec says 16 and not max number of channels + cmsUInt32Number i; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe ->Data; + + // Check for maximum number of channels supported by lcms + if (mpe -> InputChannels > MAX_INPUT_DIMENSIONS) return FALSE; + + // Only floats are supported in MPE + if (clut ->HasFloatValues == FALSE) return FALSE; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE; + + memset(Dimensions8, 0, sizeof(Dimensions8)); + + for (i=0; i < mpe ->InputChannels; i++) + Dimensions8[i] = (cmsUInt8Number) clut ->Params ->nSamples[i]; + + if (!io ->Write(io, 16, Dimensions8)) return FALSE; + + for (i=0; i < clut ->nEntries; i++) { + + if (!_cmsWriteFloat32Number(io, clut ->Tab.TFloat[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + + +// This is the list of built-in MPE types +static _cmsTagTypeLinkedList SupportedMPEtypes[] = { + +{{ (cmsTagTypeSignature) cmsSigBAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[1] }, // Ignore those elements for now +{{ (cmsTagTypeSignature) cmsSigEAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[2] }, // (That's what the spec says) + +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCurveSetElemType, MPEcurve), &SupportedMPEtypes[3] }, +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigMatrixElemType, MPEmatrix), &SupportedMPEtypes[4] }, +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType, MPEclut), NULL }, +}; + +_cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL }; + +static +cmsBool ReadMPEElem(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsStageSignature ElementSig; + cmsTagTypeHandler* TypeHandler; + cmsUInt32Number nItems; + cmsPipeline *NewLUT = (cmsPipeline *) Cargo; + _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin); + + + // Take signature and channels for each element. + if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return FALSE; + + // The reserved placeholder + if (!_cmsReadUInt32Number(io, NULL)) return FALSE; + + // Read diverse MPE types + TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes); + if (TypeHandler == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + + // An unknown element was found. + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown MPE type '%s' found.", String); + return FALSE; + } + + // If no read method, just ignore the element (valid for cmsSigBAcsElemType and cmsSigEAcsElemType) + // Read the MPE. No size is given + if (TypeHandler ->ReadPtr != NULL) { + + // This is a real element which should be read and processed + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, (cmsStage*) TypeHandler ->ReadPtr(self, io, &nItems, SizeOfTag))) + return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(SizeOfTag); + cmsUNUSED_PARAMETER(n); +} + + +// This is the main dispatcher for MPE +static +void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number ElementCount; + cmsPipeline *NewLUT = NULL; + cmsUInt32Number BaseOffset; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Read channels and element count + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL; + if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans); + if (NewLUT == NULL) return NULL; + + if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error; + if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error; + + // Check channel count + if (InputChans != NewLUT->InputChannels || + OutputChans != NewLUT->OutputChannels) goto Error; + + // Success + *nItems = 1; + return NewLUT; + + // Error +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + *nItems = 0; + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + + +// This one is a liitle bit more complex, so we don't use position tables this time. +static +cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos; + cmsUInt32Number inputChan, outputChan; + cmsUInt32Number ElemCount; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before; + cmsStageSignature ElementSig; + cmsPipeline* Lut = (cmsPipeline*) Ptr; + cmsStage* Elem = Lut ->Elements; + cmsTagTypeHandler* TypeHandler; + _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin); + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + ElemCount = cmsPipelineStageCount(Lut); + + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + // Write the head + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) inputChan)) goto Error; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) outputChan)) goto Error; + if (!_cmsWriteUInt32Number(io, (cmsUInt16Number) ElemCount)) goto Error; + + DirectoryPos = io ->Tell(io); + + // Write a fake directory to be filled latter on + for (i=0; i < ElemCount; i++) { + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size + } + + // Write each single tag. Keep track of the size as well. + for (i=0; i < ElemCount; i++) { + + ElementOffsets[i] = io ->Tell(io) - BaseOffset; + + ElementSig = Elem ->Type; + + TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes); + if (TypeHandler == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + + // An unknown element was found. + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String); + goto Error; + } + + if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + Before = io ->Tell(io); + if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error; + if (!_cmsWriteAlignment(io)) goto Error; + + ElementSizes[i] = io ->Tell(io) - Before; + + Elem = Elem ->Next; + } + + // Write the directory + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) goto Error; + + for (i=0; i < ElemCount; i++) { + if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; + if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; + } + + if (!io ->Seek(io, CurrentPos)) goto Error; + + if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes); + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_MPE_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_MPE_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigVcgtType +// ******************************************************************************** + + +#define cmsVideoCardGammaTableType 0 +#define cmsVideoCardGammaFormulaType 1 + +// Used internally +typedef struct { + double Gamma; + double Min; + double Max; +} _cmsVCGTGAMMA; + + +static +void *Type_vcgt_Read(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number* nItems, + cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number TagType, n, i; + cmsToneCurve** Curves; + + *nItems = 0; + + // Read tag type + if (!_cmsReadUInt32Number(io, &TagType)) return NULL; + + // Allocate space for the array + Curves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*)); + if (Curves == NULL) return NULL; + + // There are two possible flavors + switch (TagType) { + + // Gamma is stored as a table + case cmsVideoCardGammaTableType: + { + cmsUInt16Number nChannels, nElems, nBytes; + + // Check channel count, which should be 3 (we don't support monochrome this time) + if (!_cmsReadUInt16Number(io, &nChannels)) goto Error; + + if (nChannels != 3) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported number of channels for VCGT '%d'", nChannels); + goto Error; + } + + // Get Table element count and bytes per element + if (!_cmsReadUInt16Number(io, &nElems)) goto Error; + if (!_cmsReadUInt16Number(io, &nBytes)) goto Error; + + // Adobe's quirk fixup. Fixing broken profiles... + if (nElems == 256 && nBytes == 1 && SizeOfTag == 1576) + nBytes = 2; + + + // Populate tone curves + for (n=0; n < 3; n++) { + + Curves[n] = cmsBuildTabulatedToneCurve16(self ->ContextID, nElems, NULL); + if (Curves[n] == NULL) goto Error; + + // On depending on byte depth + switch (nBytes) { + + // One byte, 0..255 + case 1: + for (i=0; i < nElems; i++) { + + cmsUInt8Number v; + + if (!_cmsReadUInt8Number(io, &v)) goto Error; + Curves[n] ->Table16[i] = FROM_8_TO_16(v); + } + break; + + // One word 0..65535 + case 2: + if (!_cmsReadUInt16Array(io, nElems, Curves[n]->Table16)) goto Error; + break; + + // Unsupported + default: + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported bit depth for VCGT '%d'", nBytes * 8); + goto Error; + } + } // For all 3 channels + } + break; + + // In this case, gamma is stored as a formula + case cmsVideoCardGammaFormulaType: + { + _cmsVCGTGAMMA Colorant[3]; + + // Populate tone curves + for (n=0; n < 3; n++) { + + double Params[10]; + + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Gamma)) goto Error; + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Min)) goto Error; + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Max)) goto Error; + + // Parametric curve type 5 is: + // Y = (aX + b)^Gamma + e | X >= d + // Y = cX + f | X < d + + // vcgt formula is: + // Y = (Max – Min) * (X ^ Gamma) + Min + + // So, the translation is + // a = (Max – Min) ^ ( 1 / Gamma) + // e = Min + // b=c=d=f=0 + + Params[0] = Colorant[n].Gamma; + Params[1] = pow((Colorant[n].Max - Colorant[n].Min), (1.0 / Colorant[n].Gamma)); + Params[2] = 0; + Params[3] = 0; + Params[4] = 0; + Params[5] = Colorant[n].Min; + Params[6] = 0; + + Curves[n] = cmsBuildParametricToneCurve(self ->ContextID, 5, Params); + if (Curves[n] == NULL) goto Error; + } + } + break; + + // Unsupported + default: + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag type for VCGT '%d'", TagType); + goto Error; + } + + *nItems = 1; + return (void*) Curves; + +// Regret, free all resources +Error: + + cmsFreeToneCurveTriple(Curves); + _cmsFree(self ->ContextID, Curves); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// We don't support all flavors, only 16bits tables and formula +static +cmsBool Type_vcgt_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve** Curves = (cmsToneCurve**) Ptr; + cmsUInt32Number i, j; + + if (cmsGetToneCurveParametricType(Curves[0]) == 5 && + cmsGetToneCurveParametricType(Curves[1]) == 5 && + cmsGetToneCurveParametricType(Curves[2]) == 5) { + + if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaFormulaType)) return FALSE; + + // Save parameters + for (i=0; i < 3; i++) { + + _cmsVCGTGAMMA v; + + v.Gamma = Curves[i] ->Segments[0].Params[0]; + v.Min = Curves[i] ->Segments[0].Params[5]; + v.Max = pow(Curves[i] ->Segments[0].Params[1], v.Gamma) + v.Min; + + if (!_cmsWrite15Fixed16Number(io, v.Gamma)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, v.Min)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, v.Max)) return FALSE; + } + } + + else { + + // Always store as a table of 256 words + if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaTableType)) return FALSE; + if (!_cmsWriteUInt16Number(io, 3)) return FALSE; + if (!_cmsWriteUInt16Number(io, 256)) return FALSE; + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + + for (i=0; i < 3; i++) { + for (j=0; j < 256; j++) { + + cmsFloat32Number v = cmsEvalToneCurveFloat(Curves[i], (cmsFloat32Number) (j / 255.0)); + cmsUInt16Number n = _cmsQuickSaturateWord(v * 65535.0); + + if (!_cmsWriteUInt16Number(io, n)) return FALSE; + } + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_vcgt_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsToneCurve** OldCurves = (cmsToneCurve**) Ptr; + cmsToneCurve** NewCurves; + + NewCurves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*)); + if (NewCurves == NULL) return NULL; + + NewCurves[0] = cmsDupToneCurve(OldCurves[0]); + NewCurves[1] = cmsDupToneCurve(OldCurves[1]); + NewCurves[2] = cmsDupToneCurve(OldCurves[2]); + + return (void*) NewCurves; + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_vcgt_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeToneCurveTriple((cmsToneCurve**) Ptr); + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigDictType +// ******************************************************************************** + +// Single column of the table can point to wchar or MLUC elements. Holds arrays of data +typedef struct { + cmsContext ContextID; + cmsUInt32Number *Offsets; + cmsUInt32Number *Sizes; +} _cmsDICelem; + +typedef struct { + _cmsDICelem Name, Value, DisplayName, DisplayValue; + +} _cmsDICarray; + +// Allocate an empty array element +static +cmsBool AllocElem(cmsContext ContextID, _cmsDICelem* e, cmsUInt32Number Count) +{ + e->Offsets = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number)); + if (e->Offsets == NULL) return FALSE; + + e->Sizes = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number)); + if (e->Sizes == NULL) { + + _cmsFree(ContextID, e -> Offsets); + return FALSE; + } + + e ->ContextID = ContextID; + return TRUE; +} + +// Free an array element +static +void FreeElem(_cmsDICelem* e) +{ + if (e ->Offsets != NULL) _cmsFree(e -> ContextID, e -> Offsets); + if (e ->Sizes != NULL) _cmsFree(e -> ContextID, e -> Sizes); + e->Offsets = e ->Sizes = NULL; +} + +// Get rid of whole array +static +void FreeArray( _cmsDICarray* a) +{ + if (a ->Name.Offsets != NULL) FreeElem(&a->Name); + if (a ->Value.Offsets != NULL) FreeElem(&a ->Value); + if (a ->DisplayName.Offsets != NULL) FreeElem(&a->DisplayName); + if (a ->DisplayValue.Offsets != NULL) FreeElem(&a ->DisplayValue); +} + + +// Allocate whole array +static +cmsBool AllocArray(cmsContext ContextID, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length) +{ + // Empty values + memset(a, 0, sizeof(_cmsDICarray)); + + // On depending on record size, create column arrays + if (!AllocElem(ContextID, &a ->Name, Count)) goto Error; + if (!AllocElem(ContextID, &a ->Value, Count)) goto Error; + + if (Length > 16) { + if (!AllocElem(ContextID, &a -> DisplayName, Count)) goto Error; + + } + if (Length > 24) { + if (!AllocElem(ContextID, &a ->DisplayValue, Count)) goto Error; + } + return TRUE; + +Error: + FreeArray(a); + return FALSE; +} + +// Read one element +static +cmsBool ReadOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsUInt32Number BaseOffset) +{ + if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE; + if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE; + + // An offset of zero has special meaning and shal be preserved + if (e ->Offsets[i] > 0) + e ->Offsets[i] += BaseOffset; + return TRUE; +} + + +static +cmsBool ReadOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number i; + + // Read column arrays + for (i=0; i < Count; i++) { + + if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE; + if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE; + + if (Length > 16) { + + if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE; + + } + + if (Length > 24) { + + if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE; + } + } + return TRUE; +} + + +// Write one element +static +cmsBool WriteOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i) +{ + if (!_cmsWriteUInt32Number(io, e->Offsets[i])) return FALSE; + if (!_cmsWriteUInt32Number(io, e ->Sizes[i])) return FALSE; + + return TRUE; +} + +static +cmsBool WriteOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length) +{ + cmsUInt32Number i; + + for (i=0; i < Count; i++) { + + if (!WriteOneElem(io, &a -> Name, i)) return FALSE; + if (!WriteOneElem(io, &a -> Value, i)) return FALSE; + + if (Length > 16) { + + if (!WriteOneElem(io, &a -> DisplayName, i)) return FALSE; + } + + if (Length > 24) { + + if (!WriteOneElem(io, &a -> DisplayValue, i)) return FALSE; + } + } + + return TRUE; +} + +static +cmsBool ReadOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, wchar_t ** wcstr) +{ + + cmsUInt32Number nChars; + + // Special case for undefined strings (see ICC Votable + // Proposal Submission, Dictionary Type and Metadata TAG Definition) + if (e -> Offsets[i] == 0) { + + *wcstr = NULL; + return TRUE; + } + + if (!io -> Seek(io, e -> Offsets[i])) return FALSE; + + nChars = e ->Sizes[i] / sizeof(cmsUInt16Number); + + + *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t)); + if (*wcstr == NULL) return FALSE; + + if (!_cmsReadWCharArray(io, nChars, *wcstr)) { + _cmsFree(e ->ContextID, *wcstr); + return FALSE; + } + + // End of string marker + (*wcstr)[nChars] = 0; + return TRUE; +} + +static +cmsUInt32Number mywcslen(const wchar_t *s) +{ + const wchar_t *p; + + p = s; + while (*p) + p++; + + return (cmsUInt32Number)(p - s); +} + +static +cmsBool WriteOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const wchar_t * wcstr, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number Before = io ->Tell(io); + cmsUInt32Number n; + + e ->Offsets[i] = Before - BaseOffset; + + if (wcstr == NULL) { + e ->Sizes[i] = 0; + e ->Offsets[i] = 0; + return TRUE; + } + + n = mywcslen(wcstr); + if (!_cmsWriteWCharArray(io, n, wcstr)) return FALSE; + + e ->Sizes[i] = io ->Tell(io) - Before; + return TRUE; +} + +static +cmsBool ReadOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsMLU** mlu) +{ + cmsUInt32Number nItems = 0; + + // A way to get null MLUCs + if (e -> Offsets[i] == 0 || e ->Sizes[i] == 0) { + + *mlu = NULL; + return TRUE; + } + + if (!io -> Seek(io, e -> Offsets[i])) return FALSE; + + *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, e ->Sizes[i]); + return *mlu != NULL; +} + +static +cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const cmsMLU* mlu, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number Before; + + // Special case for undefined strings (see ICC Votable + // Proposal Submission, Dictionary Type and Metadata TAG Definition) + if (mlu == NULL) { + e ->Sizes[i] = 0; + e ->Offsets[i] = 0; + return TRUE; + } + + Before = io ->Tell(io); + e ->Offsets[i] = Before - BaseOffset; + + if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE; + + e ->Sizes[i] = io ->Tell(io) - Before; + return TRUE; +} + + +static +void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsHANDLE hDict; + cmsUInt32Number i, Count, Length; + cmsUInt32Number BaseOffset; + _cmsDICarray a; + wchar_t *NameWCS = NULL, *ValueWCS = NULL; + cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL; + cmsBool rc; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Get name-value record count + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Get rec length + if (!_cmsReadUInt32Number(io, &Length)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Check for valid lengths + if (Length != 16 && Length != 24 && Length != 32) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown record length in dictionary '%d'", Length); + return NULL; + } + + // Creates an empty dictionary + hDict = cmsDictAlloc(self -> ContextID); + if (hDict == NULL) return NULL; + + // On depending on record size, create column arrays + if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error; + + // Read column arrays + if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error; + + // Seek to each element and read it + for (i=0; i < Count; i++) { + + if (!ReadOneWChar(io, &a.Name, i, &NameWCS)) goto Error; + if (!ReadOneWChar(io, &a.Value, i, &ValueWCS)) goto Error; + + if (Length > 16) { + if (!ReadOneMLUC(self, io, &a.DisplayName, i, &DisplayNameMLU)) goto Error; + } + + if (Length > 24) { + if (!ReadOneMLUC(self, io, &a.DisplayValue, i, &DisplayValueMLU)) goto Error; + } + + if (NameWCS == NULL || ValueWCS == NULL) { + + cmsSignalError(self->ContextID, cmsERROR_CORRUPTION_DETECTED, "Bad dictionary Name/Value"); + rc = FALSE; + } + else { + + rc = cmsDictAddEntry(hDict, NameWCS, ValueWCS, DisplayNameMLU, DisplayValueMLU); + } + + if (NameWCS != NULL) _cmsFree(self ->ContextID, NameWCS); + if (ValueWCS != NULL) _cmsFree(self ->ContextID, ValueWCS); + if (DisplayNameMLU != NULL) cmsMLUfree(DisplayNameMLU); + if (DisplayValueMLU != NULL) cmsMLUfree(DisplayValueMLU); + + if (!rc) goto Error; + } + + FreeArray(&a); + *nItems = 1; + return (void*) hDict; + +Error: + FreeArray(&a); + cmsDictFree(hDict); + return NULL; +} + + +static +cmsBool Type_Dictionary_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsHANDLE hDict = (cmsHANDLE) Ptr; + const cmsDICTentry* p; + cmsBool AnyName, AnyValue; + cmsUInt32Number i, Count, Length; + cmsUInt32Number DirectoryPos, CurrentPos, BaseOffset; + _cmsDICarray a; + + if (hDict == NULL) return FALSE; + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Let's inspect the dictionary + Count = 0; AnyName = FALSE; AnyValue = FALSE; + for (p = cmsDictGetEntryList(hDict); p != NULL; p = cmsDictNextEntry(p)) { + + if (p ->DisplayName != NULL) AnyName = TRUE; + if (p ->DisplayValue != NULL) AnyValue = TRUE; + Count++; + } + + Length = 16; + if (AnyName) Length += 8; + if (AnyValue) Length += 8; + + if (!_cmsWriteUInt32Number(io, Count)) return FALSE; + if (!_cmsWriteUInt32Number(io, Length)) return FALSE; + + // Keep starting position of offsets table + DirectoryPos = io ->Tell(io); + + // Allocate offsets array + if (!AllocArray(self ->ContextID, &a, Count, Length)) goto Error; + + // Write a fake directory to be filled latter on + if (!WriteOffsetArray(io, &a, Count, Length)) goto Error; + + // Write each element. Keep track of the size as well. + p = cmsDictGetEntryList(hDict); + for (i=0; i < Count; i++) { + + if (!WriteOneWChar(io, &a.Name, i, p ->Name, BaseOffset)) goto Error; + if (!WriteOneWChar(io, &a.Value, i, p ->Value, BaseOffset)) goto Error; + + if (p ->DisplayName != NULL) { + if (!WriteOneMLUC(self, io, &a.DisplayName, i, p ->DisplayName, BaseOffset)) goto Error; + } + + if (p ->DisplayValue != NULL) { + if (!WriteOneMLUC(self, io, &a.DisplayValue, i, p ->DisplayValue, BaseOffset)) goto Error; + } + + p = cmsDictNextEntry(p); + } + + // Write the directory + CurrentPos = io ->Tell(io); + if (!io ->Seek(io, DirectoryPos)) goto Error; + + if (!WriteOffsetArray(io, &a, Count, Length)) goto Error; + + if (!io ->Seek(io, CurrentPos)) goto Error; + + FreeArray(&a); + return TRUE; + +Error: + FreeArray(&a); + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_Dictionary_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDictDup((cmsHANDLE) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsDictFree((cmsHANDLE) Ptr); + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type support main routines +// ******************************************************************************** + + +// This is the list of built-in types +static const _cmsTagTypeLinkedList SupportedTagTypes[] = { + +{TYPE_HANDLER(cmsSigChromaticityType, Chromaticity), (_cmsTagTypeLinkedList*) &SupportedTagTypes[1] }, +{TYPE_HANDLER(cmsSigColorantOrderType, ColorantOrderType), (_cmsTagTypeLinkedList*) &SupportedTagTypes[2] }, +{TYPE_HANDLER(cmsSigS15Fixed16ArrayType, S15Fixed16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[3] }, +{TYPE_HANDLER(cmsSigU16Fixed16ArrayType, U16Fixed16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[4] }, +{TYPE_HANDLER(cmsSigTextType, Text), (_cmsTagTypeLinkedList*) &SupportedTagTypes[5] }, +{TYPE_HANDLER(cmsSigTextDescriptionType, Text_Description), (_cmsTagTypeLinkedList*) &SupportedTagTypes[6] }, +{TYPE_HANDLER(cmsSigCurveType, Curve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[7] }, +{TYPE_HANDLER(cmsSigParametricCurveType, ParametricCurve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[8] }, +{TYPE_HANDLER(cmsSigDateTimeType, DateTime), (_cmsTagTypeLinkedList*) &SupportedTagTypes[9] }, +{TYPE_HANDLER(cmsSigLut8Type, LUT8), (_cmsTagTypeLinkedList*) &SupportedTagTypes[10] }, +{TYPE_HANDLER(cmsSigLut16Type, LUT16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[11] }, +{TYPE_HANDLER(cmsSigColorantTableType, ColorantTable), (_cmsTagTypeLinkedList*) &SupportedTagTypes[12] }, +{TYPE_HANDLER(cmsSigNamedColor2Type, NamedColor), (_cmsTagTypeLinkedList*) &SupportedTagTypes[13] }, +{TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU), (_cmsTagTypeLinkedList*) &SupportedTagTypes[14] }, +{TYPE_HANDLER(cmsSigProfileSequenceDescType, ProfileSequenceDesc),(_cmsTagTypeLinkedList*) &SupportedTagTypes[15] }, +{TYPE_HANDLER(cmsSigSignatureType, Signature), (_cmsTagTypeLinkedList*) &SupportedTagTypes[16] }, +{TYPE_HANDLER(cmsSigMeasurementType, Measurement), (_cmsTagTypeLinkedList*) &SupportedTagTypes[17] }, +{TYPE_HANDLER(cmsSigDataType, Data), (_cmsTagTypeLinkedList*) &SupportedTagTypes[18] }, +{TYPE_HANDLER(cmsSigLutAtoBType, LUTA2B), (_cmsTagTypeLinkedList*) &SupportedTagTypes[19] }, +{TYPE_HANDLER(cmsSigLutBtoAType, LUTB2A), (_cmsTagTypeLinkedList*) &SupportedTagTypes[20] }, +{TYPE_HANDLER(cmsSigUcrBgType, UcrBg), (_cmsTagTypeLinkedList*) &SupportedTagTypes[21] }, +{TYPE_HANDLER(cmsSigCrdInfoType, CrdInfo), (_cmsTagTypeLinkedList*) &SupportedTagTypes[22] }, +{TYPE_HANDLER(cmsSigMultiProcessElementType, MPE), (_cmsTagTypeLinkedList*) &SupportedTagTypes[23] }, +{TYPE_HANDLER(cmsSigScreeningType, Screening), (_cmsTagTypeLinkedList*) &SupportedTagTypes[24] }, +{TYPE_HANDLER(cmsSigViewingConditionsType, ViewingConditions), (_cmsTagTypeLinkedList*) &SupportedTagTypes[25] }, +{TYPE_HANDLER(cmsSigXYZType, XYZ), (_cmsTagTypeLinkedList*) &SupportedTagTypes[26] }, +{TYPE_HANDLER(cmsCorbisBrokenXYZtype, XYZ), (_cmsTagTypeLinkedList*) &SupportedTagTypes[27] }, +{TYPE_HANDLER(cmsMonacoBrokenCurveType, Curve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[28] }, +{TYPE_HANDLER(cmsSigProfileSequenceIdType, ProfileSequenceId), (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] }, +{TYPE_HANDLER(cmsSigDictType, Dictionary), (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] }, +{TYPE_HANDLER(cmsSigVcgtType, vcgt), NULL } +}; + + +_cmsTagTypePluginChunkType _cmsTagTypePluginChunk = { NULL }; + + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupTagTypeList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src, + int loc) +{ + _cmsTagTypePluginChunkType newHead = { NULL }; + _cmsTagTypeLinkedList* entry; + _cmsTagTypeLinkedList* Anterior = NULL; + _cmsTagTypePluginChunkType* head = (_cmsTagTypePluginChunkType*) src->chunks[loc]; + + // Walk the list copying all nodes + for (entry = head->TagTypes; + entry != NULL; + entry = entry ->Next) { + + _cmsTagTypeLinkedList *newEntry = ( _cmsTagTypeLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagTypeLinkedList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.TagTypes == NULL) + newHead.TagTypes = newEntry; + } + + ctx ->chunks[loc] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagTypePluginChunkType)); +} + + +void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Duplicate the LIST + DupTagTypeList(ctx, src, TagTypePlugin); + } + else { + static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL }; + ctx ->chunks[TagTypePlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType)); + } +} + +void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Duplicate the LIST + DupTagTypeList(ctx, src, MPEPlugin); + } + else { + static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL }; + ctx ->chunks[MPEPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType)); + } + +} + + +// Both kind of plug-ins share same structure +cmsBool _cmsRegisterTagTypePlugin(cmsContext id, cmsPluginBase* Data) +{ + return RegisterTypesPlugin(id, Data, TagTypePlugin); +} + +cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data) +{ + return RegisterTypesPlugin(id, Data,MPEPlugin); +} + + +// Wrapper for tag types +cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig) +{ + _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin); + + return GetHandler(sig, ctx->TagTypes, (_cmsTagTypeLinkedList*) SupportedTagTypes); +} + +// ******************************************************************************** +// Tag support main routines +// ******************************************************************************** + +typedef struct _cmsTagLinkedList_st { + + cmsTagSignature Signature; + cmsTagDescriptor Descriptor; + struct _cmsTagLinkedList_st* Next; + +} _cmsTagLinkedList; + +// This is the list of built-in tags. The data of this list can be modified by plug-ins +static _cmsTagLinkedList SupportedTags[] = { + + { cmsSigAToB0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]}, + { cmsSigAToB1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[2]}, + { cmsSigAToB2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[3]}, + { cmsSigBToA0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[4]}, + { cmsSigBToA1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[5]}, + { cmsSigBToA2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[6]}, + + // Allow corbis and its broken XYZ type + { cmsSigRedColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[7]}, + { cmsSigGreenColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[8]}, + { cmsSigBlueColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[9]}, + + { cmsSigRedTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[10]}, + { cmsSigGreenTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[11]}, + { cmsSigBlueTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[12]}, + + { cmsSigCalibrationDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[13]}, + { cmsSigCharTargetTag, { 1, 1, { cmsSigTextType }, NULL}, &SupportedTags[14]}, + + { cmsSigChromaticAdaptationTag, { 9, 1, { cmsSigS15Fixed16ArrayType }, NULL}, &SupportedTags[15]}, + { cmsSigChromaticityTag, { 1, 1, { cmsSigChromaticityType }, NULL}, &SupportedTags[16]}, + { cmsSigColorantOrderTag, { 1, 1, { cmsSigColorantOrderType }, NULL}, &SupportedTags[17]}, + { cmsSigColorantTableTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[18]}, + { cmsSigColorantTableOutTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[19]}, + + { cmsSigCopyrightTag, { 1, 3, { cmsSigTextType, cmsSigMultiLocalizedUnicodeType, cmsSigTextDescriptionType}, DecideTextType}, &SupportedTags[20]}, + { cmsSigDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[21]}, + + { cmsSigDeviceMfgDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[22]}, + { cmsSigDeviceModelDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[23]}, + + { cmsSigGamutTag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[24]}, + + { cmsSigGrayTRCTag, { 1, 2, { cmsSigCurveType, cmsSigParametricCurveType }, DecideCurveType}, &SupportedTags[25]}, + { cmsSigLuminanceTag, { 1, 1, { cmsSigXYZType }, NULL}, &SupportedTags[26]}, + + { cmsSigMediaBlackPointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[27]}, + { cmsSigMediaWhitePointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[28]}, + + { cmsSigNamedColor2Tag, { 1, 1, { cmsSigNamedColor2Type }, NULL}, &SupportedTags[29]}, + + { cmsSigPreview0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[30]}, + { cmsSigPreview1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[31]}, + { cmsSigPreview2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]}, + + { cmsSigProfileDescriptionTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]}, + { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL}, &SupportedTags[34]}, + { cmsSigTechnologyTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[35]}, + + { cmsSigColorimetricIntentImageStateTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]}, + { cmsSigPerceptualRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[37]}, + { cmsSigSaturationRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[38]}, + + { cmsSigMeasurementTag, { 1, 1, { cmsSigMeasurementType }, NULL}, &SupportedTags[39]}, + + { cmsSigPs2CRD0Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[40]}, + { cmsSigPs2CRD1Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[41]}, + { cmsSigPs2CRD2Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[42]}, + { cmsSigPs2CRD3Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[43]}, + { cmsSigPs2CSATag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[44]}, + { cmsSigPs2RenderingIntentTag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[45]}, + + { cmsSigViewingCondDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[46]}, + + { cmsSigUcrBgTag, { 1, 1, { cmsSigUcrBgType}, NULL}, &SupportedTags[47]}, + { cmsSigCrdInfoTag, { 1, 1, { cmsSigCrdInfoType}, NULL}, &SupportedTags[48]}, + + { cmsSigDToB0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[49]}, + { cmsSigDToB1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[50]}, + { cmsSigDToB2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[51]}, + { cmsSigDToB3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[52]}, + { cmsSigBToD0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[53]}, + { cmsSigBToD1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[54]}, + { cmsSigBToD2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[55]}, + { cmsSigBToD3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[56]}, + + { cmsSigScreeningDescTag, { 1, 1, { cmsSigTextDescriptionType }, NULL}, &SupportedTags[57]}, + { cmsSigViewingConditionsTag, { 1, 1, { cmsSigViewingConditionsType }, NULL}, &SupportedTags[58]}, + + { cmsSigScreeningTag, { 1, 1, { cmsSigScreeningType}, NULL }, &SupportedTags[59]}, + { cmsSigVcgtTag, { 1, 1, { cmsSigVcgtType}, NULL }, &SupportedTags[60]}, + { cmsSigMetaTag, { 1, 1, { cmsSigDictType}, NULL }, &SupportedTags[61]}, + { cmsSigProfileSequenceIdTag, { 1, 1, { cmsSigProfileSequenceIdType}, NULL }, &SupportedTags[62]}, + + { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]}, + { cmsSigArgyllArtsTag, { 9, 1, { cmsSigS15Fixed16ArrayType}, NULL}, NULL} + +}; + +/* + Not supported Why + ======================= ========================================= + cmsSigOutputResponseTag ==> WARNING, POSSIBLE PATENT ON THIS SUBJECT! + cmsSigNamedColorTag ==> Deprecated + cmsSigDataTag ==> Ancient, unused + cmsSigDeviceSettingsTag ==> Deprecated, useless +*/ + + +_cmsTagPluginChunkType _cmsTagPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupTagList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsTagPluginChunkType newHead = { NULL }; + _cmsTagLinkedList* entry; + _cmsTagLinkedList* Anterior = NULL; + _cmsTagPluginChunkType* head = (_cmsTagPluginChunkType*) src->chunks[TagPlugin]; + + // Walk the list copying all nodes + for (entry = head->Tag; + entry != NULL; + entry = entry ->Next) { + + _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.Tag == NULL) + newHead.Tag = newEntry; + } + + ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagPluginChunkType)); +} + +void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + DupTagList(ctx, src); + } + else { + static _cmsTagPluginChunkType TagPluginChunk = { NULL }; + ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagPluginChunk, sizeof(_cmsTagPluginChunkType)); + } + +} + +cmsBool _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data) +{ + cmsPluginTag* Plugin = (cmsPluginTag*) Data; + _cmsTagLinkedList *pt; + _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin); + + if (Data == NULL) { + + TagPluginChunk->Tag = NULL; + return TRUE; + } + + pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList)); + if (pt == NULL) return FALSE; + + pt ->Signature = Plugin ->Signature; + pt ->Descriptor = Plugin ->Descriptor; + pt ->Next = TagPluginChunk ->Tag; + + TagPluginChunk ->Tag = pt; + + return TRUE; +} + +// Return a descriptor for a given tag or NULL +cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig) +{ + _cmsTagLinkedList* pt; + _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(ContextID, TagPlugin); + + for (pt = TagPluginChunk->Tag; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Signature) return &pt ->Descriptor; + } + + for (pt = SupportedTags; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Signature) return &pt ->Descriptor; + } + + return NULL; +} + diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsvirt.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsvirt.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsvirt.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,425 +49,328 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - +// +//--------------------------------------------------------------------------------- +// -#include "lcms.h" - +#include "lcms2_internal.h" // Virtual (built-in) profiles // ----------------------------------------------------------------------------------- +static +cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description) +{ + cmsMLU *DescriptionMLU, *CopyrightMLU; + cmsBool rc = FALSE; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + DescriptionMLU = cmsMLUalloc(ContextID, 1); + CopyrightMLU = cmsMLUalloc(ContextID, 1); + + if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error; + + if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error; + if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error; + + if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error; + if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error; + + rc = TRUE; + +Error: + + if (DescriptionMLU) + cmsMLUfree(DescriptionMLU); + if (CopyrightMLU) + cmsMLUfree(CopyrightMLU); + return rc; +} + + +static +cmsBool SetSeqDescTag(cmsHPROFILE hProfile, const char* Model) +{ + cmsBool rc = FALSE; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1); + + if (Seq == NULL) return FALSE; + + Seq->seq[0].deviceMfg = (cmsSignature) 0; + Seq->seq[0].deviceModel = (cmsSignature) 0; + +#ifdef CMS_DONT_USE_INT64 + Seq->seq[0].attributes[0] = 0; + Seq->seq[0].attributes[1] = 0; +#else + Seq->seq[0].attributes = 0; +#endif + + Seq->seq[0].technology = (cmsTechnologySignature) 0; + + cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS"); + cmsMLUsetASCII( Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model); + + if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error; + + rc = TRUE; + +Error: + if (Seq) + cmsFreeProfileSequenceDescription(Seq); + + return rc; +} + + // This function creates a profile based on White point, primaries and // transfer functions. - - -cmsHPROFILE LCMSEXPORT cmsCreateRGBProfile(LPcmsCIExyY WhitePoint, - LPcmsCIExyYTRIPLE Primaries, - LPGAMMATABLE TransferFunction[3]) +cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]) { - cmsHPROFILE hICC; - cmsCIEXYZ tmp; - MAT3 MColorants; - cmsCIEXYZTRIPLE Colorants; - cmsCIExyY MaxWhite; - + cmsHPROFILE hICC; + cmsMAT3 MColorants; + cmsCIEXYZTRIPLE Colorants; + cmsCIExyY MaxWhite; + cmsMAT3 CHAD; + cmsCIEXYZ WhitePointXYZ; - hICC = _cmsCreateProfilePlaceholder(); - if (!hICC) // can't allocate - return NULL; + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + cmsSetProfileVersion(hICC, 4.3); - cmsSetDeviceClass(hICC, icSigDisplayClass); - cmsSetColorSpace(hICC, icSigRgbData); - cmsSetPCS(hICC, icSigXYZData); - cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); + cmsSetDeviceClass(hICC, cmsSigDisplayClass); + cmsSetColorSpace(hICC, cmsSigRgbData); + cmsSetPCS(hICC, cmsSigXYZData); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - // Implement profile using following tags: - // - // 1 icSigProfileDescriptionTag - // 2 icSigMediaWhitePointTag - // 3 icSigRedColorantTag - // 4 icSigGreenColorantTag - // 5 icSigBlueColorantTag - // 6 icSigRedTRCTag - // 7 icSigGreenTRCTag - // 8 icSigBlueTRCTag - - // This conforms a standard RGB DisplayProfile as says ICC, and then I add - - // 9 icSigChromaticityTag - - // As addendum II + // Implement profile using following tags: + // + // 1 cmsSigProfileDescriptionTag + // 2 cmsSigMediaWhitePointTag + // 3 cmsSigRedColorantTag + // 4 cmsSigGreenColorantTag + // 5 cmsSigBlueColorantTag + // 6 cmsSigRedTRCTag + // 7 cmsSigGreenTRCTag + // 8 cmsSigBlueTRCTag + // 9 Chromatic adaptation Tag + // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II) + // 10 cmsSigChromaticityTag - // Fill-in the tags + if (!SetTextTags(hICC, L"RGB built-in")) goto Error; - cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms RGB virtual profile"); - cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "rgb built-in"); + if (WhitePoint) { + if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; - if (WhitePoint) { + cmsxyY2XYZ(&WhitePointXYZ, WhitePoint); + _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ()); - cmsxyY2XYZ(&tmp, WhitePoint); - cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp); - } + // This is a V4 tag, but many CMM does read and understand it no matter which version + if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error; + } - if (WhitePoint && Primaries) { + if (WhitePoint && Primaries) { MaxWhite.x = WhitePoint -> x; MaxWhite.y = WhitePoint -> y; MaxWhite.Y = 1.0; - if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) - { - cmsCloseProfile(hICC); - return NULL; - } + if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error; - cmsAdaptMatrixToD50(&MColorants, &MaxWhite); - - Colorants.Red.X = MColorants.v[0].n[0]; - Colorants.Red.Y = MColorants.v[1].n[0]; - Colorants.Red.Z = MColorants.v[2].n[0]; + Colorants.Red.X = MColorants.v[0].n[0]; + Colorants.Red.Y = MColorants.v[1].n[0]; + Colorants.Red.Z = MColorants.v[2].n[0]; - Colorants.Green.X = MColorants.v[0].n[1]; - Colorants.Green.Y = MColorants.v[1].n[1]; - Colorants.Green.Z = MColorants.v[2].n[1]; + Colorants.Green.X = MColorants.v[0].n[1]; + Colorants.Green.Y = MColorants.v[1].n[1]; + Colorants.Green.Z = MColorants.v[2].n[1]; - Colorants.Blue.X = MColorants.v[0].n[2]; - Colorants.Blue.Y = MColorants.v[1].n[2]; - Colorants.Blue.Z = MColorants.v[2].n[2]; + Colorants.Blue.X = MColorants.v[0].n[2]; + Colorants.Blue.Y = MColorants.v[1].n[2]; + Colorants.Blue.Z = MColorants.v[2].n[2]; - cmsAddTag(hICC, icSigRedColorantTag, (LPVOID) &Colorants.Red); - cmsAddTag(hICC, icSigBlueColorantTag, (LPVOID) &Colorants.Blue); - cmsAddTag(hICC, icSigGreenColorantTag, (LPVOID) &Colorants.Green); - } + if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error; + if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error; + if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error; + } - if (TransferFunction) { + if (TransferFunction) { + + // Tries to minimize space. Thanks to Richard Hughes for this nice idea + if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error; + + if (TransferFunction[1] == TransferFunction[0]) { + + if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error; - // In case of gamma, we must dup' the table pointer + } else { + + if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error; + } + + if (TransferFunction[2] == TransferFunction[0]) { + + if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error; + + } else { - cmsAddTag(hICC, icSigRedTRCTag, (LPVOID) TransferFunction[0]); - cmsAddTag(hICC, icSigGreenTRCTag, (LPVOID) TransferFunction[1]); - cmsAddTag(hICC, icSigBlueTRCTag, (LPVOID) TransferFunction[2]); - } + if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error; + } + } + + if (Primaries) { + if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error; + } + + + return hICC; - if (Primaries) { - cmsAddTag(hICC, icSigChromaticityTag, (LPVOID) Primaries); - } +Error: + if (hICC) + cmsCloseProfile(hICC); + return NULL; +} - return hICC; +cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]) +{ + return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction); } // This function creates a profile based on White point and transfer function. - -cmsHPROFILE LCMSEXPORT cmsCreateGrayProfile(LPcmsCIExyY WhitePoint, - LPGAMMATABLE TransferFunction) +cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction) { - cmsHPROFILE hICC; - cmsCIEXYZ tmp; - - - hICC = _cmsCreateProfilePlaceholder(); - if (!hICC) // can't allocate - return NULL; - - - cmsSetDeviceClass(hICC, icSigDisplayClass); - cmsSetColorSpace(hICC, icSigGrayData); - cmsSetPCS(hICC, icSigXYZData); - cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); - - - - // Implement profile using following tags: - // - // 1 icSigProfileDescriptionTag - // 2 icSigMediaWhitePointTag - // 6 icSigGrayTRCTag + cmsHPROFILE hICC; + cmsCIEXYZ tmp; - // This conforms a standard Gray DisplayProfile - - // Fill-in the tags - - - cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms gray virtual profile"); - cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "gray built-in"); - - - if (WhitePoint) { + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; - cmsxyY2XYZ(&tmp, WhitePoint); - cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp); - } - - - if (TransferFunction) { + cmsSetProfileVersion(hICC, 4.3); - // In case of gamma, we must dup' the table pointer - - cmsAddTag(hICC, icSigGrayTRCTag, (LPVOID) TransferFunction); - } - - return hICC; - -} + cmsSetDeviceClass(hICC, cmsSigDisplayClass); + cmsSetColorSpace(hICC, cmsSigGrayData); + cmsSetPCS(hICC, cmsSigXYZData); + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); -static -int IsPCS(icColorSpaceSignature ColorSpace) -{ - return (ColorSpace == icSigXYZData || - ColorSpace == icSigLabData); -} + // Implement profile using following tags: + // + // 1 cmsSigProfileDescriptionTag + // 2 cmsSigMediaWhitePointTag + // 3 cmsSigGrayTRCTag -static -void FixColorSpaces(cmsHPROFILE hProfile, - icColorSpaceSignature ColorSpace, - icColorSpaceSignature PCS, - DWORD dwFlags) -{ - - if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) { - - if (IsPCS(ColorSpace) && IsPCS(PCS)) { + // This conforms a standard Gray DisplayProfile - cmsSetDeviceClass(hProfile, icSigAbstractClass); - cmsSetColorSpace(hProfile, ColorSpace); - cmsSetPCS(hProfile, PCS); - return; - } + // Fill-in the tags - if (IsPCS(ColorSpace) && !IsPCS(PCS)) { + if (!SetTextTags(hICC, L"gray built-in")) goto Error; + - cmsSetDeviceClass(hProfile, icSigOutputClass); - cmsSetPCS(hProfile, ColorSpace); - cmsSetColorSpace(hProfile, PCS); - return; - } + if (WhitePoint) { - if (IsPCS(PCS) && !IsPCS(ColorSpace)) { - - cmsSetDeviceClass(hProfile, icSigInputClass); - cmsSetColorSpace(hProfile, ColorSpace); - cmsSetPCS(hProfile, PCS); - return; - } + cmsxyY2XYZ(&tmp, WhitePoint); + if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error; } - cmsSetDeviceClass(hProfile, icSigLinkClass); - cmsSetColorSpace(hProfile, ColorSpace); - cmsSetPCS(hProfile, PCS); - -} - - -static -cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) -{ - _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform; - cmsHPROFILE hICC; - cmsCIEXYZ WhitePoint; - int i, nColors; - size_t Size; - LPcmsNAMEDCOLORLIST nc2; - - - hICC = _cmsCreateProfilePlaceholder(); - if (hICC == NULL) return NULL; + if (TransferFunction) { - cmsSetRenderingIntent(hICC, v -> Intent); - cmsSetDeviceClass(hICC, icSigNamedColorClass); - cmsSetColorSpace(hICC, v ->ExitColorSpace); - cmsSetPCS(hICC, cmsGetPCS(v ->InputProfile)); - cmsTakeMediaWhitePoint(&WhitePoint, v ->InputProfile); - - cmsAddTag(hICC, icSigMediaWhitePointTag, &WhitePoint); - cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS"); - cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Named color Device link"); - cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Named color Device link"); - - - nColors = cmsNamedColorCount(xform); - nc2 = cmsAllocNamedColorList(nColors); - - Size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * (nColors-1)); - - CopyMemory(nc2, v->NamedColorList, Size); - nc2 ->ColorantCount = _cmsChannelsOf(v ->ExitColorSpace); - - for (i=0; i < nColors; i++) { - cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); + if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error; } - cmsAddTag(hICC, icSigNamedColor2Tag, (void*) nc2); - cmsFreeNamedColorList(nc2); + return hICC; - return hICC; +Error: + if (hICC) + cmsCloseProfile(hICC); + return NULL; } -// Does convert a transform into a device link profile -cmsHPROFILE LCMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, DWORD dwFlags) +cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction) +{ + return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction); +} + +// This is a devicelink operating in the target colorspace with as many transfer functions as components + +cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]) { cmsHPROFILE hICC; - _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) hTransform; - LPLUT Lut; - LCMSBOOL MustFreeLUT; - LPcmsNAMEDCOLORLIST InputColorant = NULL; - LPcmsNAMEDCOLORLIST OutputColorant = NULL; - - - // Check if is a named color transform - - if (cmsGetDeviceClass(v ->InputProfile) == icSigNamedColorClass) { - - return CreateNamedColorDevicelink(hTransform); - - } + cmsPipeline* Pipeline; + cmsUInt32Number nChannels; - if (v ->DeviceLink) { - - Lut = v -> DeviceLink; - MustFreeLUT = FALSE; - } - else { + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) + return NULL; - Lut = _cmsPrecalculateDeviceLink(hTransform, dwFlags); - if (!Lut) return NULL; - MustFreeLUT = TRUE; - } - - hICC = _cmsCreateProfilePlaceholder(); - if (!hICC) { // can't allocate + cmsSetProfileVersion(hICC, 4.3); - if (MustFreeLUT) cmsFreeLUT(Lut); - return NULL; - } - - - FixColorSpaces(hICC, v -> EntryColorSpace, v -> ExitColorSpace, dwFlags); + cmsSetDeviceClass(hICC, cmsSigLinkClass); + cmsSetColorSpace(hICC, ColorSpace); + cmsSetPCS(hICC, ColorSpace); - cmsSetRenderingIntent(hICC, v -> Intent); - - // Implement devicelink profile using following tags: - // - // 1 icSigProfileDescriptionTag - // 2 icSigMediaWhitePointTag - // 3 icSigAToB0Tag + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - - cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS"); - cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Device link"); - cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Device link"); - - - cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); + // Set up channels + nChannels = cmsChannelsOf(ColorSpace); - if (cmsGetDeviceClass(hICC) == icSigOutputClass) { - - cmsAddTag(hICC, icSigBToA0Tag, (LPVOID) Lut); - } - else - cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); - + // Creates a Pipeline with prelinearization step only + Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels); + if (Pipeline == NULL) goto Error; - // Try to read input and output colorant table - if (cmsIsTag(v ->InputProfile, icSigColorantTableTag)) { - - // Input table can only come in this way. - InputColorant = cmsReadColorantTable(v ->InputProfile, icSigColorantTableTag); - } - - // Output is a little bit more complex. - if (cmsGetDeviceClass(v ->OutputProfile) == icSigLinkClass) { - - // This tag may exist only on devicelink profiles. - if (cmsIsTag(v ->OutputProfile, icSigColorantTableOutTag)) { - - OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableOutTag); - } - - } else { + // Copy tables to Pipeline + if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions))) + goto Error; - if (cmsIsTag(v ->OutputProfile, icSigColorantTableTag)) { - - OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableTag); - } - } - - if (InputColorant) - cmsAddTag(hICC, icSigColorantTableTag, InputColorant); + // Create tags + if (!SetTextTags(hICC, L"Linearization built-in")) goto Error; + if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error; + if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error; - if (OutputColorant) - cmsAddTag(hICC, icSigColorantTableOutTag, OutputColorant); - - + // Pipeline is already on virtual profile + cmsPipelineFree(Pipeline); - if (MustFreeLUT) cmsFreeLUT(Lut); - if (InputColorant) cmsFreeNamedColorList(InputColorant); - if (OutputColorant) cmsFreeNamedColorList(OutputColorant); - + // Ok, done return hICC; -} +Error: + cmsPipelineFree(Pipeline); + if (hICC) + cmsCloseProfile(hICC); -// This is a devicelink operating in the target colorspace with as many transfer -// functions as components - -cmsHPROFILE LCMSEXPORT cmsCreateLinearizationDeviceLink(icColorSpaceSignature ColorSpace, - LPGAMMATABLE TransferFunctions[]) -{ - cmsHPROFILE hICC; - LPLUT Lut; - - - hICC = _cmsCreateProfilePlaceholder(); - if (!hICC) // can't allocate - return NULL; - - - cmsSetDeviceClass(hICC, icSigLinkClass); - cmsSetColorSpace(hICC, ColorSpace); - cmsSetPCS(hICC, ColorSpace); - cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); - - - // Creates a LUT with prelinearization step only - Lut = cmsAllocLUT(); - if (Lut == NULL) return NULL; - - // Set up channels - Lut ->InputChan = Lut ->OutputChan = _cmsChannelsOf(ColorSpace); - - // Copy tables to LUT - cmsAllocLinearTable(Lut, TransferFunctions, 1); - - // Create tags - cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms linearization device link"); - cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "linearization built-in"); - - cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); - cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); - - // LUT is already on virtual profile - cmsFreeLUT(Lut); - - // Ok, done - return hICC; + return NULL; } +cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]) +{ + return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions); +} // Ink-limiting algorithm // @@ -486,296 +390,332 @@ // K: Does not change static -int InkLimitingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +int InkLimitingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) { - double InkLimit = *(double *) Cargo; - double SumCMY, SumCMYK, Ratio; + cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo; + cmsFloat64Number SumCMY, SumCMYK, Ratio; - InkLimit = (InkLimit * 655.35); + InkLimit = (InkLimit * 655.35); - SumCMY = In[0] + In[1] + In[2]; - SumCMYK = SumCMY + In[3]; + SumCMY = In[0] + In[1] + In[2]; + SumCMYK = SumCMY + In[3]; - if (SumCMYK > InkLimit) { + if (SumCMYK > InkLimit) { - Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY); - if (Ratio < 0) - Ratio = 0; - } - else Ratio = 1; + Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY); + if (Ratio < 0) + Ratio = 0; + } + else Ratio = 1; - Out[0] = (WORD) floor(In[0] * Ratio + 0.5); // C - Out[1] = (WORD) floor(In[1] * Ratio + 0.5); // M - Out[2] = (WORD) floor(In[2] * Ratio + 0.5); // Y + Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C + Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M + Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y - Out[3] = In[3]; // K (untouched) + Out[3] = In[3]; // K (untouched) - return TRUE; + return TRUE; } // This is a devicelink operating in CMYK for ink-limiting -cmsHPROFILE LCMSEXPORT cmsCreateInkLimitingDeviceLink(icColorSpaceSignature ColorSpace, - double Limit) +cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsFloat64Number Limit) { - cmsHPROFILE hICC; - LPLUT Lut; + cmsHPROFILE hICC; + cmsPipeline* LUT; + cmsStage* CLUT; + cmsUInt32Number nChannels; - if (ColorSpace != icSigCmykData) { - cmsSignalError(LCMS_ERRC_ABORTED, "InkLimiting: Only CMYK currently supported"); - return NULL; - } + if (ColorSpace != cmsSigCmykData) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported"); + return NULL; + } - if (Limit < 0.0 || Limit > 400) { - - cmsSignalError(LCMS_ERRC_WARNING, "InkLimiting: Limit should be between 0..400"); - if (Limit < 0) Limit = 0; - if (Limit > 400) Limit = 400; + if (Limit < 0.0 || Limit > 400) { - } + cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400"); + if (Limit < 0) Limit = 0; + if (Limit > 400) Limit = 400; - hICC = _cmsCreateProfilePlaceholder(); - if (!hICC) // can't allocate - return NULL; - + } - cmsSetDeviceClass(hICC, icSigLinkClass); - cmsSetColorSpace(hICC, ColorSpace); - cmsSetPCS(hICC, ColorSpace); - cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + cmsSetProfileVersion(hICC, 4.3); - // Creates a LUT with 3D grid only - Lut = cmsAllocLUT(); - if (Lut == NULL) { - cmsCloseProfile(hICC); - return NULL; - } + cmsSetDeviceClass(hICC, cmsSigLinkClass); + cmsSetColorSpace(hICC, ColorSpace); + cmsSetPCS(hICC, ColorSpace); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - cmsAlloc3DGrid(Lut, 17, _cmsChannelsOf(ColorSpace), - _cmsChannelsOf(ColorSpace)); - - if (!cmsSample3DGrid(Lut, InkLimitingSampler, (LPVOID) &Limit, 0)) { - - // Shouldn't reach here - cmsFreeLUT(Lut); - cmsCloseProfile(hICC); - return NULL; - } - - // Create tags - - cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms ink limiting device link"); - cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "ink limiting built-in"); - - cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); - - cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); - - // LUT is already on virtual profile - cmsFreeLUT(Lut); - - // Ok, done - return hICC; -} - + // Creates a Pipeline with 3D grid only + LUT = cmsPipelineAlloc(ContextID, 4, 4); + if (LUT == NULL) goto Error; -static -LPLUT Create3x3EmptyLUT(void) -{ - LPLUT AToB0 = cmsAllocLUT(); - if (AToB0 == NULL) return NULL; + nChannels = cmsChannelsOf(ColorSpace); + + CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL); + if (CLUT == NULL) goto Error; + + if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) || + !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) || + !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels))) + goto Error; + + // Create tags + if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error; - AToB0 -> InputChan = AToB0 -> OutputChan = 3; - return AToB0; + if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error; + if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error; + + // cmsPipeline is already on virtual profile + cmsPipelineFree(LUT); + + // Ok, done + return hICC; + +Error: + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hICC != NULL) + cmsCloseProfile(hICC); + + return NULL; } - - -// Creates a fake Lab identity. -cmsHPROFILE LCMSEXPORT cmsCreateLabProfile(LPcmsCIExyY WhitePoint) +cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit) { - cmsHPROFILE hProfile; - LPLUT Lut; - - hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); - if (hProfile == NULL) return NULL; - - cmsSetDeviceClass(hProfile, icSigAbstractClass); - cmsSetColorSpace(hProfile, icSigLabData); - cmsSetPCS(hProfile, icSigLabData); - - cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity"); - cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab built-in"); - - - // An empty LUTs is all we need - Lut = Create3x3EmptyLUT(); - if (Lut == NULL) { - cmsCloseProfile(hProfile); - return NULL; - } - - cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut); - cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut); - - cmsFreeLUT(Lut); - - return hProfile; + return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit); } // Creates a fake Lab identity. -cmsHPROFILE LCMSEXPORT cmsCreateLab4Profile(LPcmsCIExyY WhitePoint) +cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) { - cmsHPROFILE hProfile; - LPLUT Lut; + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); + if (hProfile == NULL) return NULL; - hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); - if (hProfile == NULL) return NULL; + cmsSetProfileVersion(hProfile, 2.1); + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigLabData); + cmsSetPCS(hProfile, cmsSigLabData); + + if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL; - cmsSetProfileICCversion(hProfile, 0x4000000); + // An identity LUT is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); - cmsSetDeviceClass(hProfile, icSigAbstractClass); - cmsSetColorSpace(hProfile, icSigLabData); - cmsSetPCS(hProfile, icSigLabData); + return hProfile; + +Error: - cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity v4"); - cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab v4 built-in"); + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} - // An empty LUTs is all we need - Lut = Create3x3EmptyLUT(); - if (Lut == NULL) { - cmsCloseProfile(hProfile); - return NULL; - } - - Lut -> wFlags |= LUT_V4_INPUT_EMULATE_V2; - cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut); - - Lut -> wFlags |= LUT_V4_OUTPUT_EMULATE_V2; - cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut); - - cmsFreeLUT(Lut); - - return hProfile; +cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint) +{ + return cmsCreateLab2ProfileTHR(NULL, WhitePoint); } +// Creates a fake Lab V4 identity. +cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); + if (hProfile == NULL) return NULL; + + cmsSetProfileVersion(hProfile, 4.3); + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigLabData); + cmsSetPCS(hProfile, cmsSigLabData); + + if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error; + + // An empty LUTs is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); + + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint) +{ + return cmsCreateLab4ProfileTHR(NULL, WhitePoint); +} + // Creates a fake XYZ identity -cmsHPROFILE LCMSEXPORT cmsCreateXYZProfile(void) +cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID) { - cmsHPROFILE hProfile; - LPLUT Lut; + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL); + if (hProfile == NULL) return NULL; - hProfile = cmsCreateRGBProfile(cmsD50_xyY(), NULL, NULL); - if (hProfile == NULL) return NULL; + cmsSetProfileVersion(hProfile, 4.3); - cmsSetDeviceClass(hProfile, icSigAbstractClass); - cmsSetColorSpace(hProfile, icSigXYZData); - cmsSetPCS(hProfile, icSigXYZData); + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigXYZData); + cmsSetPCS(hProfile, cmsSigXYZData); + + if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error; - cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms XYZ identity"); - cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "XYZ built-in"); + // An identity LUT is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); - // An empty LUTs is all we need - Lut = Create3x3EmptyLUT(); - if (Lut == NULL) { - cmsCloseProfile(hProfile); - return NULL; - } + return hProfile; + +Error: - cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut); - cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut); - cmsAddTag(hProfile, icSigPreview0Tag, (LPVOID) Lut); + if (LUT != NULL) + cmsPipelineFree(LUT); - cmsFreeLUT(Lut); - return hProfile; + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; } - -/* - -If R’sRGB,G’sRGB, B’sRGB < 0.04045 - - R = R’sRGB / 12.92 - G = G’sRGB / 12.92 - B = B’sRGB / 12.92 - +cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void) +{ + return cmsCreateXYZProfileTHR(NULL); +} -else if R’sRGB,G’sRGB, B’sRGB >= 0.04045 - - R = ((R’sRGB + 0.055) / 1.055)^2.4 - G = ((G’sRGB + 0.055) / 1.055)^2.4 - B = ((B’sRGB + 0.055) / 1.055)^2.4 - - */ +//sRGB Curves are defined by: +// +//If R’sRGB,G’sRGB, B’sRGB < 0.04045 +// +// R = R’sRGB / 12.92 +// G = G’sRGB / 12.92 +// B = B’sRGB / 12.92 +// +// +//else if R’sRGB,G’sRGB, B’sRGB >= 0.04045 +// +// R = ((R’sRGB + 0.055) / 1.055)^2.4 +// G = ((G’sRGB + 0.055) / 1.055)^2.4 +// B = ((B’sRGB + 0.055) / 1.055)^2.4 static -LPGAMMATABLE Build_sRGBGamma(void) +cmsToneCurve* Build_sRGBGamma(cmsContext ContextID) { - double Parameters[5]; + cmsFloat64Number Parameters[5]; Parameters[0] = 2.4; Parameters[1] = 1. / 1.055; Parameters[2] = 0.055 / 1.055; Parameters[3] = 1. / 12.92; - Parameters[4] = 0.04045; // d + Parameters[4] = 0.04045; - return cmsBuildParametricGamma(1024, 4, Parameters); + return cmsBuildParametricToneCurve(ContextID, 4, Parameters); } // Create the ICC virtual profile for sRGB space -cmsHPROFILE LCMSEXPORT cmsCreate_sRGBProfile(void) +cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID) { - cmsCIExyY D65; + cmsCIExyY D65 = { 0.3127, 0.3290, 1.0 }; cmsCIExyYTRIPLE Rec709Primaries = { {0.6400, 0.3300, 1.0}, {0.3000, 0.6000, 1.0}, {0.1500, 0.0600, 1.0} }; - LPGAMMATABLE Gamma22[3]; + cmsToneCurve* Gamma22[3]; cmsHPROFILE hsRGB; - cmsWhitePointFromTemp(6504, &D65); - Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(); + // cmsWhitePointFromTemp(&D65, 6504); + Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID); + if (Gamma22[0] == NULL) return NULL; - hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma22); - cmsFreeGamma(Gamma22[0]); + hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22); + cmsFreeToneCurve(Gamma22[0]); if (hsRGB == NULL) return NULL; - - cmsAddTag(hsRGB, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hsRGB, icSigDeviceModelDescTag, (LPVOID) "sRGB built-in"); - cmsAddTag(hsRGB, icSigProfileDescriptionTag, (LPVOID) "sRGB built-in"); + if (!SetTextTags(hsRGB, L"sRGB built-in")) { + cmsCloseProfile(hsRGB); + return NULL; + } return hsRGB; } +cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void) +{ + return cmsCreate_sRGBProfileTHR(NULL); +} + typedef struct { - double Brightness; - double Contrast; - double Hue; - double Saturation; + cmsFloat64Number Brightness; + cmsFloat64Number Contrast; + cmsFloat64Number Hue; + cmsFloat64Number Saturation; + cmsBool lAdjustWP; cmsCIEXYZ WPsrc, WPdest; } BCHSWADJUSTS, *LPBCHSWADJUSTS; static -int bchswSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) { cmsCIELab LabIn, LabOut; cmsCIELCh LChIn, LChOut; @@ -798,9 +738,10 @@ cmsLCh2Lab(&LabOut, &LChOut); // Move white point in Lab - - cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabOut); - cmsXYZ2Lab(&bchsw ->WPdest, &LabOut, &XYZ); + if (bchsw->lAdjustWP) { + cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut); + cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ); + } // Back to encoded @@ -813,116 +754,492 @@ // Creates an abstract profile operating in Lab space for Brightness, // contrast, Saturation and white point displacement -cmsHPROFILE LCMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints, - double Bright, - double Contrast, - double Hue, - double Saturation, - int TempSrc, - int TempDest) +cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, + cmsUInt32Number nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + cmsUInt32Number TempSrc, + cmsUInt32Number TempDest) { - cmsHPROFILE hICC; - LPLUT Lut; - BCHSWADJUSTS bchsw; - cmsCIExyY WhitePnt; + cmsHPROFILE hICC; + cmsPipeline* Pipeline; + BCHSWADJUSTS bchsw; + cmsCIExyY WhitePnt; + cmsStage* CLUT; + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + cmsUInt32Number i; + + bchsw.Brightness = Bright; + bchsw.Contrast = Contrast; + bchsw.Hue = Hue; + bchsw.Saturation = Saturation; + if (TempSrc == TempDest) { - bchsw.Brightness = Bright; - bchsw.Contrast = Contrast; - bchsw.Hue = Hue; - bchsw.Saturation = Saturation; + bchsw.lAdjustWP = FALSE; + } + else { + bchsw.lAdjustWP = TRUE; + cmsWhitePointFromTemp(&WhitePnt, TempSrc); + cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt); + cmsWhitePointFromTemp(&WhitePnt, TempDest); + cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt); + + } + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; - cmsWhitePointFromTemp(TempSrc, &WhitePnt); - cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt); + cmsSetDeviceClass(hICC, cmsSigAbstractClass); + cmsSetColorSpace(hICC, cmsSigLabData); + cmsSetPCS(hICC, cmsSigLabData); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - cmsWhitePointFromTemp(TempDest, &WhitePnt); - cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt); + // Creates a Pipeline with 3D grid only + Pipeline = cmsPipelineAlloc(ContextID, 3, 3); + if (Pipeline == NULL) { + cmsCloseProfile(hICC); + return NULL; + } - hICC = _cmsCreateProfilePlaceholder(); - if (!hICC) // can't allocate - return NULL; + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints; + CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL); + if (CLUT == NULL) goto Error; - cmsSetDeviceClass(hICC, icSigAbstractClass); - cmsSetColorSpace(hICC, icSigLabData); - cmsSetPCS(hICC, icSigLabData); + if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) { + + // Shouldn't reach here + goto Error; + } + + if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) { + goto Error; + } + + // Create tags + if (!SetTextTags(hICC, L"BCHS built-in")) return NULL; - cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL); + cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ()); + + cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline); + + // Pipeline is already on virtual profile + cmsPipelineFree(Pipeline); + + // Ok, done + return hICC; + +Error: + cmsPipelineFree(Pipeline); + cmsCloseProfile(hICC); + return NULL; +} - // Creates a LUT with 3D grid only - Lut = cmsAllocLUT(); - if (Lut == NULL) { - cmsCloseProfile(hICC); - return NULL; - } - - cmsAlloc3DGrid(Lut, nLUTPoints, 3, 3); - - if (!cmsSample3DGrid(Lut, bchswSampler, (LPVOID) &bchsw, 0)) { - - // Shouldn't reach here - cmsFreeLUT(Lut); - cmsCloseProfile(hICC); - return NULL; - } - - // Create tags - - cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)"); - cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms BCHSW abstract profile"); - cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "BCHSW built-in"); - - cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ()); - - cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut); - - // LUT is already on virtual profile - cmsFreeLUT(Lut); - - // Ok, done - return hICC; - +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + cmsUInt32Number TempSrc, + cmsUInt32Number TempDest) +{ + return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest); } // Creates a fake NULL profile. This profile return 1 channel as always 0. // Is useful only for gamut checking tricks - -cmsHPROFILE LCMSEXPORT cmsCreateNULLProfile(void) +cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID) { - cmsHPROFILE hProfile; - LPLUT Lut; - LPGAMMATABLE EmptyTab; + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + cmsStage* PostLin; + cmsStage* OutLin; + cmsToneCurve* EmptyTab[3]; + cmsUInt16Number Zero[2] = { 0, 0 }; + const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; + + hProfile = cmsCreateProfilePlaceholder(ContextID); + if (!hProfile) // can't allocate + return NULL; + + cmsSetProfileVersion(hProfile, 4.3); + + if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error; + + + cmsSetDeviceClass(hProfile, cmsSigOutputClass); + cmsSetColorSpace(hProfile, cmsSigGrayData); + cmsSetPCS(hProfile, cmsSigLabData); + + // Create a valid ICC 4 structure + LUT = cmsPipelineAlloc(ContextID, 3, 1); + if (LUT == NULL) goto Error; + + EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); + PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab); + OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab); + cmsFreeToneCurve(EmptyTab[0]); + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin)) + goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL))) + goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin)) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error; + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; + + cmsPipelineFree(LUT); + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void) +{ + return cmsCreateNULLProfileTHR(NULL); +} + + +static +int IsPCS(cmsColorSpaceSignature ColorSpace) +{ + return (ColorSpace == cmsSigXYZData || + ColorSpace == cmsSigLabData); +} + + +static +void FixColorSpaces(cmsHPROFILE hProfile, + cmsColorSpaceSignature ColorSpace, + cmsColorSpaceSignature PCS, + cmsUInt32Number dwFlags) +{ + if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) { + + if (IsPCS(ColorSpace) && IsPCS(PCS)) { - hProfile = _cmsCreateProfilePlaceholder(); - if (!hProfile) // can't allocate - return NULL; + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); + return; + } + + if (IsPCS(ColorSpace) && !IsPCS(PCS)) { + + cmsSetDeviceClass(hProfile, cmsSigOutputClass); + cmsSetPCS(hProfile, ColorSpace); + cmsSetColorSpace(hProfile, PCS); + return; + } + + if (IsPCS(PCS) && !IsPCS(ColorSpace)) { + + cmsSetDeviceClass(hProfile, cmsSigInputClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); + return; + } + } + + cmsSetDeviceClass(hProfile, cmsSigLinkClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); +} + + + +// This function creates a named color profile dumping all the contents of transform to a single profile +// In this way, LittleCMS may be used to "group" several named color databases into a single profile. +// It has, however, several minor limitations. PCS is always Lab, which is not very critic since this +// is the normal PCS for named color profiles. +static +cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) +{ + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + cmsHPROFILE hICC = NULL; + cmsUInt32Number i, nColors; + cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL; - cmsSetDeviceClass(hProfile, icSigOutputClass); - cmsSetColorSpace(hProfile, icSigGrayData); - cmsSetPCS(hProfile, icSigLabData); + // Create an empty placeholder + hICC = cmsCreateProfilePlaceholder(v->ContextID); + if (hICC == NULL) return NULL; + + // Critical information + cmsSetDeviceClass(hICC, cmsSigNamedColorClass); + cmsSetColorSpace(hICC, v ->ExitColorSpace); + cmsSetPCS(hICC, cmsSigLabData); + + // Tag profile with information + if (!SetTextTags(hICC, L"Named color devicelink")) goto Error; + + Original = cmsGetNamedColorList(xform); + if (Original == NULL) goto Error; + + nColors = cmsNamedColorCount(Original); + nc2 = cmsDupNamedColorList(Original); + if (nc2 == NULL) goto Error; + + // Colorant count now depends on the output space + nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut); + + // Make sure we have proper formatters + cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX, + FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace)) + | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace))); + + // Apply the transfor to colorants. + for (i=0; i < nColors; i++) { + cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); + } + + if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error; + cmsFreeNamedColorList(nc2); + + return hICC; + +Error: + if (hICC != NULL) cmsCloseProfile(hICC); + return NULL; +} - // An empty LUTs is all we need - Lut = cmsAllocLUT(); - if (Lut == NULL) { - cmsCloseProfile(hProfile); - return NULL; - } +// This structure holds information about which MPU can be stored on a profile based on the version + +typedef struct { + cmsBool IsV4; // Is a V4 tag? + cmsTagSignature RequiredTag; // Set to 0 for both types + cmsTagTypeSignature LutType; // The LUT type + int nTypes; // Number of types (up to 5) + cmsStageSignature MpeTypes[5]; // 5 is the maximum number + +} cmsAllowedLUT; + +#define cmsSig0 ((cmsTagSignature) 0) + +static const cmsAllowedLUT AllowedLUTTypes[] = { + + { FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, + { FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, + { FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } }, + { TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } }, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } }, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }} +}; + +#define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT)) + +// Check a single entry +static +cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut) +{ + cmsStage* mpe; + int n; + + for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) { + + if (n > Tab ->nTypes) return FALSE; + if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE; + } + + return (n == Tab ->nTypes); +} + + +static +const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag) +{ + cmsUInt32Number n; + + for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) { - Lut -> InputChan = 3; - Lut -> OutputChan = 1; + const cmsAllowedLUT* Tab = AllowedLUTTypes + n; + + if (IsV4 ^ Tab -> IsV4) continue; + if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue; + + if (CheckOne(Tab, Lut)) return Tab; + } + + return NULL; +} + + +// Does convert a transform into a device link profile +cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) +{ + cmsHPROFILE hProfile = NULL; + cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut; + int ColorSpaceBitsIn, ColorSpaceBitsOut; + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + cmsPipeline* LUT = NULL; + cmsStage* mpe; + cmsContext ContextID = cmsGetTransformContextID(hTransform); + const cmsAllowedLUT* AllowedLUT; + cmsTagSignature DestinationTag; + cmsProfileClassSignature deviceClass; + + _cmsAssert(hTransform != NULL); + + // Get the first mpe to check for named color + mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut); + + // Check if is a named color transform + if (mpe != NULL) { + + if (cmsStageType(mpe) == cmsSigNamedColorElemType) { + return CreateNamedColorDevicelink(hTransform); + } + } + + // First thing to do is to get a copy of the transformation + LUT = cmsPipelineDup(xform ->Lut); + if (LUT == NULL) return NULL; + + // Time to fix the Lab2/Lab4 issue. + if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) { + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID))) + goto Error; + } + + // On the output side too + if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) { + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + } - EmptyTab = cmsAllocGamma(2); - EmptyTab ->GammaTable[0] = 0; - EmptyTab ->GammaTable[1] = 0; + + hProfile = cmsCreateProfilePlaceholder(ContextID); + if (!hProfile) goto Error; // can't allocate + + cmsSetProfileVersion(hProfile, Version); + + FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags); + + // Optimize the LUT and precalculate a devicelink + + ChansIn = cmsChannelsOf(xform -> EntryColorSpace); + ChansOut = cmsChannelsOf(xform -> ExitColorSpace); + + ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace); + ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace); + + FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2); + FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2); + + deviceClass = cmsGetDeviceClass(hProfile); + + if (deviceClass == cmsSigOutputClass) + DestinationTag = cmsSigBToA0Tag; + else + DestinationTag = cmsSigAToB0Tag; - cmsAllocLinearTable(Lut, &EmptyTab, 2); + // Check if the profile/version can store the result + if (dwFlags & cmsFLAGS_FORCE_CLUT) + AllowedLUT = NULL; + else + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + + if (AllowedLUT == NULL) { + + // Try to optimize + _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + + } + + // If no way, then force CLUT that for sure can be written + if (AllowedLUT == NULL) { + + cmsStage* FirstStage; + cmsStage* LastStage; + + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); + + // Put identity curves if needed + FirstStage = cmsPipelineGetPtrToFirstStage(LUT); + if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType) + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn))) + goto Error; - cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut); + LastStage = cmsPipelineGetPtrToLastStage(LUT); + if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType) + if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut))) + goto Error; + + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + } + + // Somethings is wrong... + if (AllowedLUT == NULL) { + goto Error; + } + + + if (dwFlags & cmsFLAGS_8BITS_DEVICELINK) + cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE); + + // Tag profile with information + if (!SetTextTags(hProfile, L"devicelink")) goto Error; + + // Store result + if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error; + + + if (xform -> InputColorant != NULL) { + if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error; + } - cmsFreeLUT(Lut); - cmsFreeGamma(EmptyTab); + if (xform -> OutputColorant != NULL) { + if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error; + } + + if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) { + if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error; + } - return hProfile; + // Set the white point + if (deviceClass == cmsSigInputClass) { + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error; + } + else { + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error; + } + + + // Per 7.2.15 in spec 4.3 + cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent); + + cmsPipelineFree(LUT); + return hProfile; + +Error: + if (LUT != NULL) cmsPipelineFree(LUT); + cmsCloseProfile(hProfile); + return NULL; } diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmswtpnt.c --- a/src/share/native/sun/java2d/cmm/lcms/cmswtpnt.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmswtpnt.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,271 +49,72 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#include "lcms.h" - - -// Conversions - -void LCMSEXPORT cmsXYZ2xyY(LPcmsCIExyY Dest, const cmsCIEXYZ* Source) -{ - double ISum; - - ISum = 1./(Source -> X + Source -> Y + Source -> Z); - - Dest -> x = (Source -> X) * ISum; - Dest -> y = (Source -> Y) * ISum; - Dest -> Y = Source -> Y; -} - - -void LCMSEXPORT cmsxyY2XYZ(LPcmsCIEXYZ Dest, const cmsCIExyY* Source) -{ - - Dest -> X = (Source -> x / Source -> y) * Source -> Y; - Dest -> Y = Source -> Y; - Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y; -} - - -// Obtains WhitePoint from Temperature - -LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint) -{ - double x, y; - double T, T2, T3; - // double M1, M2; - - - // No optimization provided. - - T = TempK; - T2 = T*T; // Square - T3 = T2*T; // Cube - - // For correlated color temperature (T) between 4000K and 7000K: - - if (T >= 4000. && T <= 7000.) - { - x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; - } - else - // or for correlated color temperature (T) between 7000K and 25000K: - - if (T > 7000.0 && T <= 25000.0) - { - x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; - } - else { - cmsSignalError(LCMS_ERRC_ABORTED, "cmsWhitePointFromTemp: invalid temp"); - return FALSE; - } - - // Obtain y(x) +// +//--------------------------------------------------------------------------------- +// - y = -3.000*(x*x) + 2.870*x - 0.275; - - // wave factors (not used, but here for futures extensions) - - // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); - // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); - - - - // Fill WhitePoint struct - - WhitePoint -> x = x; - WhitePoint -> y = y; - WhitePoint -> Y = 1.0; - - return TRUE; -} - -// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ -// This is just an approximation, I am not handling all the non-linear -// aspects of the RGB to XYZ process, and assumming that the gamma correction -// has transitive property in the tranformation chain. -// -// the alghoritm: -// -// - First I build the absolute conversion matrix using -// primaries in XYZ. This matrix is next inverted -// - Then I eval the source white point across this matrix -// obtaining the coeficients of the transformation -// - Then, I apply these coeficients to the original matrix - - -LCMSBOOL LCMSEXPORT cmsBuildRGB2XYZtransferMatrix(LPMAT3 r, LPcmsCIExyY WhitePt, - LPcmsCIExyYTRIPLE Primrs) -{ - VEC3 WhitePoint, Coef; - MAT3 Result, Primaries; - double xn, yn; - double xr, yr; - double xg, yg; - double xb, yb; - - - xn = WhitePt -> x; - yn = WhitePt -> y; - xr = Primrs -> Red.x; - yr = Primrs -> Red.y; - xg = Primrs -> Green.x; - yg = Primrs -> Green.y; - xb = Primrs -> Blue.x; - yb = Primrs -> Blue.y; - - - // Build Primaries matrix - VEC3init(&Primaries.v[0], xr, xg, xb); - VEC3init(&Primaries.v[1], yr, yg, yb); - VEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); - - - // Result = Primaries ^ (-1) inverse matrix - if (!MAT3inverse(&Primaries, &Result)) - return FALSE; +#include "lcms2_internal.h" - VEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn); - - // Across inverse primaries ... - MAT3eval(&Coef, &Result, &WhitePoint); +// D50 - Widely used +const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void) +{ + static cmsCIEXYZ D50XYZ = {cmsD50X, cmsD50Y, cmsD50Z}; - // Give us the Coefs, then I build transformation matrix - VEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb); - VEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb); - VEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb)); - - - return TRUE; + return &D50XYZ; } - - -// Compute chromatic adaptation matrix using Chad as cone matrix - -static -void ComputeChromaticAdaptation(LPMAT3 Conversion, - LPcmsCIEXYZ SourceWhitePoint, - LPcmsCIEXYZ DestWhitePoint, - LPMAT3 Chad) - +const cmsCIExyY* CMSEXPORT cmsD50_xyY(void) { - - MAT3 Chad_Inv; - VEC3 ConeSourceXYZ, ConeSourceRGB; - VEC3 ConeDestXYZ, ConeDestRGB; - MAT3 Cone, Tmp; - - - Tmp = *Chad; - MAT3inverse(&Tmp, &Chad_Inv); + static cmsCIExyY D50xyY; - VEC3init(&ConeSourceXYZ, SourceWhitePoint -> X, - SourceWhitePoint -> Y, - SourceWhitePoint -> Z); - - VEC3init(&ConeDestXYZ, DestWhitePoint -> X, - DestWhitePoint -> Y, - DestWhitePoint -> Z); - - MAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ); - MAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ); + cmsXYZ2xyY(&D50xyY, cmsD50_XYZ()); - // Build matrix - - VEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0); - VEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0); - VEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); - - - // Normalize - MAT3per(&Tmp, &Cone, Chad); - MAT3per(Conversion, &Chad_Inv, &Tmp); - + return &D50xyY; } - -// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll -// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed - -LCMSBOOL cmsAdaptationMatrix(LPMAT3 r, LPMAT3 ConeMatrix, LPcmsCIEXYZ FromIll, LPcmsCIEXYZ ToIll) +// Obtains WhitePoint from Temperature +cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK) { - MAT3 LamRigg = {{ // Bradford matrix - {{ 0.8951, 0.2664, -0.1614 }}, - {{ -0.7502, 1.7135, 0.0367 }}, - {{ 0.0389, -0.0685, 1.0296 }} - }}; - - - if (ConeMatrix == NULL) - ConeMatrix = &LamRigg; - - ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix); - return TRUE; + cmsFloat64Number x, y; + cmsFloat64Number T, T2, T3; + // cmsFloat64Number M1, M2; -} - -// Same as anterior, but assuming D50 destination. White point is given in xyY - -LCMSBOOL cmsAdaptMatrixToD50(LPMAT3 r, LPcmsCIExyY SourceWhitePt) -{ - cmsCIEXYZ Dn; - MAT3 Bradford; - MAT3 Tmp; + _cmsAssert(WhitePoint != NULL); - cmsxyY2XYZ(&Dn, SourceWhitePt); - - cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ()); + T = TempK; + T2 = T*T; // Square + T3 = T2*T; // Cube - Tmp = *r; - MAT3per(r, &Bradford, &Tmp); - - return TRUE; -} - + // For correlated color temperature (T) between 4000K and 7000K: -// Same as anterior, but assuming D50 source. White point is given in xyY - -LCMSBOOL cmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt) -{ - cmsCIEXYZ Dn; - MAT3 Bradford; - MAT3 Tmp; - - cmsxyY2XYZ(&Dn, DestWhitePt); - - cmsAdaptationMatrix(&Bradford, NULL, cmsD50_XYZ(), &Dn); - - Tmp = *r; - MAT3per(r, &Bradford, &Tmp); - - return TRUE; -} - + if (T >= 4000. && T <= 7000.) + { + x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; + } + else + // or for correlated color temperature (T) between 7000K and 25000K: -// Adapts a color to a given illuminant. Original color is expected to have -// a SourceWhitePt white point. - -LCMSBOOL LCMSEXPORT cmsAdaptToIlluminant(LPcmsCIEXYZ Result, - LPcmsCIEXYZ SourceWhitePt, - LPcmsCIEXYZ Illuminant, - LPcmsCIEXYZ Value) -{ - MAT3 Bradford; - VEC3 In, Out; + if (T > 7000.0 && T <= 25000.0) + { + x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; + } + else { + cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp"); + return FALSE; + } - // BradfordLamRiggChromaticAdaptation(&Bradford, SourceWhitePt, Illuminant); + // Obtain y(x) + y = -3.000*(x*x) + 2.870*x - 0.275; - cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant); + // wave factors (not used, but here for futures extensions) - VEC3init(&In, Value -> X, Value -> Y, Value -> Z); - MAT3eval(&Out, &Bradford, &In); + // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); + // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); - Result -> X = Out.n[0]; - Result -> Y = Out.n[1]; - Result -> Z = Out.n[2]; + WhitePoint -> x = x; + WhitePoint -> y = y; + WhitePoint -> Y = 1.0; return TRUE; } @@ -321,14 +123,14 @@ typedef struct { - double mirek; // temp (in microreciprocal kelvin) - double ut; // u coord of intersection w/ blackbody locus - double vt; // v coord of intersection w/ blackbody locus - double tt; // slope of ISOTEMPERATURE. line + cmsFloat64Number mirek; // temp (in microreciprocal kelvin) + cmsFloat64Number ut; // u coord of intersection w/ blackbody locus + cmsFloat64Number vt; // v coord of intersection w/ blackbody locus + cmsFloat64Number tt; // slope of ISOTEMPERATURE. line - } ISOTEMPERATURE,FAR* LPISOTEMPERATURE; + } ISOTEMPERATURE; -static ISOTEMPERATURE isotempdata[] = { +static const ISOTEMPERATURE isotempdata[] = { // {Mirek, Ut, Vt, Tt } {0, 0.18006, 0.26352, -0.24341}, {10, 0.18066, 0.26589, -0.25479}, @@ -360,27 +162,28 @@ {525, 0.31320, 0.35968, -15.628 }, {550, 0.32129, 0.36011, -23.325 }, {575, 0.32931, 0.36038, -40.770 }, - {600, 0.33724, 0.36051, -116.45 } + {600, 0.33724, 0.36051, -116.45 } }; #define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE) // Robertson's method - -static -double Robertson(LPcmsCIExyY v) +cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint) { - int j; - double us,vs; - double uj,vj,tj,di,dj,mi,mj; - double Tc = -1, xs, ys; + cmsUInt32Number j; + cmsFloat64Number us,vs; + cmsFloat64Number uj,vj,tj,di,dj,mi,mj; + cmsFloat64Number xs, ys; + + _cmsAssert(WhitePoint != NULL); + _cmsAssert(TempK != NULL); di = mi = 0; - xs = v -> x; - ys = v -> y; + xs = WhitePoint -> x; + ys = WhitePoint -> y; - // convert (x,y) to CIE 1960 (u,v) + // convert (x,y) to CIE 1960 (u,WhitePoint) us = (2*xs) / (-xs + 6*ys + 1.5); vs = (3*ys) / (-xs + 6*ys + 1.5); @@ -393,332 +196,184 @@ tj = isotempdata[j].tt; mj = isotempdata[j].mirek; - dj = ((vs - vj) - tj * (us - uj)) / sqrt(1 + tj*tj); + dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj); + + if ((j != 0) && (di/dj < 0.0)) { - if ((j!=0) && (di/dj < 0.0)) { - Tc = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi)); - break; + // Found a match + *TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi)); + return TRUE; } di = dj; mi = mj; } - - if (j == NISO) return -1; - return Tc; -} - - - -static -LCMSBOOL InRange(LPcmsCIExyY a, LPcmsCIExyY b, double tolerance) -{ - double dist_x, dist_y; - - dist_x = fabs(a->x - b->x); - dist_y = fabs(a->y - b->y); - - return (tolerance >= dist_x * dist_x + dist_y * dist_y); - + // Not found + return FALSE; } -typedef struct { - char Name[30]; - cmsCIExyY Val; - - } WHITEPOINTS,FAR *LPWHITEPOINTS; +// Compute chromatic adaptation matrix using Chad as cone matrix static -int FromD40toD150(LPWHITEPOINTS pts) +cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion, + const cmsCIEXYZ* SourceWhitePoint, + const cmsCIEXYZ* DestWhitePoint, + const cmsMAT3* Chad) + { - int i, n; + + cmsMAT3 Chad_Inv; + cmsVEC3 ConeSourceXYZ, ConeSourceRGB; + cmsVEC3 ConeDestXYZ, ConeDestRGB; + cmsMAT3 Cone, Tmp; + + + Tmp = *Chad; + if (!_cmsMAT3inverse(&Tmp, &Chad_Inv)) return FALSE; - n = 0; - for (i=40; i < 150; i ++) - { - sprintf(pts[n].Name, "D%d", i); - cmsWhitePointFromTemp((int) (i*100.0), &pts[n].Val); - n++; - } + _cmsVEC3init(&ConeSourceXYZ, SourceWhitePoint -> X, + SourceWhitePoint -> Y, + SourceWhitePoint -> Z); + + _cmsVEC3init(&ConeDestXYZ, DestWhitePoint -> X, + DestWhitePoint -> Y, + DestWhitePoint -> Z); - return n; -} + _cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ); + _cmsMAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ); + + // Build matrix + _cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0); + _cmsVEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0); + _cmsVEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); -// To be removed in future versions -void _cmsIdentifyWhitePoint(char *Buffer, LPcmsCIEXYZ WhitePt) + // Normalize + _cmsMAT3per(&Tmp, &Cone, Chad); + _cmsMAT3per(Conversion, &Chad_Inv, &Tmp); + + return TRUE; +} + +// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll +// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed +cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll) { - int i, n; - cmsCIExyY Val; - double T; - WHITEPOINTS SomeIlluminants[140] = { + cmsMAT3 LamRigg = {{ // Bradford matrix + {{ 0.8951, 0.2664, -0.1614 }}, + {{ -0.7502, 1.7135, 0.0367 }}, + {{ 0.0389, -0.0685, 1.0296 }} + }}; + + if (ConeMatrix == NULL) + ConeMatrix = &LamRigg; - {"CIE illuminant A", {0.4476, 0.4074, 1.0}}, - {"CIE illuminant C", {0.3101, 0.3162, 1.0}}, - {"D65 (daylight)", {0.3127, 0.3291, 1.0}}, - }; + return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix); +} - n = FromD40toD150(&SomeIlluminants[3]) + 3; +// Same as anterior, but assuming D50 destination. White point is given in xyY +static +cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt) +{ + cmsCIEXYZ Dn; + cmsMAT3 Bradford; + cmsMAT3 Tmp; - cmsXYZ2xyY(&Val, WhitePt); + cmsxyY2XYZ(&Dn, SourceWhitePt); + + if (!_cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ())) return FALSE; + + Tmp = *r; + _cmsMAT3per(r, &Bradford, &Tmp); + + return TRUE; +} - Val.Y = 1.; - for (i=0; i < n; i++) - { +// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ +// This is just an approximation, I am not handling all the non-linear +// aspects of the RGB to XYZ process, and assumming that the gamma correction +// has transitive property in the transformation chain. +// +// the alghoritm: +// +// - First I build the absolute conversion matrix using +// primaries in XYZ. This matrix is next inverted +// - Then I eval the source white point across this matrix +// obtaining the coeficients of the transformation +// - Then, I apply these coeficients to the original matrix +// +cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs) +{ + cmsVEC3 WhitePoint, Coef; + cmsMAT3 Result, Primaries; + cmsFloat64Number xn, yn; + cmsFloat64Number xr, yr; + cmsFloat64Number xg, yg; + cmsFloat64Number xb, yb; - if (InRange(&Val, &SomeIlluminants[i].Val, 0.000005)) - { - strcpy(Buffer, "WhitePoint : "); - strcat(Buffer, SomeIlluminants[i].Name); - return; - } - } + xn = WhitePt -> x; + yn = WhitePt -> y; + xr = Primrs -> Red.x; + yr = Primrs -> Red.y; + xg = Primrs -> Green.x; + yg = Primrs -> Green.y; + xb = Primrs -> Blue.x; + yb = Primrs -> Blue.y; + + // Build Primaries matrix + _cmsVEC3init(&Primaries.v[0], xr, xg, xb); + _cmsVEC3init(&Primaries.v[1], yr, yg, yb); + _cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); + - T = Robertson(&Val); + // Result = Primaries ^ (-1) inverse matrix + if (!_cmsMAT3inverse(&Primaries, &Result)) + return FALSE; + + + _cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn); - if (T > 0) - sprintf(Buffer, "White point near %dK", (int) T); - else - { - sprintf(Buffer, "Unknown white point (X:%1.2g, Y:%1.2g, Z:%1.2g)", - WhitePt -> X, WhitePt -> Y, WhitePt -> Z); + // Across inverse primaries ... + _cmsMAT3eval(&Coef, &Result, &WhitePoint); - } + // Give us the Coefs, then I build transformation matrix + _cmsVEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb); + _cmsVEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb); + _cmsVEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb)); + + + return _cmsAdaptMatrixToD50(r, WhitePt); } -// Use darker colorant to obtain black point - -static -int BlackPointAsDarkerColorant(cmsHPROFILE hInput, - int Intent, - LPcmsCIEXYZ BlackPoint, - DWORD dwFlags) +// Adapts a color to a given illuminant. Original color is expected to have +// a SourceWhitePt white point. +cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, + const cmsCIEXYZ* SourceWhitePt, + const cmsCIEXYZ* Illuminant, + const cmsCIEXYZ* Value) { - WORD *Black, *White; - cmsHTRANSFORM xform; - icColorSpaceSignature Space; - int nChannels; - DWORD dwFormat; - cmsHPROFILE hLab; - cmsCIELab Lab; - cmsCIEXYZ BlackXYZ, MediaWhite; - - // If the profile does not support input direction, assume Black point 0 - if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) { - - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return 0; - } - - - // Try to get black by using black colorant - Space = cmsGetColorSpace(hInput); - - if (!_cmsEndPointsBySpace(Space, &White, &Black, &nChannels)) { - - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return 0; - } + cmsMAT3 Bradford; + cmsVEC3 In, Out; - dwFormat = CHANNELS_SH(nChannels)|BYTES_SH(2); - - hLab = cmsCreateLabProfile(NULL); - - xform = cmsCreateTransform(hInput, dwFormat, - hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC); - + _cmsAssert(Result != NULL); + _cmsAssert(SourceWhitePt != NULL); + _cmsAssert(Illuminant != NULL); + _cmsAssert(Value != NULL); - cmsDoTransform(xform, Black, &Lab, 1); - - // Force it to be neutral, clip to max. L* of 50 - - Lab.a = Lab.b = 0; - if (Lab.L > 50) Lab.L = 50; - - cmsCloseProfile(hLab); - cmsDeleteTransform(xform); + if (!_cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE; - cmsLab2XYZ(NULL, &BlackXYZ, &Lab); - - if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { - - *BlackPoint = BlackXYZ; - } - else { + _cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z); + _cmsMAT3eval(&Out, &Bradford, &In); - if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)) { + Result -> X = Out.n[0]; + Result -> Y = Out.n[1]; + Result -> Z = Out.n[2]; - cmsTakeMediaWhitePoint(&MediaWhite, hInput); - cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ); - } - else - *BlackPoint = BlackXYZ; - } - - return 1; + return TRUE; } -// Get a black point of output CMYK profile, discounting any ink-limiting embedded -// in the profile. For doing that, use perceptual intent in input direction: -// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab - -static -int BlackPointUsingPerceptualBlack(LPcmsCIEXYZ BlackPoint, - cmsHPROFILE hProfile, - DWORD dwFlags) -{ - cmsHTRANSFORM hPercLab2CMYK, hRelColCMYK2Lab; - cmsHPROFILE hLab; - cmsCIELab LabIn, LabOut; - WORD CMYK[MAXCHANNELS]; - cmsCIEXYZ BlackXYZ, MediaWhite; - - - if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) { - - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return 0; - } - - hLab = cmsCreateLabProfile(NULL); - - hPercLab2CMYK = cmsCreateTransform(hLab, TYPE_Lab_DBL, - hProfile, TYPE_CMYK_16, - INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC); - - hRelColCMYK2Lab = cmsCreateTransform(hProfile, TYPE_CMYK_16, - hLab, TYPE_Lab_DBL, - INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOTPRECALC); - - LabIn.L = LabIn.a = LabIn.b = 0; - - cmsDoTransform(hPercLab2CMYK, &LabIn, CMYK, 1); - cmsDoTransform(hRelColCMYK2Lab, CMYK, &LabOut, 1); - - if (LabOut.L > 50) LabOut.L = 50; - LabOut.a = LabOut.b = 0; - - cmsDeleteTransform(hPercLab2CMYK); - cmsDeleteTransform(hRelColCMYK2Lab); - cmsCloseProfile(hLab); - - cmsLab2XYZ(NULL, &BlackXYZ, &LabOut); - - if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)){ - cmsTakeMediaWhitePoint(&MediaWhite, hProfile); - cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ); - } - else - *BlackPoint = BlackXYZ; - - return 1; - -} - - -// Get Perceptual black of v4 profiles. -static -int GetV4PerceptualBlack(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, DWORD dwFlags) -{ - if (dwFlags & LCMS_BPFLAGS_D50_ADAPTED) { - - BlackPoint->X = PERCEPTUAL_BLACK_X; - BlackPoint->Y = PERCEPTUAL_BLACK_Y; - BlackPoint->Z = PERCEPTUAL_BLACK_Z; - } - else { - - cmsCIEXYZ D50BlackPoint, MediaWhite; - - cmsTakeMediaWhitePoint(&MediaWhite, hProfile); - D50BlackPoint.X = PERCEPTUAL_BLACK_X; - D50BlackPoint.Y = PERCEPTUAL_BLACK_Y; - D50BlackPoint.Z = PERCEPTUAL_BLACK_Z; - - // Obtain the absolute XYZ. Adapt perceptual black back from D50 to whatever media white - cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &D50BlackPoint); - } - - - return 1; -} - - -// This function shouldn't exist at all -- there is such quantity of broken -// profiles on black point tag, that we must somehow fix chromaticity to -// avoid huge tint when doing Black point compensation. This function does -// just that. There is a special flag for using black point tag, but turned -// off by default because it is bogus on most profiles. The detection algorithm -// involves to turn BP to neutral and to use only L component. - -int cmsDetectBlackPoint(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, int Intent, DWORD dwFlags) -{ - - // v4 + perceptual & saturation intents does have its own black point, and it is - // well specified enough to use it. - - if ((cmsGetProfileICCversion(hProfile) >= 0x4000000) && - (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { - - // Matrix shaper share MRC & perceptual intents - if (_cmsIsMatrixShaper(hProfile)) - return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, cmsFLAGS_NOTPRECALC); - - // CLUT based - Get perceptual black point (fixed value) - return GetV4PerceptualBlack(BlackPoint, hProfile, dwFlags); - } - - -#ifdef HONOR_BLACK_POINT_TAG - - // v2, v4 rel/abs colorimetric - if (cmsIsTag(hProfile, icSigMediaBlackPointTag) && - Intent == INTENT_RELATIVE_COLORIMETRIC) { - - cmsCIEXYZ BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite; - cmsCIELab Lab; - - // If black point is specified, then use it, - - cmsTakeMediaBlackPoint(&BlackXYZ, hProfile); - cmsTakeMediaWhitePoint(&MediaWhite, hProfile); - - // Black point is absolute XYZ, so adapt to D50 to get PCS value - cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ); - - // Force a=b=0 to get rid of any chroma - - cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint); - Lab.a = Lab.b = 0; - if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50 - - cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab); - - // Return BP as D50 relative or absolute XYZ (depends on flags) - if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)) - cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &TrustedBlackPoint); - else - *BlackPoint = TrustedBlackPoint; - - return 1; - } - -#endif - - // That is about v2 profiles. - - // If output profile, discount ink-limiting and that's all - if (Intent == INTENT_RELATIVE_COLORIMETRIC && - (cmsGetDeviceClass(hProfile) == icSigOutputClass) && - (cmsGetColorSpace(hProfile) == icSigCmykData)) - return BlackPointUsingPerceptualBlack(BlackPoint, hProfile, dwFlags); - - // Nope, compute BP using current intent. - return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags); - -} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/cmsxform.c --- a/src/share/native/sun/java2d/cmm/lcms/cmsxform.c Wed Jan 31 22:55:12 2018 -0800 +++ b/src/share/native/sun/java2d/cmm/lcms/cmsxform.c Thu Dec 07 09:11:50 2017 -0800 @@ -27,9 +27,10 @@ // However, the following notice accompanied the original version of this // file: // +//--------------------------------------------------------------------------------- // -// Little cms -// Copyright (C) 1998-2007 Marti Maria +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), @@ -48,2005 +49,1320 @@ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - +// +//--------------------------------------------------------------------------------- +// -#include "lcms.h" - +#include "lcms2_internal.h" // Transformations stuff // ----------------------------------------------------------------------- +#define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0 -// Interface +// The Context0 observer adaptation state. +_cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE }; + +// Init and duplicate observer adaptation state +void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE }; + void* from; + + if (src != NULL) { + from = src ->chunks[AdaptationStateContext]; + } + else { + from = &AdaptationStateChunk; + } -cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input, - DWORD InputFormat, - cmsHPROFILE Output, - DWORD OutputFormat, - int Intent, - DWORD dwFlags); + ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType)); +} + + +// Sets adaptation state for absolute colorimetric intent in the given context. Adaptation state applies on all +// but cmsCreateExtendedTransformTHR(). Little CMS can handle incomplete adaptation states. +cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d) +{ + cmsFloat64Number prev; + _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext); -cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input, - DWORD InputFormat, - cmsHPROFILE Output, - DWORD OutputFormat, - cmsHPROFILE Proofing, - int Intent, - int ProofingIntent, - DWORD dwFlags); + // Get previous value for return + prev = ptr ->AdaptationState; + + // Set the value if d is positive or zero + if (d >= 0.0) { + + ptr ->AdaptationState = d; + } + + // Always return previous value + return prev; +} -void LCMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform); +// The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine +cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d) +{ + return cmsSetAdaptationStateTHR(NULL, d); +} -void LCMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, - LPVOID InputBuffer, - LPVOID OutputBuffer, unsigned int Size); +// ----------------------------------------------------------------------- + +// Alarm codes for 16-bit transformations, because the fixed range of containers there are +// no values left to mark out of gamut. -void LCMSEXPORT cmsGetAlarmCodes(int *r, int *g, int *b); -void LCMSEXPORT cmsSetAlarmCodes(int r, int g, int b); -LCMSBOOL LCMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, - int Intent, int UsedDirection); +#define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +_cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE }; -// ------------------------------------------------------------------------- +// Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be +// encoded in 16 bits. +void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS]) +{ + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); - -// Alarm RGB codes + _cmsAssert(ContextAlarmCodes != NULL); // Can't happen -static WORD AlarmR = 0x8fff, AlarmG = 0x8fff, AlarmB = 0x8fff; + memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes)); +} -// Tag tables, soted by intents +// Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context. +// Values are meant to be encoded in 16 bits. +void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS]) +{ + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); + + _cmsAssert(ContextAlarmCodes != NULL); // Can't happen -static icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual - icSigAToB1Tag, // Relative colorimetric - icSigAToB2Tag, // Saturation - icSigAToB1Tag }; // Absolute colorimetric - // (Relative/WhitePoint) + memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes)); +} + +void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]) +{ + _cmsAssert(NewAlarm != NULL); -static icTagSignature PCS2Device[] = {icSigBToA0Tag, // Perceptual - icSigBToA1Tag, // Relative colorimetric - icSigBToA2Tag, // Saturation - icSigBToA1Tag }; // Absolute colorimetric - // (Relative/WhitePoint) + cmsSetAlarmCodesTHR(NULL, NewAlarm); +} + +void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS]) +{ + _cmsAssert(OldAlarm != NULL); + cmsGetAlarmCodesTHR(NULL, OldAlarm); +} -static icTagSignature Preview[] = {icSigPreview0Tag, - icSigPreview1Tag, - icSigPreview2Tag, - icSigPreview1Tag }; +// Init and duplicate alarm codes +void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE }; + void* from; - + if (src != NULL) { + from = src ->chunks[AlarmCodesContext]; + } + else { + from = &AlarmCodesChunk; + } -static volatile double GlobalAdaptationState = 0; + ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType)); +} -// --------------------------------Stages-------------------------------------- +// ----------------------------------------------------------------------- -// Following routines does implement several kind of steps inside -// transform. On building the transform, code chooses adequate. +// Get rid of transform resources +void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform; + _cmsAssert(p != NULL); -// From Shaper-Matrix to PCS + if (p -> GamutCheck) + cmsPipelineFree(p -> GamutCheck); + + if (p -> Lut) + cmsPipelineFree(p -> Lut); -static -void ShaperMatrixToPCS(struct _cmstransform_struct *p, - WORD In[3], WORD Out[3]) -{ - cmsEvalMatShaper(p -> InMatShaper, In, Out); + if (p ->InputColorant) + cmsFreeNamedColorList(p ->InputColorant); + + if (p -> OutputColorant) + cmsFreeNamedColorList(p ->OutputColorant); + + if (p ->Sequence) + cmsFreeProfileSequenceDescription(p ->Sequence); + + if (p ->UserData) + p ->FreeUserData(p ->ContextID, p ->UserData); + + _cmsFree(p ->ContextID, (void *) p); } -// From LUT to PCS +// Apply transform. +void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size) -static -void LUTtoPCS(struct _cmstransform_struct *p, - WORD In[], WORD Out[3]) { - cmsEvalLUT(p -> Device2PCS, In, Out); + _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; + cmsStride stride; + + stride.BytesPerLineIn = 0; // Not used + stride.BytesPerLineOut = 0; + stride.BytesPerPlaneIn = Size; + stride.BytesPerPlaneOut = Size; + + p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride); } -// From indexed named color to PCS -static -void NC2toPCS(struct _cmstransform_struct *p, - WORD In[], WORD Out[3]) +// This is a legacy stride for planar +void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size, cmsUInt32Number Stride) + { - int index = In[0]; + _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; + cmsStride stride; - if (index >= p ->NamedColorList-> nColors) - cmsSignalError(LCMS_ERRC_WARNING, "Color %d out of range", index); - else - CopyMemory(Out, p ->NamedColorList->List[index].PCS, 3 * sizeof(WORD)); + stride.BytesPerLineIn = 0; + stride.BytesPerLineOut = 0; + stride.BytesPerPlaneIn = Stride; + stride.BytesPerPlaneOut = Stride; + + p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride); } -// From PCS to Shaper-Matrix +// This is the "fast" function for plugins +void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + cmsUInt32Number BytesPerLineIn, + cmsUInt32Number BytesPerLineOut, + cmsUInt32Number BytesPerPlaneIn, + cmsUInt32Number BytesPerPlaneOut) -static -void PCStoShaperMatrix(struct _cmstransform_struct *p, - WORD In[3], WORD Out[3]) { - cmsEvalMatShaper(p -> OutMatShaper, In, Out); -} - -// From PCS to LUT + _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; + cmsStride stride; -static -void PCStoLUT(struct _cmstransform_struct *p, - WORD In[3], WORD Out[]) -{ - cmsEvalLUT(p -> PCS2Device, In, Out); + stride.BytesPerLineIn = BytesPerLineIn; + stride.BytesPerLineOut = BytesPerLineOut; + stride.BytesPerPlaneIn = BytesPerPlaneIn; + stride.BytesPerPlaneOut = BytesPerPlaneOut; + + p->xform(p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride); } - -// ----------------------- TRANSFORMATIONS -------------------------- - - -// Inlining some assignations - -#define COPY_3CHANS(to, from) { to[0]=from[0]; to[1]=from[1]; to[2]=from[2]; } - - -// Null transformation, only hold channels +// Transform routines ---------------------------------------------------------------------------------------------------------- -static -void NullXFORM(_LPcmsTRANSFORM p, - LPVOID in, - LPVOID out, unsigned int Size) -{ - register LPBYTE accum; - register LPBYTE output; - WORD wIn[MAXCHANNELS]; - register unsigned int i, n; - - - accum = (LPBYTE) in; - output = (LPBYTE) out; - n = Size; // Buffer len - - for (i=0; i < n; i++) - { - accum = p -> FromInput(p, wIn, accum); - output = p -> ToOutput(p, wIn, output); - } - -} - - -// This is the "normal" proofing transform - +// Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check. +// Note that because extended range, we can use a -1.0 value for out of gamut in this case. static -void NormalXFORM(_LPcmsTRANSFORM p, - LPVOID in, - LPVOID out, unsigned int Size) +void FloatXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) { - register LPBYTE accum; - register LPBYTE output; - WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS]; - WORD wStageABC[3], wPCS[3], wStageLMN[MAXCHANNELS]; - WORD wGamut[1]; - register unsigned int i, n; + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS]; + cmsFloat32Number OutOfGamut; + cmsUInt32Number i, j, c, strideIn, strideOut; + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + strideIn = 0; + strideOut = 0; + memset(fIn, 0, sizeof(fIn)); + memset(fOut, 0, sizeof(fIn)); - accum = (LPBYTE) in; - output = (LPBYTE) out; - n = Size; // Buffer len + for (i = 0; i < LineCount; i++) { - for (i=0; i < n; i++) - { + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { - accum = p -> FromInput(p, wIn, accum); + accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn); - p -> FromDevice(p, wIn, wStageABC); + // Any gamut chack to do? + if (p->GamutCheck != NULL) { - if (p -> Stage1) { + // Evaluate gamut marker. + cmsPipelineEvalFloat(fIn, &OutOfGamut, p->GamutCheck); - p -> Stage1(wStageABC, wPCS, &p->m1, &p->of1); + // Is current color out of gamut? + if (OutOfGamut > 0.0) { - if (wPCS[0] == 0xFFFF && - wPCS[1] == 0xFFFF && - wPCS[2] == 0xFFFF) { - - // White cutoff + // Certainly, out of gamut + for (c = 0; c < cmsMAXCHANNELS; c++) + fOut[c] = -1.0; - output = p -> ToOutput((_LPcmsTRANSFORM) p, - _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)), - output); - continue; - } - } - else - COPY_3CHANS(wPCS, wStageABC); + } + else { + // No, proceed normally + cmsPipelineEvalFloat(fIn, fOut, p->Lut); + } + } + else { + + // No gamut check at all + cmsPipelineEvalFloat(fIn, fOut, p->Lut); + } - if (p->Gamut) { - - // Gamut check, enabled across CLUT - - cmsEvalLUT(p -> Gamut, wPCS, wGamut); - - if (wGamut[0] >= 1) { - - wOut[0] = AlarmR; // Gamut alarm - wOut[1] = AlarmG; - wOut[2] = AlarmB; - wOut[3] = 0; - - output = p -> ToOutput((_LPcmsTRANSFORM)p, wOut, output); - continue; - } - } - - if (p -> Preview) - { - WORD wPreview[3]; // PCS - - cmsEvalLUT(p -> Preview, wPCS, wPreview); - COPY_3CHANS(wPCS, wPreview); - } - - if (p -> Stage2) { - - p -> Stage2(wPCS, wStageLMN, &p->m2, &p->of2); - - if (wPCS[0] == 0xFFFF && - wPCS[1] == 0xFFFF && - wPCS[2] == 0xFFFF) { - - // White cutoff - - output = p -> ToOutput((_LPcmsTRANSFORM)p, - _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)), - output); - - continue; - } - - } - else - COPY_3CHANS(wStageLMN, wPCS); - - // Here wOut may come as MAXCHANNELS channels - - p -> ToDevice(p, wStageLMN, wOut); - - output = p -> ToOutput((_LPcmsTRANSFORM)p, wOut, output); - } -} - -// Using precalculated LUT + output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut); + } -static -void PrecalculatedXFORM(_LPcmsTRANSFORM p, - LPVOID in, - LPVOID out, unsigned int Size) -{ - register LPBYTE accum; - register LPBYTE output; - WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS]; - unsigned int i, n; - - - accum = (LPBYTE) in; - output = (LPBYTE) out; - n = Size; // Buffer len - - - for (i=0; i < n; i++) { - - accum = p -> FromInput(p, wIn, accum); - - // Try to speedup things on plain devicelinks - - if (p ->DeviceLink ->wFlags == LUT_HAS3DGRID) { - - p ->DeviceLink ->CLut16params.Interp3D(wIn, wOut, - p ->DeviceLink -> T, - &p ->DeviceLink -> CLut16params); - } - else - cmsEvalLUT(p -> DeviceLink, wIn, wOut); - - - output = p -> ToOutput(p, wOut, output); - } -} - -// Auxiliar: Handle precalculated gamut check - -static -void TransformOnePixelWithGamutCheck(_LPcmsTRANSFORM p, WORD wIn[], WORD wOut[]) -{ - WORD wOutOfGamut; - - cmsEvalLUT(p ->GamutCheck, wIn, &wOutOfGamut); - - if (wOutOfGamut >= 1) { - - ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS); - - wOut[0] = AlarmR; - wOut[1] = AlarmG; - wOut[2] = AlarmB; - - } - else - cmsEvalLUT(p -> DeviceLink, wIn, wOut); + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } } static -void PrecalculatedXFORMGamutCheck(_LPcmsTRANSFORM p, - LPVOID in, - LPVOID out, unsigned int Size) +void NullFloatXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) + +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsFloat32Number fIn[cmsMAXCHANNELS]; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(fIn, 0, sizeof(fIn)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*) in + strideIn; + output = (cmsUInt8Number*) out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInputFloat(p, fIn, accum, Stride ->BytesPerPlaneIn); + output = p->ToOutputFloat(p, fIn, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} + +// 16 bit precision ----------------------------------------------------------------------------------------------------------- + +// Null transformation, only applies formatters. No caché +static +void NullXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS]; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(wIn, 0, sizeof(wIn)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } + +} + + +// No gamut check, no cache, 16 bits +static +void PrecalculatedXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) { - register LPBYTE accum; - register LPBYTE output; - WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS]; - register unsigned int i, n; + register cmsUInt8Number* accum; + register cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data); + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } + +} + + +// Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical. +static +void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, + const cmsUInt16Number wIn[], + cmsUInt16Number wOut[]) +{ + cmsUInt16Number wOutOfGamut; + + p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data); + if (wOutOfGamut >= 1) { + + cmsUInt16Number i; + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext); + + for (i=0; i < p ->Lut->OutputChannels; i++) { + + wOut[i] = ContextAlarmCodes ->AlarmCodes[i]; + } + } + else + p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); +} + +// Gamut check, No caché, 16 bits. +static +void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + TransformOnePixelWithGamutCheck(p, wIn, wOut); + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} - accum = (LPBYTE) in; - output = (LPBYTE) out; - n = Size; // Buffer len +// No gamut check, Caché, 16 bits, +static +void CachedXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + _cmsCACHE Cache; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + // Empty buffers for quick memcmp + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + // Get copy of zero cache + memcpy(&Cache, &p->Cache, sizeof(Cache)); + + strideIn = 0; + strideOut = 0; + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + + if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { + + memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); + } + else { + p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data); + + memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); + memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); + } - for (i=0; i < n; i++) { + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} + +// All those nice features together +static +void CachedXFORMGamutCheck(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + _cmsCACHE Cache; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + // Empty buffers for quick memcmp + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + // Get copy of zero cache + memcpy(&Cache, &p->Cache, sizeof(Cache)); + + strideIn = 0; + strideOut = 0; + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + + if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { + + memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); + } + else { + TransformOnePixelWithGamutCheck(p, wIn, wOut); - accum = p -> FromInput(p, wIn, accum); + memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); + memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); + } + + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} + +// Transform plug-ins ---------------------------------------------------------------------------------------------------- + +// List of used-defined transform factories +typedef struct _cmsTransformCollection_st { + + _cmsTransform2Factory Factory; + cmsBool OldXform; // Factory returns xform function in the old style + + struct _cmsTransformCollection_st *Next; + +} _cmsTransformCollection; + +// The linked list head +_cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginTransformList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsTransformPluginChunkType newHead = { NULL }; + _cmsTransformCollection* entry; + _cmsTransformCollection* Anterior = NULL; + _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin]; + + // Walk the list copying all nodes + for (entry = head->TransformCollection; + entry != NULL; + entry = entry ->Next) { + + _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection)); + + if (newEntry == NULL) + return; - TransformOnePixelWithGamutCheck(p, wIn, wOut); + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.TransformCollection == NULL) + newHead.TransformCollection = newEntry; + } + + ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType)); +} + +// Allocates memory for transform plugin factory +void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { - output = p -> ToOutput(p, wOut, output); + // Copy all linked list + DupPluginTransformList(ctx, src); + } + else { + static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL }; + ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType)); + } +} + +// Adaptor for old versions of plug-in +static +void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + + cmsUInt32Number i, strideIn, strideOut; + + _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + + for (i = 0; i < LineCount; i++) { + + void *accum = (cmsUInt8Number*)InputBuffer + strideIn; + void *output = (cmsUInt8Number*)OutputBuffer + strideOut; + + CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn); + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; } } -// Using precalculated LUT + Cache - -static -void CachedXFORM(_LPcmsTRANSFORM p, - LPVOID in, - LPVOID out, unsigned int Size) +// Register new ways to transform +cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data) { - register LPBYTE accum; - register LPBYTE output; - WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS]; - register unsigned int i, n; - WORD CacheIn[MAXCHANNELS], CacheOut[MAXCHANNELS]; - - - accum = (LPBYTE) in; - output = (LPBYTE) out; - n = Size; // Buffer len - - // Empty buffers for quick memcmp - - ZeroMemory(wIn, sizeof(WORD) * MAXCHANNELS); - ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS); - - - LCMS_READ_LOCK(&p ->rwlock); - CopyMemory(CacheIn, p ->CacheIn, sizeof(WORD) * MAXCHANNELS); - CopyMemory(CacheOut, p ->CacheOut, sizeof(WORD) * MAXCHANNELS); - LCMS_UNLOCK(&p ->rwlock); - - for (i=0; i < n; i++) { - - accum = p -> FromInput(p, wIn, accum); - + cmsPluginTransform* Plugin = (cmsPluginTransform*) Data; + _cmsTransformCollection* fl; + _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin); - if (memcmp(wIn, CacheIn, sizeof(WORD) * MAXCHANNELS) == 0) { - - CopyMemory(wOut, CacheOut, sizeof(WORD) * MAXCHANNELS); - } - else { - - // Try to speedup things on plain devicelinks - - if (p ->DeviceLink ->wFlags == LUT_HAS3DGRID) { - - p ->DeviceLink ->CLut16params.Interp3D(wIn, wOut, - p ->DeviceLink -> T, - &p ->DeviceLink -> CLut16params); - } - else - cmsEvalLUT(p -> DeviceLink, wIn, wOut); + if (Data == NULL) { - - CopyMemory(CacheIn, wIn, sizeof(WORD) * MAXCHANNELS); - CopyMemory(CacheOut, wOut, sizeof(WORD) * MAXCHANNELS); - } - - output = p -> ToOutput(p, wOut, output); - } + // Free the chain. Memory is safely freed at exit + ctx->TransformCollection = NULL; + return TRUE; + } - - LCMS_WRITE_LOCK(&p ->rwlock); - CopyMemory(p->CacheIn, CacheIn, sizeof(WORD) * MAXCHANNELS); - CopyMemory(p->CacheOut, CacheOut, sizeof(WORD) * MAXCHANNELS); - LCMS_UNLOCK(&p ->rwlock); - -} - + // Factory callback is required + if (Plugin->factories.xform == NULL) return FALSE; -// Using precalculated LUT + Cache - -static -void CachedXFORMGamutCheck(_LPcmsTRANSFORM p, - LPVOID in, - LPVOID out, unsigned int Size) -{ - register LPBYTE accum; - register LPBYTE output; - WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS]; - register unsigned int i, n; - WORD CacheIn[MAXCHANNELS], CacheOut[MAXCHANNELS]; - - - accum = (LPBYTE) in; - output = (LPBYTE) out; - n = Size; // Buffer len + fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection)); + if (fl == NULL) return FALSE; - // Empty buffers for quick memcmp - - ZeroMemory(wIn, sizeof(WORD) * MAXCHANNELS); - ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS); - - LCMS_READ_LOCK(&p ->rwlock); - CopyMemory(CacheIn, p ->CacheIn, sizeof(WORD) * MAXCHANNELS); - CopyMemory(CacheOut, p ->CacheOut, sizeof(WORD) * MAXCHANNELS); - LCMS_UNLOCK(&p ->rwlock); - - - for (i=0; i < n; i++) { - - accum = p -> FromInput(p, wIn, accum); - - if (memcmp(wIn, CacheIn, sizeof(WORD) * MAXCHANNELS) == 0) { - - CopyMemory(wOut, CacheOut, sizeof(WORD) * MAXCHANNELS); - } - else { + // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case + if (Plugin->base.ExpectedVersion < 2080) { - TransformOnePixelWithGamutCheck(p, wIn, wOut); - - CopyMemory(CacheIn, wIn, sizeof(WORD) * MAXCHANNELS); - CopyMemory(CacheOut, wOut, sizeof(WORD) * MAXCHANNELS); - } - - output = p -> ToOutput(p, wOut, output); - } - - LCMS_WRITE_LOCK(&p ->rwlock); - CopyMemory(p->CacheIn, CacheIn, sizeof(WORD) * MAXCHANNELS); - CopyMemory(p->CacheOut, CacheOut, sizeof(WORD) * MAXCHANNELS); - LCMS_UNLOCK(&p ->rwlock); -} - - -// Using smelted Matrix/Shaper + fl->OldXform = TRUE; + } + else + fl->OldXform = FALSE; -static -void MatrixShaperXFORM(_LPcmsTRANSFORM p, - LPVOID in, - LPVOID out, unsigned int Size) -{ - register LPBYTE accum; - register LPBYTE output; - WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS]; - register unsigned int i, n; - + // Copy the parameters + fl->Factory = Plugin->factories.xform; - accum = (LPBYTE) in; - output = (LPBYTE) out; - n = Size; // Buffer len + // Keep linked list + fl ->Next = ctx->TransformCollection; + ctx->TransformCollection = fl; - for (i=0; i < n; i++) - { - accum = p -> FromInput(p, wIn, accum); - cmsEvalMatShaper(p -> SmeltMatShaper, wIn, wOut); - output = p -> ToOutput(p, wOut, output); - } + // All is ok + return TRUE; } -// Using Named color input table - -static -void NC2deviceXform(_LPcmsTRANSFORM p, - LPVOID in, - LPVOID out, unsigned int Size) +void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn) { - - register LPBYTE accum; - register LPBYTE output; - WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS]; - register unsigned int i; - - - accum = (LPBYTE) in; - output = (LPBYTE) out; - - for (i=0; i < Size; i++) { - - accum = p -> FromInput(p, wIn, accum); - CopyMemory(wOut, p ->NamedColorList->List[wIn[0]].DeviceColorant, sizeof(WORD) * MAXCHANNELS); - output = p -> ToOutput(p, wOut, output); - } - + _cmsAssert(CMMcargo != NULL); + CMMcargo ->UserData = ptr; + CMMcargo ->FreeUserData = FreePrivateDataFn; } - - -// -------------------------------------------------------------------------- -// Build a LUT based on shape-matrix method. - - -// Some non-conformant gray profiles are using kTCR as L*, -// this function converts the curve to XYZ PCS. - -static -void FromLstarToXYZ(LPGAMMATABLE g, LPGAMMATABLE gxyz[3]) +// returns the pointer defined by the plug-in to store private data +void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo) { - int i; - int nPoints = 4096; - cmsCIELab Lab; - cmsCIEXYZ XYZ; - L16PARAMS L16; - - // Setup interpolation across origin - cmsCalcL16Params(g ->nEntries, &L16); - - // Allocate curves - gxyz[0] = cmsAllocGamma(nPoints); - gxyz[1] = cmsAllocGamma(nPoints); - gxyz[2] = cmsAllocGamma(nPoints); - - // Transport from Lab to XYZ - - for (i=0; i < nPoints; i++) { - - WORD val = _cmsQuantizeVal(i, nPoints); - WORD w = cmsLinearInterpLUT16(val, g->GammaTable, &L16); - - Lab.L = ((double) 100.0 * w ) / 65535.0; - Lab.a = Lab.b = 0; - - cmsLab2XYZ(NULL, &XYZ, &Lab); - - // Should be same curve - gxyz[0] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.X) / D50X + 0.5); - gxyz[1] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.Y) / D50Y + 0.5); - gxyz[2] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.Z) / D50Z + 0.5); - } + _cmsAssert(CMMcargo != NULL); + return CMMcargo ->UserData; } -// Monochrome version - -static -LPMATSHAPER cmsBuildGrayInputMatrixShaper(cmsHPROFILE hProfile) +// returns the current formatters +void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput) { - cmsCIEXYZ Illuminant; - LPGAMMATABLE GrayTRC, Shapes[3]; - LPMATSHAPER MatShaper; - MAT3 Scale; - - GrayTRC = cmsReadICCGamma(hProfile, icSigGrayTRCTag); // Y - if (GrayTRC == NULL) return NULL; - - cmsTakeIluminant(&Illuminant, hProfile); - - if (cmsGetPCS(hProfile) == icSigLabData) { - - // Fixup for Lab monochrome - FromLstarToXYZ(GrayTRC, Shapes); - } - else { - Shapes[0] = cmsDupGamma(GrayTRC); - Shapes[1] = cmsDupGamma(GrayTRC); - Shapes[2] = cmsDupGamma(GrayTRC); - } - - if (!Shapes[0] || !Shapes[1] || !Shapes[2]) - return NULL; - - cmsFreeGamma(GrayTRC); - - // R=G=B as precondition - - VEC3init(&Scale.v[0], Illuminant.X/3, Illuminant.X/3, Illuminant.X/3); - VEC3init(&Scale.v[1], Illuminant.Y/3, Illuminant.Y/3, Illuminant.Y/3); - VEC3init(&Scale.v[2], Illuminant.Z/3, Illuminant.Z/3, Illuminant.Z/3); - - - MatShaper = cmsAllocMatShaper(&Scale, Shapes, MATSHAPER_INPUT); - cmsFreeGammaTriple(Shapes); - return MatShaper; - + _cmsAssert(CMMcargo != NULL); + if (FromInput) *FromInput = CMMcargo ->FromInput; + if (ToOutput) *ToOutput = CMMcargo ->ToOutput; } - -// Monochrome as output - -static -LPMATSHAPER cmsBuildGrayOutputMatrixShaper(cmsHPROFILE hProfile) +void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput) { - cmsCIEXYZ Illuminant; - LPGAMMATABLE GrayTRC, Shapes[3]; - LPMATSHAPER MatShaper; - MAT3 Scale; - - cmsTakeIluminant(&Illuminant, hProfile); - - // That is a special case for non-compliant profiles. - - if (cmsGetPCS(hProfile) == icSigLabData) { - - LPGAMMATABLE Shapes1[3]; - - GrayTRC = cmsReadICCGamma(hProfile, icSigGrayTRCTag); - if (!GrayTRC) { - return NULL; - } - FromLstarToXYZ(GrayTRC, Shapes1); - - // Reversing must be done after curve translation - - Shapes[0] = cmsReverseGamma(Shapes1[0]->nEntries, Shapes1[0]); - Shapes[1] = cmsReverseGamma(Shapes1[1]->nEntries, Shapes1[1]); - Shapes[2] = cmsReverseGamma(Shapes1[2]->nEntries, Shapes1[2]); - - cmsFreeGammaTriple(Shapes1); - - } - else { - - // Normal case - - GrayTRC = cmsReadICCGammaReversed(hProfile, icSigGrayTRCTag); // Y - if (!GrayTRC) { - return NULL; - } - Shapes[0] = cmsDupGamma(GrayTRC); - Shapes[1] = cmsDupGamma(GrayTRC); - Shapes[2] = cmsDupGamma(GrayTRC); - } - - if (!Shapes[0] || !Shapes[1] || !Shapes[2]) - return NULL; - - cmsFreeGamma(GrayTRC); - - VEC3init(&Scale.v[0], 0, 1.0/Illuminant.Y, 0); - VEC3init(&Scale.v[1], 0, 1.0/Illuminant.Y, 0); - VEC3init(&Scale.v[2], 0, 1.0/Illuminant.Y, 0); - - - MatShaper = cmsAllocMatShaper(&Scale, Shapes, MATSHAPER_OUTPUT); - cmsFreeGammaTriple(Shapes); - return MatShaper; - + _cmsAssert(CMMcargo != NULL); + if (FromInput) *FromInput = CMMcargo ->FromInputFloat; + if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat; } - -// Input matrix, only in XYZ - -LPMATSHAPER cmsBuildInputMatrixShaper(cmsHPROFILE InputProfile) +// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper +// for separated transforms. If this is the case, +static +_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, + cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) { - MAT3 DoubleMat; - LPGAMMATABLE Shapes[3]; - LPMATSHAPER InMatSh; - - // Check if this is a grayscale profile. If so, build - // appropiate conversion tables. The tables are the PCS - // iluminant, scaled across GrayTRC - - if (cmsGetColorSpace(InputProfile) == icSigGrayData) - { - return cmsBuildGrayInputMatrixShaper(InputProfile); - } - - if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat, InputProfile)) - return NULL; - - Shapes[0] = cmsReadICCGamma(InputProfile, icSigRedTRCTag); - Shapes[1] = cmsReadICCGamma(InputProfile, icSigGreenTRCTag); - Shapes[2] = cmsReadICCGamma(InputProfile, icSigBlueTRCTag); - - if (!Shapes[0] || !Shapes[1] || !Shapes[2]) - return NULL; - - InMatSh = cmsAllocMatShaper(&DoubleMat, Shapes, MATSHAPER_INPUT); - - cmsFreeGammaTriple(Shapes); - - return InMatSh; -} - - -// Output style matrix-shaper - - -LPMATSHAPER cmsBuildOutputMatrixShaper(cmsHPROFILE OutputProfile) -{ - MAT3 DoubleMat, DoubleInv; - LPGAMMATABLE InverseShapes[3]; - LPMATSHAPER OutMatSh; - - - - if (cmsGetColorSpace(OutputProfile) == icSigGrayData) - { - return cmsBuildGrayOutputMatrixShaper(OutputProfile); - } - - - if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat, OutputProfile)) - return NULL; - - if (MAT3inverse(&DoubleMat, &DoubleInv) < 0) - return NULL; - - - InverseShapes[0] = cmsReadICCGammaReversed(OutputProfile, icSigRedTRCTag); - InverseShapes[1] = cmsReadICCGammaReversed(OutputProfile, icSigGreenTRCTag); - InverseShapes[2] = cmsReadICCGammaReversed(OutputProfile, icSigBlueTRCTag); - - if (InverseShapes[0] == NULL || - InverseShapes[1] == NULL || - InverseShapes[2] == NULL) return NULL; - - OutMatSh = cmsAllocMatShaper(&DoubleInv, InverseShapes, MATSHAPER_OUTPUT); - - cmsFreeGammaTriple(InverseShapes); + _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin); + _cmsTransformCollection* Plugin; - return OutMatSh; -} - - - -// This function builds a transform matrix chaining parameters - -static -LCMSBOOL cmsBuildSmeltMatShaper(_LPcmsTRANSFORM p) -{ - MAT3 From, To, ToInv, Transfer; - LPGAMMATABLE In[3], InverseOut[3]; - - - if (!cmsReadICCMatrixRGB2XYZ(&From, p -> InputProfile)) - return FALSE; - - - if (!cmsReadICCMatrixRGB2XYZ(&To, p -> OutputProfile)) - return FALSE; - - // invert dest - - if (MAT3inverse(&To, &ToInv) < 0) - return FALSE; - - // Multiply - MAT3per(&Transfer, &ToInv, &From); - - - // Read gamma curves - - In[0] = cmsReadICCGamma(p -> InputProfile, icSigRedTRCTag); - In[1] = cmsReadICCGamma(p -> InputProfile, icSigGreenTRCTag); - In[2] = cmsReadICCGamma(p -> InputProfile, icSigBlueTRCTag); - - if (!In[0] || !In[1] || !In[2]) - return FALSE; - - - InverseOut[0] = cmsReadICCGammaReversed(p -> OutputProfile, icSigRedTRCTag); - InverseOut[1] = cmsReadICCGammaReversed(p -> OutputProfile, icSigGreenTRCTag); - InverseOut[2] = cmsReadICCGammaReversed(p -> OutputProfile, icSigBlueTRCTag); - - if (!InverseOut[0] || !InverseOut[1] || !InverseOut[2]) { - cmsFreeGammaTriple(In); - return FALSE; - } - - p -> SmeltMatShaper = cmsAllocMatShaper2(&Transfer, In, InverseOut, MATSHAPER_ALLSMELTED); - - cmsFreeGammaTriple(In); - cmsFreeGammaTriple(InverseOut); - - return (p -> SmeltMatShaper != NULL); -} - - - - -// Conversion between PCS ------------------------------------------ - -// Identifies intent archieved by LUT - -static -int GetPhase(cmsHPROFILE hProfile) -{ - switch (cmsGetPCS(hProfile)) { - - case icSigXYZData: return XYZRel; - - case icSigLabData: return LabRel; - - default: cmsSignalError(LCMS_ERRC_ABORTED, "Invalid PCS"); + // Allocate needed memory + _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); + if (!p) { + cmsPipelineFree(lut); + return NULL; } - return XYZRel; -} - - - + // Store the proposed pipeline + p->Lut = lut; -static -void TakeConversionRoutines(_LPcmsTRANSFORM p, int DoBPC) -{ - cmsCIEXYZ BlackPointIn, WhitePointIn, IlluminantIn; - cmsCIEXYZ BlackPointOut, WhitePointOut, IlluminantOut; - cmsCIEXYZ BlackPointProof, WhitePointProof, IlluminantProof; - MAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; - MAT3 ChromaticAdaptationMatrixProof; - - - cmsTakeIluminant(&IlluminantIn, p -> InputProfile); - cmsTakeMediaWhitePoint(&WhitePointIn, p -> InputProfile); - cmsTakeMediaBlackPoint(&BlackPointIn, p -> InputProfile); - cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixIn, p -> InputProfile); - - cmsTakeIluminant(&IlluminantOut, p -> OutputProfile); - cmsTakeMediaWhitePoint(&WhitePointOut, p -> OutputProfile); - cmsTakeMediaBlackPoint(&BlackPointOut, p -> OutputProfile); - cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixOut, p -> OutputProfile); - + // Let's see if any plug-in want to do the transform by itself + if (p->Lut != NULL) { - if (p -> Preview == NULL && p ->Gamut == NULL) // Non-proofing - { - if (p ->Intent == INTENT_PERCEPTUAL || - p ->Intent == INTENT_SATURATION) { - + for (Plugin = ctx->TransformCollection; + Plugin != NULL; + Plugin = Plugin->Next) { - // For v4 profiles, Perceptual PCS has a reference black point - // which v2 profiles should scale to. - - if ((cmsGetProfileICCversion(p ->InputProfile) >= 0x4000000) || - (cmsGetProfileICCversion(p ->OutputProfile) >= 0x4000000)) { + if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) { - DoBPC = TRUE; - } - } - - // Black point compensation does not apply to absolute intent - - if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) - DoBPC = FALSE; - - // Black point compensation does not apply to devicelink profiles - - if (cmsGetDeviceClass(p ->InputProfile) == icSigLinkClass) - DoBPC = FALSE; - - if (cmsGetDeviceClass(p ->OutputProfile) == icSigLinkClass) - DoBPC = FALSE; - - if (DoBPC) { + // Last plugin in the declaration order takes control. We just keep + // the original parameters as a logging. + // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default + // an optimized transform is not reusable. The plug-in can, however, change + // the flags and make it suitable. - // Detect Black points - - cmsDetectBlackPoint(&BlackPointIn, p->InputProfile, p->Intent, 0); - cmsDetectBlackPoint(&BlackPointOut, p->OutputProfile, p->Intent, 0); - - // If equal black points, then do nothing. This often applies to BP=0 - - if (BlackPointIn.X == BlackPointOut.X && - BlackPointIn.Y == BlackPointOut.Y && - BlackPointIn.Z == BlackPointOut.Z) - DoBPC = FALSE; - + p->ContextID = ContextID; + p->InputFormat = *InputFormat; + p->OutputFormat = *OutputFormat; + p->dwOriginalFlags = *dwFlags; - } - - cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC, - - p -> Phase1, - &BlackPointIn, - &WhitePointIn, - &IlluminantIn, - &ChromaticAdaptationMatrixIn, - - p -> Phase3, - &BlackPointOut, - &WhitePointOut, - &IlluminantOut, - &ChromaticAdaptationMatrixOut, + // Fill the formatters just in case the optimized routine is interested. + // No error is thrown if the formatter doesn't exist. It is up to the optimization + // factory to decide what to do in those cases. + p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; + p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; - DoBPC, - p ->AdaptationState, - &p->Stage1, - &p->m1, &p->of1); - - } - else // Proofing - { - - - cmsTakeIluminant(&IlluminantProof, p -> PreviewProfile); - cmsTakeMediaWhitePoint(&WhitePointProof, p -> PreviewProfile); - cmsTakeMediaBlackPoint(&BlackPointProof, p -> PreviewProfile); - cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixProof, p -> PreviewProfile); + // Save the day? (Ignore the warning) + if (Plugin->OldXform) { + p->OldXform = (_cmsTransformFn) p->xform; + p->xform = _cmsTransform2toTransformAdaptor; + } - if (DoBPC) { - - cmsDetectBlackPoint(&BlackPointProof, p->PreviewProfile, p->Intent, 0); - cmsDetectBlackPoint(&BlackPointIn, p->InputProfile, p->Intent, 0); - cmsDetectBlackPoint(&BlackPointOut, p->OutputProfile, p->Intent, 0); + return p; + } + } - // Reality check - - if (BlackPointIn.X == BlackPointProof.X && - BlackPointIn.Y == BlackPointProof.Y && - BlackPointIn.Z == BlackPointProof.Z) - DoBPC = FALSE; - - + // Not suitable for the transform plug-in, let's check the pipeline plug-in + _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags); } - - - cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC, - - p -> Phase1, - &BlackPointIn, - &WhitePointIn, - &IlluminantIn, - &ChromaticAdaptationMatrixIn, - - p -> Phase2, - &BlackPointProof, - &WhitePointProof, - &IlluminantProof, - &ChromaticAdaptationMatrixProof, - DoBPC, - p ->AdaptationState, - &p->Stage1, - &p->m1, &p->of1); - - cmsChooseCnvrt(p -> ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC, - - p -> Phase2, - &BlackPointProof, - &WhitePointProof, - &IlluminantProof, - &ChromaticAdaptationMatrixProof, + // Check whatever this is a true floating point transform + if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) { - p -> Phase3, - &BlackPointOut, - &WhitePointOut, - &IlluminantOut, - &ChromaticAdaptationMatrixOut, - 0, - 0.0, - &p->Stage2, - &p->m2, &p->of2); - } - -} - + // Get formatter function always return a valid union, but the contents of this union may be NULL. + p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; -// Check colorspace - -static -LCMSBOOL IsProperColorSpace(cmsHPROFILE hProfile, DWORD dwFormat, LCMSBOOL lUsePCS) -{ - int Space = T_COLORSPACE(dwFormat); - - if (Space == PT_ANY) return TRUE; - - if (lUsePCS) - return (Space == _cmsLCMScolorSpace(cmsGetPCS(hProfile))); - else - return (Space == _cmsLCMScolorSpace(cmsGetColorSpace(hProfile))); -} + if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { - -// Auxiliary: allocate transform struct and set to defaults - -static -_LPcmsTRANSFORM AllocEmptyTransform(void) -{ - // Allocate needed memory - - _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) _cmsMalloc(sizeof(_cmsTRANSFORM)); - if (!p) { - - cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: _cmsMalloc() failed"); - return NULL; - } - - ZeroMemory(p, sizeof(_cmsTRANSFORM)); - - // Initialize default methods + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + cmsDeleteTransform(p); + return NULL; + } - p -> xform = NULL; - p -> Intent = INTENT_PERCEPTUAL; - p -> ProofIntent = INTENT_ABSOLUTE_COLORIMETRIC; - p -> DoGamutCheck = FALSE; - p -> InputProfile = NULL; - p -> OutputProfile = NULL; - p -> PreviewProfile = NULL; - p -> Preview = NULL; - p -> Gamut = NULL; - p -> DeviceLink = NULL; - p -> InMatShaper = NULL; - p -> OutMatShaper = NULL; - p -> SmeltMatShaper = NULL; - p -> NamedColorList = NULL; - p -> EntryColorSpace = (icColorSpaceSignature) 0; - p -> ExitColorSpace = (icColorSpaceSignature) 0; - p -> AdaptationState = GlobalAdaptationState; + if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { - LCMS_CREATE_LOCK(&p->rwlock); - - return p; -} - - -// Identify whatever a transform is to be cached - -static -void SetPrecalculatedTransform(_LPcmsTRANSFORM p) -{ - if ((p->dwOriginalFlags & cmsFLAGS_GAMUTCHECK) && p ->GamutCheck != NULL) { - - p -> xform = PrecalculatedXFORMGamutCheck; - - if (!(p->dwOriginalFlags & cmsFLAGS_NOTCACHE)) { - - ZeroMemory(p ->CacheIn, sizeof(WORD) * MAXCHANNELS); - TransformOnePixelWithGamutCheck(p, p->CacheIn, p ->CacheOut); - p ->xform = CachedXFORMGamutCheck; + p ->xform = NullFloatXFORM; + } + else { + // Float transforms don't use caché, always are non-NULL + p ->xform = FloatXFORM; } } else { - p -> xform = PrecalculatedXFORM; + if (*InputFormat == 0 && *OutputFormat == 0) { + p ->FromInput = p ->ToOutput = NULL; + *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; + } + else { + + cmsUInt32Number BytesPerPixelInput; + + p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; - if (!(p->dwOriginalFlags & cmsFLAGS_NOTCACHE)) { + if (p ->FromInput == NULL || p ->ToOutput == NULL) { + + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + cmsDeleteTransform(p); + return NULL; + } + + BytesPerPixelInput = T_BYTES(p ->InputFormat); + if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) + *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; - ZeroMemory(p ->CacheIn, sizeof(WORD) * MAXCHANNELS); - cmsEvalLUT(p ->DeviceLink, p->CacheIn, p ->CacheOut); - p ->xform = CachedXFORM; + } + + if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { + + p ->xform = NullXFORM; + } + else { + if (*dwFlags & cmsFLAGS_NOCACHE) { + + if (*dwFlags & cmsFLAGS_GAMUTCHECK) + p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no caché + else + p ->xform = PrecalculatedXFORM; // No caché, no gamut check + } + else { + + if (*dwFlags & cmsFLAGS_GAMUTCHECK) + p ->xform = CachedXFORMGamutCheck; // Gamut check, caché + else + p ->xform = CachedXFORM; // No gamut check, caché + + } } } + + p ->InputFormat = *InputFormat; + p ->OutputFormat = *OutputFormat; + p ->dwOriginalFlags = *dwFlags; + p ->ContextID = ContextID; + p ->UserData = NULL; + return p; } - -// Transform is identified as device-link static -cmsHPROFILE CreateDeviceLinkTransform(_LPcmsTRANSFORM p) +cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) { + cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut; + cmsColorSpaceSignature PostColorSpace; + cmsUInt32Number i; - if (!IsProperColorSpace(p->InputProfile, p->InputFormat, FALSE)) { - cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on input"); - return NULL; + if (nProfiles == 0) return FALSE; + if (hProfiles[0] == NULL) return FALSE; + + *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); + + for (i=0; i < nProfiles; i++) { + + cmsProfileClassSignature cls; + cmsHPROFILE hProfile = hProfiles[i]; + + int lIsInput = (PostColorSpace != cmsSigXYZData) && + (PostColorSpace != cmsSigLabData); + + if (hProfile == NULL) return FALSE; + + cls = cmsGetDeviceClass(hProfile); + + if (cls == cmsSigNamedColorClass) { + + ColorSpaceIn = cmsSig1colorData; + ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile); + } + else + if (lIsInput || (cls == cmsSigLinkClass)) { + + ColorSpaceIn = cmsGetColorSpace(hProfile); + ColorSpaceOut = cmsGetPCS(hProfile); + } + else + { + ColorSpaceIn = cmsGetPCS(hProfile); + ColorSpaceOut = cmsGetColorSpace(hProfile); + } + + if (i==0) + *Input = ColorSpaceIn; + + PostColorSpace = ColorSpaceOut; } - if (!IsProperColorSpace(p->InputProfile, p->OutputFormat, TRUE)) { - cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on output"); + *Output = PostColorSpace; + + return TRUE; +} + +// Check colorspace +static +cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat) +{ + int Space1 = (int) T_COLORSPACE(dwFormat); + int Space2 = _cmsLCMScolorSpace(Check); + + if (Space1 == PT_ANY) return TRUE; + if (Space1 == Space2) return TRUE; + + if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE; + if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE; + + return FALSE; +} + +// ---------------------------------------------------------------------------------------------------------------- + +// Jun-21-2000: Some profiles (those that comes with W2K) comes +// with the media white (media black?) x 100. Add a sanity check + +static +void NormalizeXYZ(cmsCIEXYZ* Dest) +{ + while (Dest -> X > 2. && + Dest -> Y > 2. && + Dest -> Z > 2.) { + + Dest -> X /= 10.; + Dest -> Y /= 10.; + Dest -> Z /= 10.; + } +} + +static +void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src) +{ + if (src == NULL) { + wtPt ->X = cmsD50X; + wtPt ->Y = cmsD50Y; + wtPt ->Z = cmsD50Z; + } + else { + wtPt ->X = src->X; + wtPt ->Y = src->Y; + wtPt ->Z = src->Z; + + NormalizeXYZ(wtPt); + } + +} + +// New to lcms 2.0 -- have all parameters available. +cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, + cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsHPROFILE hGamutProfile, + cmsUInt32Number nGamutPCSposition, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number dwFlags) +{ + _cmsTRANSFORM* xform; + cmsColorSpaceSignature EntryColorSpace; + cmsColorSpaceSignature ExitColorSpace; + cmsPipeline* Lut; + cmsUInt32Number LastIntent = Intents[nProfiles-1]; + + // If it is a fake transform + if (dwFlags & cmsFLAGS_NULLTRANSFORM) + { + return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags); + } + + // If gamut check is requested, make sure we have a gamut profile + if (dwFlags & cmsFLAGS_GAMUTCHECK) { + if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; + } + + // On floating point transforms, inhibit cache + if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) + dwFlags |= cmsFLAGS_NOCACHE; + + // Mark entry/exit spaces + if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { + cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); return NULL; } - // Device link does only have AToB0Tag (ICC-Spec 1998/09) - - p->DeviceLink = cmsReadICCLut(p->InputProfile, icSigAToB0Tag); - - if (!p->DeviceLink) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Noncompliant device-link profile"); - cmsDeleteTransform((cmsHTRANSFORM) p); - return NULL; - } - - if (p ->PreviewProfile != NULL) { - cmsSignalError(LCMS_ERRC_WARNING, "Proofing not supported on device link transforms"); + // Check if proper colorspaces + if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform"); + return NULL; } - if (p ->OutputProfile != NULL) { - cmsSignalError(LCMS_ERRC_WARNING, "Output profile should be NULL, since this is a device-link transform"); - } - - p -> Phase1 = -1; - p -> Phase2 = -1; - p -> Phase3 = -1; - - SetPrecalculatedTransform(p); - - p -> EntryColorSpace = cmsGetColorSpace(p -> InputProfile); - p -> ExitColorSpace = cmsGetPCS(p -> InputProfile); - - if (p ->EntryColorSpace == icSigRgbData || - p ->EntryColorSpace == icSigCmyData) { - - p->DeviceLink -> CLut16params.Interp3D = cmsTetrahedralInterp16; + if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform"); + return NULL; } - // Precalculated device-link profile is ready - return (cmsHTRANSFORM) p; -} - - -// Transform that includes proofing -static -void CreateProof(_LPcmsTRANSFORM p, icTagSignature *ToTagPtr) - -{ - icTagSignature ProofTag; - - if (p -> dwOriginalFlags & cmsFLAGS_SOFTPROOFING) { - - // Apr-15, 2002 - Too much profiles does have bogus content - // on preview tag, so I do compute it by my own. - - p -> Preview = _cmsComputeSoftProofLUT(p ->PreviewProfile, p ->Intent); - p -> Phase2 = LabRel; - - // That's a proofing transfor, so use second intent for output. - - *ToTagPtr = PCS2Device[p->ProofIntent]; + // Create a pipeline with all transformations + Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (Lut == NULL) { + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles"); + return NULL; + } - if (p -> Preview == NULL) { - - ProofTag = Preview[p -> Intent]; - - if (!cmsIsTag(p ->PreviewProfile, ProofTag)) { - - ProofTag = Preview[0]; - if (!cmsIsTag(p ->PreviewProfile, ProofTag)) - ProofTag = (icTagSignature)0; - } - - if (ProofTag) { - - p -> Preview = cmsReadICCLut(p ->PreviewProfile, ProofTag); - p -> Phase2 = GetPhase(p ->PreviewProfile); - - } - else - { - p -> Preview = NULL; - p ->PreviewProfile = NULL; - cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not previewing capabilities"); - } - } - + // Check channel count + if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || + (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { + cmsPipelineFree(Lut); + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); + return NULL; } - // Aug-31, 2001 - Too much profiles does have bogus content - // on gamut tag, so I do compute it by my own. + // All seems ok + xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); + if (xform == NULL) { + return NULL; + } + + // Keep values + xform ->EntryColorSpace = EntryColorSpace; + xform ->ExitColorSpace = ExitColorSpace; + xform ->RenderingIntent = Intents[nProfiles-1]; - if ((p -> dwOriginalFlags & cmsFLAGS_GAMUTCHECK) && (p -> dwOriginalFlags & cmsFLAGS_NOTPRECALC)) { + // Take white points + SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag)); + SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag)); + + + // Create a gamut check LUT if requested + if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK)) + xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, + BPC, Intents, + AdaptationStates, + nGamutPCSposition, + hGamutProfile); - p -> Gamut = _cmsComputeGamutLUT(p->PreviewProfile, p ->Intent); - p -> Phase2 = LabRel; + // Try to read input and output colorant table + if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) { - if (p -> Gamut == NULL) { - - // Profile goes only in one direction... try to see - // if profile has the tag, and use it, no matter it - // could be bogus. This is the last chance! + // Input table can only come in this way. + xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag)); + } - if (cmsIsTag(p ->PreviewProfile, icSigGamutTag)) { + // Output is a little bit more complex. + if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) { - p -> Gamut = cmsReadICCLut(p ->PreviewProfile, icSigGamutTag); + // This tag may exist only on devicelink profiles. + if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) { - } - else { + // It may be NULL if error + xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)); + } - // Nope, cannot be done. + } else { - cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not gamut checking capabilities"); - p -> Gamut = NULL; - } - } + if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) { - } - -} - -// Choose the adequate transform routine + xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)); + } + } -static -_LPcmsTRANSFORM PickTransformRoutine(_LPcmsTRANSFORM p, - icTagSignature *FromTagPtr, - icTagSignature *ToTagPtr) -{ - + // Store the sequence of profiles + if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) { + xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles); + } + else + xform ->Sequence = NULL; - + // If this is a cached transform, init first value, which is zero (16 bits only) + if (!(dwFlags & cmsFLAGS_NOCACHE)) { - // Is a named color profile? - if (cmsGetDeviceClass(p->InputProfile) == icSigNamedColorClass) { + memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn)); - // Yes, and used as input - p ->FromDevice = NC2toPCS; + if (xform ->GamutCheck != NULL) { + TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut); } else { - // Can we optimize matrix-shaper only transform? - if ((*FromTagPtr == 0) && - (*ToTagPtr == 0) && - (!p->PreviewProfile) && - (p -> Intent != INTENT_ABSOLUTE_COLORIMETRIC) && - (p -> EntryColorSpace == icSigRgbData) && - (p -> ExitColorSpace == icSigRgbData) && - !(p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)) { - - // Yes... try to smelt matrix-shapers - p -> xform = MatrixShaperXFORM; - p -> dwOriginalFlags |= cmsFLAGS_NOTPRECALC; + xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data); + } - if (!cmsBuildSmeltMatShaper(p)) - { - cmsSignalError(LCMS_ERRC_ABORTED, "unable to smelt shaper-matrix, required tags missing"); - return NULL; - } - - p -> Phase1 = p -> Phase3 = XYZRel; - return p; - - } + } - // No, is a transform involving LUT - - if (*FromTagPtr != 0) { - - p -> FromDevice = LUTtoPCS; - p -> Device2PCS = cmsReadICCLut(p -> InputProfile, *FromTagPtr); - if (!p -> Device2PCS) { - - cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input"); - return NULL; - } + return (cmsHTRANSFORM) xform; +} - } - else - { - p -> FromDevice = ShaperMatrixToPCS; - p -> InMatShaper = cmsBuildInputMatrixShaper(p -> InputProfile); - - if (!p ->InMatShaper) { - cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input"); - return NULL; - } - - p -> Phase1 = XYZRel; - - } - } - - if (*ToTagPtr != 0) { +// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes. +cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsBool BPC[256]; + cmsUInt32Number Intents[256]; + cmsFloat64Number AdaptationStates[256]; - p -> ToDevice = PCStoLUT; - p -> PCS2Device = cmsReadICCLut(p -> OutputProfile, *ToTagPtr); - if (!p -> PCS2Device) { - cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output"); - return NULL; - } + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); + return NULL; + } - } - else - { - p -> ToDevice = PCStoShaperMatrix; - p -> OutMatShaper = cmsBuildOutputMatrixShaper(p->OutputProfile); - - if (!p -> OutMatShaper) { - cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output"); - return NULL; - } - p -> Phase3 = XYZRel; - - } + for (i=0; i < nProfiles; i++) { + BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; + Intents[i] = Intent; + AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1); + } - return p; + return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags); } - -// Create a transform. - -cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile, - DWORD InputFormat, - cmsHPROFILE OutputProfile, - DWORD OutputFormat, - cmsHPROFILE ProofingProfile, - int nIntent, - int ProofingIntent, - DWORD dwFlags) - +cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) { - _LPcmsTRANSFORM p; - icTagSignature FromTag; - icTagSignature ToTag; - - if (nIntent < 0 || nIntent > 3 || - ProofingIntent < 0 || ProofingIntent > 3) { - - cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: intent mismatch"); - return NULL; - } - - p = AllocEmptyTransform(); - if (p == NULL) return NULL; - - p -> xform = NormalXFORM; - p -> Intent = nIntent; - p -> ProofIntent = ProofingIntent; - p -> DoGamutCheck = FALSE; - p -> InputProfile = InputProfile; - p -> OutputProfile = OutputProfile; - p -> PreviewProfile = ProofingProfile; - p -> InputFormat = InputFormat; - p -> OutputFormat = OutputFormat; - p -> dwOriginalFlags = dwFlags; - - p -> lInputV4Lab = p ->lOutputV4Lab = FALSE; - - - p -> FromInput = _cmsIdentifyInputFormat(p, InputFormat); - p -> ToOutput = _cmsIdentifyOutputFormat(p, OutputFormat); - - // Null transform can be done without profiles - if ((p->dwOriginalFlags & cmsFLAGS_NULLTRANSFORM) || - ((InputProfile == NULL) && - (OutputProfile == NULL))) { - - p -> xform = NullXFORM; - return (cmsHTRANSFORM) p; - } - - // From here we need at least one input profile - if (InputProfile == NULL) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Input profile cannot be NULL!"); - cmsDeleteTransform((cmsHTRANSFORM) p); - return NULL; - } - - - // Device link are means to store precalculated transform grids. - if (cmsGetDeviceClass(InputProfile) == icSigLinkClass) { - return CreateDeviceLinkTransform(p); - } - - if (!IsProperColorSpace(InputProfile, InputFormat, FALSE)) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Input profile is operating on wrong colorspace"); - cmsDeleteTransform((cmsHTRANSFORM) p); - return NULL; - } - - p ->EntryColorSpace = cmsGetColorSpace(InputProfile); - - // Oct-21-2002: Added named color transforms - if (cmsGetDeviceClass(InputProfile) == icSigNamedColorClass) { - - if (p ->NamedColorList == NULL) - p ->NamedColorList = cmsAllocNamedColorList(0); - - cmsReadICCnamedColorList(p, InputProfile, icSigNamedColor2Tag); - - // Special case. If output profile == NULL, then the transform gives - // device values from named colors. - - if (OutputProfile == NULL) { - - p ->ExitColorSpace = p -> EntryColorSpace; - p ->xform = NC2deviceXform; - return (cmsHTRANSFORM) p; - } - - // Named color doesn't precalc anything - p -> dwOriginalFlags |= cmsFLAGS_NOTPRECALC; - } + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); + return NULL; + } - - // From here we need also output profile. - if (OutputProfile == NULL) { - cmsSignalError(LCMS_ERRC_ABORTED, "Output profile cannot be NULL!"); - cmsDeleteTransform((cmsHTRANSFORM) p); - return NULL; - } - - - if (!IsProperColorSpace(OutputProfile, OutputFormat, FALSE)) { - cmsSignalError(LCMS_ERRC_ABORTED, "Output profile is operating on wrong colorspace"); - cmsDeleteTransform((cmsHTRANSFORM) p); - return NULL; - } - - p -> ExitColorSpace = cmsGetColorSpace(OutputProfile); - - // Named color only on input - if (cmsGetDeviceClass(OutputProfile) == icSigNamedColorClass) { - - cmsSignalError(LCMS_ERRC_ABORTED, "Named color profiles are not supported as output"); - cmsDeleteTransform((cmsHTRANSFORM) p); - return NULL; - } - - p -> Phase1 = GetPhase(InputProfile); - p -> Phase2 = -1; - p -> Phase3 = GetPhase(OutputProfile); - - // Try to locate a LUT - - FromTag = Device2PCS[nIntent]; - ToTag = PCS2Device[nIntent]; + return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]), + hProfiles, + nProfiles, + InputFormat, + OutputFormat, + Intent, + dwFlags); +} - if (!cmsIsTag(InputProfile, FromTag)) { - - FromTag = Device2PCS[0]; - - if (!cmsIsTag(InputProfile, FromTag)) { - FromTag = (icTagSignature)0; - } - } - - // If proofing is needed, add required tags/parameters - if (ProofingProfile) - CreateProof(p, &ToTag); - - - if (!cmsIsTag(OutputProfile, ToTag)) { - - ToTag = PCS2Device[0]; - - // 12-Dec-2003, Abstract profiles can be placed as output and still using AToB0 - if (cmsGetDeviceClass(OutputProfile) == icSigAbstractClass) { - - if (!cmsIsTag(OutputProfile, ToTag)) { - ToTag = (icTagSignature) icSigAToB0Tag; - } - } - - if (!cmsIsTag(OutputProfile, ToTag)) - ToTag = (icTagSignature)0; - } - +cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ - if (p-> dwOriginalFlags & cmsFLAGS_MATRIXINPUT) - FromTag = (icTagSignature)0; - - if (p -> dwOriginalFlags & cmsFLAGS_MATRIXOUTPUT) - ToTag = (icTagSignature)0; - - - - if (PickTransformRoutine(p, &FromTag, &ToTag) == NULL) { - - cmsDeleteTransform((cmsHTRANSFORM) p); - return NULL; - - } - - TakeConversionRoutines(p, dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); - - if (!(p -> dwOriginalFlags & cmsFLAGS_NOTPRECALC)) { - - LPLUT DeviceLink; - LPLUT GamutCheck = NULL; - - - if (p ->EntryColorSpace == icSigCmykData && - p ->ExitColorSpace == icSigCmykData && - (dwFlags & cmsFLAGS_PRESERVEBLACK)) { - - DeviceLink = _cmsPrecalculateBlackPreservingDeviceLink((cmsHTRANSFORM) p, dwFlags); - - // Cannot be done at all? - if (DeviceLink == NULL) - DeviceLink = _cmsPrecalculateDeviceLink((cmsHTRANSFORM) p, dwFlags); - - } - else { + cmsHPROFILE hArray[2]; - DeviceLink = _cmsPrecalculateDeviceLink((cmsHTRANSFORM) p, dwFlags); - } - - // Allow to specify cmsFLAGS_GAMUTCHECK, even if no proofing profile is given - if ((p ->PreviewProfile != NULL) && (p -> dwOriginalFlags & cmsFLAGS_GAMUTCHECK)) { - - GamutCheck = _cmsPrecalculateGamutCheck((cmsHTRANSFORM) p); - } - - // If input colorspace is Rgb, Cmy, then use tetrahedral interpolation - // for speed reasons (it only works well on spaces on Luma is diagonal, and - // not if luma is in separate channel) - if (p ->EntryColorSpace == icSigRgbData || - p ->EntryColorSpace == icSigCmyData) { - + hArray[0] = Input; + hArray[1] = Output; - cmsCalcCLUT16ParamsEx(DeviceLink->CLut16params.nSamples, - DeviceLink->CLut16params.nInputs, - DeviceLink->CLut16params.nOutputs, - TRUE, &DeviceLink->CLut16params); - - } - - // If this is a 8-bit transform, optimize LUT further. - - if ((T_BYTES(InputFormat) == 1) && (T_CHANNELS(InputFormat) == 3)) { - - DeviceLink = _cmsBlessLUT8(DeviceLink); - if (DeviceLink == NULL) return NULL; - - } - + return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags); +} - p ->GamutCheck = GamutCheck; - - if (DeviceLink) { - - p ->DeviceLink = DeviceLink; - - if ((nIntent != INTENT_ABSOLUTE_COLORIMETRIC) && - !(p -> dwOriginalFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) - - _cmsFixWhiteMisalignment(p); - - } - else - { - - cmsSignalError(LCMS_ERRC_ABORTED, - "Cannot precalculate %d->%d channels transform!", - T_CHANNELS(InputFormat), T_CHANNELS(OutputFormat)); - - cmsDeleteTransform(p); - return NULL; - } - - - SetPrecalculatedTransform(p); - - - } - - // Re-Identify formats - p -> FromInput = _cmsIdentifyInputFormat(p, InputFormat); - p -> ToOutput = _cmsIdentifyOutputFormat(p, OutputFormat); - - - return p; +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags); } -// Wrapper por simpler non-proofing transforms. - -cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input, - DWORD InputFormat, - cmsHPROFILE Output, - DWORD OutputFormat, - int Intent, - DWORD dwFlags) - +cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, + cmsHPROFILE InputProfile, + cmsUInt32Number InputFormat, + cmsHPROFILE OutputProfile, + cmsUInt32Number OutputFormat, + cmsHPROFILE ProofingProfile, + cmsUInt32Number nIntent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags) { - return cmsCreateProofingTransform(Input, InputFormat, - Output, OutputFormat, - NULL, - Intent, INTENT_ABSOLUTE_COLORIMETRIC, - dwFlags); -} + cmsHPROFILE hArray[4]; + cmsUInt32Number Intents[4]; + cmsBool BPC[4]; + cmsFloat64Number Adaptation[4]; + cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE; -// Profiles are *NOT* closed + hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile; + Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent; + BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0; -void LCMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform) -{ - _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) (LPSTR) hTransform; + Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1); - if (p -> Device2PCS) - cmsFreeLUT(p -> Device2PCS); - if (p -> PCS2Device) - cmsFreeLUT(p -> PCS2Device); - if (p -> Gamut) - cmsFreeLUT(p -> Gamut); - if (p -> Preview) - cmsFreeLUT(p -> Preview); - if (p -> DeviceLink) - cmsFreeLUT(p -> DeviceLink); - if (p -> InMatShaper) - cmsFreeMatShaper(p -> InMatShaper); - if (p -> OutMatShaper) - cmsFreeMatShaper(p -> OutMatShaper); - if (p -> SmeltMatShaper) - cmsFreeMatShaper(p -> SmeltMatShaper); - if (p ->NamedColorList) - cmsFreeNamedColorList(p ->NamedColorList); - if (p -> GamutCheck) - cmsFreeLUT(p -> GamutCheck); + if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) + return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); - LCMS_FREE_LOCK(&p->rwlock); - - _cmsFree((void *) p); -} - - -// Apply transform code -void LCMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, - LPVOID InputBuffer, - LPVOID OutputBuffer, unsigned int Size) - -{ - - _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) (LPSTR) Transform; - - p -> StrideIn = p -> StrideOut = Size; - - p -> xform(p, InputBuffer, OutputBuffer, Size); + return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, + ProofingProfile, 1, InputFormat, OutputFormat, dwFlags); } -void LCMSEXPORT cmsSetAlarmCodes(int r, int g, int b) -{ - AlarmR = RGB_8_TO_16(r); - AlarmG = RGB_8_TO_16(g); - AlarmB = RGB_8_TO_16(b); -} - -void LCMSEXPORT cmsGetAlarmCodes(int *r, int *g, int *b) -{ - *r = RGB_16_TO_8(AlarmR); - *g = RGB_16_TO_8(AlarmG); - *b = RGB_16_TO_8(AlarmB); -} - -// Returns TRUE if the profile is implemented as matrix-shaper - -LCMSBOOL LCMSEXPORT _cmsIsMatrixShaper(cmsHPROFILE hProfile) -{ - switch (cmsGetColorSpace(hProfile)) { - - case icSigGrayData: - - return cmsIsTag(hProfile, icSigGrayTRCTag); - - case icSigRgbData: - - return (cmsIsTag(hProfile, icSigRedColorantTag) && - cmsIsTag(hProfile, icSigGreenColorantTag) && - cmsIsTag(hProfile, icSigBlueColorantTag) && - cmsIsTag(hProfile, icSigRedTRCTag) && - cmsIsTag(hProfile, icSigGreenTRCTag) && - cmsIsTag(hProfile, icSigBlueTRCTag)); - - default: - - return FALSE; - } -} - - -LCMSBOOL LCMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, - int Intent, int UsedDirection) +cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile, + cmsUInt32Number InputFormat, + cmsHPROFILE OutputProfile, + cmsUInt32Number OutputFormat, + cmsHPROFILE ProofingProfile, + cmsUInt32Number nIntent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags) { - - icTagSignature* TagTable; - - // Device link profiles only implements the intent in header - - if (cmsGetDeviceClass(hProfile) != icSigLinkClass) { - - switch (UsedDirection) { - - case LCMS_USED_AS_INPUT: TagTable = Device2PCS; break; - case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device; break; - case LCMS_USED_AS_PROOF: TagTable = Preview; break; - - default: - cmsSignalError(LCMS_ERRC_ABORTED, "Unexpected direction (%d)", UsedDirection); - return FALSE; - } - - if (cmsIsTag(hProfile, TagTable[Intent])) return TRUE; - return _cmsIsMatrixShaper(hProfile); - } - - return (cmsTakeRenderingIntent(hProfile) == Intent); -} - -// Multiple profile transform. -static -int MultiprofileSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) -{ - cmsHTRANSFORM* Transforms = (cmsHTRANSFORM*) Cargo; - int i; - - cmsDoTransform(Transforms[0], In, Out, 1); - - for (i=1; Transforms[i]; i++) - cmsDoTransform(Transforms[i], Out, Out, 1); - - - - return TRUE; -} - - -static -int IsAllowedInSingleXform(icProfileClassSignature aClass) -{ - return (aClass == icSigInputClass) || - (aClass == icSigDisplayClass) || - (aClass == icSigOutputClass) || - (aClass == icSigColorSpaceClass); + return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), + InputProfile, + InputFormat, + OutputProfile, + OutputFormat, + ProofingProfile, + nIntent, + ProofingIntent, + dwFlags); } -// A multiprofile transform does chain several profiles into a single -// devicelink. It couls also be used to merge named color profiles into -// a single database. - - -cmsHTRANSFORM LCMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], - int nProfiles, - DWORD dwInput, - DWORD dwOutput, - int Intent, - DWORD dwFlags) +// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed +cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform) { - cmsHTRANSFORM Transforms[257]; - DWORD dwPrecalcFlags = (dwFlags|cmsFLAGS_NOTPRECALC|cmsFLAGS_NOTCACHE); - DWORD FormatInput, FormatOutput; - cmsHPROFILE hLab, hXYZ, hProfile; - icColorSpaceSignature ColorSpace, CurrentColorSpace; - icColorSpaceSignature ColorSpaceIn, ColorSpaceOut; - LPLUT Grid; - int nGridPoints, ChannelsInput, ChannelsOutput = 3, i; - _LPcmsTRANSFORM p; - int nNamedColor; - - if (nProfiles > 255) { - cmsSignalError(LCMS_ERRC_ABORTED, "What are you trying to do with more that 255 profiles?!?, of course aborted"); - return NULL; - } + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; - // There is a simple case with just two profiles, try to catch it in order of getting - // black preservation to work on this function, at least with two profiles. - - - if (nProfiles == 2) { + if (xform == NULL) return NULL; + return xform -> ContextID; +} - icProfileClassSignature Class1 = cmsGetDeviceClass(hProfiles[0]); - icProfileClassSignature Class2 = cmsGetDeviceClass(hProfiles[1]); - - // Only input, output and display are allowed - - if (IsAllowedInSingleXform(Class1) && - IsAllowedInSingleXform(Class2)) - return cmsCreateTransform(hProfiles[0], dwInput, hProfiles[1], dwOutput, Intent, dwFlags); - } - +// Grab the input/output formats +cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; - // Creates a phantom transform for latter filling - p = (_LPcmsTRANSFORM) cmsCreateTransform(NULL, dwInput, - NULL, dwOutput, Intent, cmsFLAGS_NULLTRANSFORM); - - // If user wants null one, give it - if (dwFlags & cmsFLAGS_NULLTRANSFORM) return (cmsHPROFILE) p; + if (xform == NULL) return 0; + return xform->InputFormat; +} - // Is a bunch of named color profiles? - nNamedColor = 0; - for (i=0; i < nProfiles; i++) { - if (cmsGetDeviceClass(hProfiles[i]) == icSigNamedColorClass) - nNamedColor++; - } - - - if (nNamedColor == nProfiles) { - - // Yes, only named color. Create a named color-device - // and append to named color table - - cmsDeleteTransform((cmsHTRANSFORM) p); +cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; - p = (_LPcmsTRANSFORM) cmsCreateTransform(hProfiles[0], dwInput, NULL, dwOutput, Intent, dwFlags); - for (i=1; i < nProfiles; i++) { - cmsReadICCnamedColorList(p, hProfiles[i], icSigNamedColor2Tag); - } - - return p; // Ok, done so far - } - else - if (nNamedColor > 0) { - - cmsDeleteTransform((cmsHTRANSFORM) p); - cmsSignalError(LCMS_ERRC_ABORTED, "Could not mix named color profiles with other types in multiprofile transform"); - return NULL; - } + if (xform == NULL) return 0; + return xform->OutputFormat; +} - - // We will need a 3DCLUT for device link - Grid = cmsAllocLUT(); - if (!Grid) return NULL; - - // This one is our PCS (Always Lab) - hLab = cmsCreateLabProfile(NULL); - hXYZ = cmsCreateXYZProfile(); - - if (!hLab || !hXYZ) goto ErrorCleanup; - - // Take some info.... - - p ->EntryColorSpace = CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); +// For backwards compatibility +cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + cmsFormatter16 FromInput, ToOutput; - for (i=0; i < nProfiles; i++) { - - int lIsDeviceLink, lIsInput; - - // Check colorspace - - hProfile = hProfiles[i]; - lIsDeviceLink = (cmsGetDeviceClass(hProfile) == icSigLinkClass); - lIsInput = (CurrentColorSpace != icSigXYZData) && - (CurrentColorSpace != icSigLabData); - - if (lIsInput) { - - ColorSpaceIn = cmsGetColorSpace(hProfile); - ColorSpaceOut = cmsGetPCS(hProfile); - - } - else { - - ColorSpaceIn = cmsGetPCS(hProfile); - ColorSpaceOut = cmsGetColorSpace(hProfile); - } - - ChannelsInput = _cmsChannelsOf(ColorSpaceIn); - ChannelsOutput = _cmsChannelsOf(ColorSpaceOut); - - FormatInput = BYTES_SH(2)|CHANNELS_SH(ChannelsInput); - FormatOutput = BYTES_SH(2)|CHANNELS_SH(ChannelsOutput); - - ColorSpace = ColorSpaceIn; - - - if (ColorSpace == CurrentColorSpace) { - - if (lIsDeviceLink) { - - Transforms[i] = cmsCreateTransform(hProfile, FormatInput, - NULL, FormatOutput, - Intent, dwPrecalcFlags); - } - - else { + // We only can afford to change formatters if previous transform is at least 16 bits + if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) { - if (lIsInput) { - - Transforms[i] = cmsCreateTransform(hProfile, FormatInput, - (ColorSpaceOut == icSigLabData ? hLab : hXYZ), FormatOutput, - Intent, dwPrecalcFlags); - } - else { - Transforms[i] = cmsCreateTransform((ColorSpaceIn == icSigLabData ? hLab : hXYZ), FormatInput, - hProfile, FormatOutput, - Intent, dwPrecalcFlags); - - } - } - - - } - else // Can come from pcs? - if (CurrentColorSpace == icSigXYZData) { - - Transforms[i] = cmsCreateTransform(hXYZ, FormatInput, - hProfile, FormatOutput, - Intent, dwPrecalcFlags); - - } - else - if (CurrentColorSpace == icSigLabData) { - - Transforms[i] = cmsCreateTransform(hLab, FormatInput, - hProfile, FormatOutput, - Intent, dwPrecalcFlags); - - } - else { - cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateMultiprofileTransform: ColorSpace mismatch"); - goto ErrorCleanup; - } - - if (Transforms[i] == NULL) { - cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateMultiprofileTransform: unable to create transform"); - goto ErrorCleanup; - } - CurrentColorSpace = ColorSpaceOut; - + cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision"); + return FALSE; } - p ->ExitColorSpace = CurrentColorSpace; - Transforms[i] = NULL; // End marker - - p ->InputProfile = hProfiles[0]; - p ->OutputProfile = hProfiles[nProfiles - 1]; - - nGridPoints = _cmsReasonableGridpointsByColorspace(p ->EntryColorSpace, dwFlags); - - ChannelsInput = _cmsChannelsOf(cmsGetColorSpace(p ->InputProfile)); + FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; - Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsInput, ChannelsOutput); - - if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) - _cmsComputePrelinearizationTablesFromXFORM(Transforms, nProfiles, Grid); + if (FromInput == NULL || ToOutput == NULL) { - // Compute device link on 16-bit basis - if (!cmsSample3DGrid(Grid, MultiprofileSampler, (LPVOID) Transforms, Grid -> wFlags)) { - - cmsFreeLUT(Grid); - goto ErrorCleanup; + cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + return FALSE; } - // All ok, store the newly created LUT - p -> DeviceLink = Grid; - - SetPrecalculatedTransform(p); - - for (i=nProfiles-1; i >= 0; --i) - cmsDeleteTransform(Transforms[i]); - - - if (hLab) cmsCloseProfile(hLab); - if (hXYZ) cmsCloseProfile(hXYZ); - - - if (p ->EntryColorSpace == icSigRgbData || - p ->EntryColorSpace == icSigCmyData) { - - p->DeviceLink -> CLut16params.Interp3D = cmsTetrahedralInterp16; - } - - - if ((Intent != INTENT_ABSOLUTE_COLORIMETRIC) && - !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) - _cmsFixWhiteMisalignment(p); - - return (cmsHTRANSFORM) p; - - -ErrorCleanup: - - if (hLab) cmsCloseProfile(hLab); - if (hXYZ) cmsCloseProfile(hXYZ); - return NULL; + xform ->InputFormat = InputFormat; + xform ->OutputFormat = OutputFormat; + xform ->FromInput = FromInput; + xform ->ToOutput = ToOutput; + return TRUE; } - - - -double LCMSEXPORT cmsSetAdaptationState(double d) -{ - double OldVal = GlobalAdaptationState; - - if (d >= 0) - GlobalAdaptationState = d; - - return OldVal; - -} diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/icc34.h --- a/src/share/native/sun/java2d/cmm/lcms/icc34.h Wed Jan 31 22:55:12 2018 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1023 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* Header file guard bands */ -#ifndef ICC_H -#define ICC_H - -/* - * This version of the header file corresponds to the profile - * specification version 3.4. - * - * All header file entries are pre-fixed with "ic" to help - * avoid name space collisions. Signatures are pre-fixed with - * icSig. - * - * The structures defined in this header file were created to - * represent a description of an ICC profile on disk. Rather - * than use pointers a technique is used where a single byte array - * was placed at the end of each structure. This allows us in "C" - * to extend the structure by allocating more data than is needed - * to account for variable length structures. - * - * This also ensures that data following is allocated - * contiguously and makes it easier to write and read data from - * the file. - * - * For example to allocate space for a 256 count length UCR - * and BG array, and fill the allocated data. Note strlen + 1 - * to remember NULL terminator. - * - icUcrBgCurve *ucrCurve, *bgCurve; - int ucr_nbytes, bg_nbytes, string_bytes; - icUcrBg *ucrBgWrite; - char ucr_string[100], *ucr_char; - - strcpy(ucr_string, "Example ucrBG curves"); - ucr_nbytes = sizeof(icUInt32Number) + - (UCR_CURVE_SIZE * sizeof(icUInt16Number)); - bg_nbytes = sizeof(icUInt32Number) + - (BG_CURVE_SIZE * sizeof(icUInt16Number)); - string_bytes = strlen(ucr_string) + 1; - - ucrBgWrite = (icUcrBg *)malloc( - (ucr_nbytes + bg_nbytes + string_bytes)); - - ucrCurve = (icUcrBgCurve *)ucrBgWrite->data; - ucrCurve->count = UCR_CURVE_SIZE; - for (i=0; icount; i++) - ucrCurve->curve[i] = (icUInt16Number)i; - - bgCurve = (icUcrBgCurve *)((char *)ucrCurve + ucr_nbytes); - bgCurve->count = BG_CURVE_SIZE; - for (i=0; icount; i++) - bgCurve->curve[i] = 255 - (icUInt16Number)i; - - ucr_char = (char *)((char *)bgCurve + bg_nbytes); - memcpy(ucr_char, ucr_string, string_bytes); - * - */ - -/* - * Many of the structures contain variable length arrays. This - * is represented by the use of the convention. - * - * type data[icAny]; - */ - -/*------------------------------------------------------------------------*/ -/* - * Defines used in the specification - */ -#define icMagicNumber 0x61637370L /* 'acsp' */ -#define icVersionNumber 0x02100000L /* 2.1.0, BCD */ - -/* Screening Encodings */ -#define icPrtrDefaultScreensFalse 0x00000000L /* Bit pos 0 */ -#define icPrtrDefaultScreensTrue 0x00000001L /* Bit pos 0 */ -#define icLinesPerInch 0x00000002L /* Bit pos 1 */ -#define icLinesPerCm 0x00000000L /* Bit pos 1 */ - -/* - * Device attributes, currently defined values correspond - * to the low 4 bytes of the 8 byte attribute quantity, see - * the header for their location. - */ -#define icReflective 0x00000000L /* Bit pos 0 */ -#define icTransparency 0x00000001L /* Bit pos 0 */ -#define icGlossy 0x00000000L /* Bit pos 1 */ -#define icMatte 0x00000002L /* Bit pos 1 */ - -/* - * Profile header flags, the low 16 bits are reserved for consortium - * use. - */ -#define icEmbeddedProfileFalse 0x00000000L /* Bit pos 0 */ -#define icEmbeddedProfileTrue 0x00000001L /* Bit pos 0 */ -#define icUseAnywhere 0x00000000L /* Bit pos 1 */ -#define icUseWithEmbeddedDataOnly 0x00000002L /* Bit pos 1 */ - -/* Ascii or Binary data */ -#define icAsciiData 0x00000000L -#define icBinaryData 0x00000001L - -/* - * Define used to indicate that this is a variable length array - */ -#define icAny 1 - - -/*------------------------------------------------------------------------*/ -/* - * Use this area to translate platform definitions of long - * etc into icXXX form. The rest of the header uses the icXXX - * typedefs. Signatures are 4 byte quantities. - * - */ - - -#ifdef PACKAGE_NAME -/* - June 9, 2003, Adapted for use with configure by Bob Friesenhahn - Added the stupid check for autoconf by Marti Maria. - PACKAGE_NAME is defined if autoconf is being used -*/ - -typedef @UINT8_T@ icUInt8Number; -typedef @UINT16_T@ icUInt16Number; -typedef @UINT32_T@ icUInt32Number; -typedef @UINT32_T@ icUInt64Number[2]; - -typedef @INT8_T@ icInt8Number; -typedef @INT16_T@ icInt16Number; -typedef @INT32_T@ icInt32Number; -typedef @INT32_T@ icInt64Number[2]; - -#else - -/* - *Apr-17-2002: Modified by Marti Maria in order to provide wider portability. - */ - -#if defined (__digital__) && defined (__unix__) - -/* Tru64 */ - -#include - -typedef uint8_t icUInt8Number; -typedef uint16_t icUInt16Number; -typedef uint32_t icUInt32Number; -typedef uint32_t icUInt64Number[2]; - -typedef int8_t icInt8Number; -typedef int16_t icInt16Number; -typedef int32_t icInt32Number; -typedef int32_t icInt64Number[2]; - -#else -#ifdef __sgi -#include "sgidefs.h" - - -/* - * Number definitions - */ - -/* Unsigned integer numbers */ -typedef unsigned char icUInt8Number; -typedef unsigned short icUInt16Number; -typedef __uint32_t icUInt32Number; -typedef __uint32_t icUInt64Number[2]; - -/* Signed numbers */ -typedef char icInt8Number; -typedef short icInt16Number; -typedef __int32_t icInt32Number; -typedef __int32_t icInt64Number[2]; - - -#else -#if defined(__GNUC__) || defined(__unix__) || defined(__unix) - -#include - -#if defined(__sun) || defined(__hpux) || defined (__MINGW) || defined(__MINGW32__) - -#if defined (__MINGW) || defined(__MINGW32__) -#include -#endif - - -typedef uint8_t icUInt8Number; -typedef uint16_t icUInt16Number; -typedef uint32_t icUInt32Number; -typedef uint32_t icUInt64Number[2]; - -#else - -/* Unsigned integer numbers */ -typedef u_int8_t icUInt8Number; -typedef u_int16_t icUInt16Number; -typedef u_int32_t icUInt32Number; -typedef u_int32_t icUInt64Number[2]; - -#endif - - -/* Signed numbers */ -typedef int8_t icInt8Number; -typedef int16_t icInt16Number; -typedef int32_t icInt32Number; -typedef int32_t icInt64Number[2]; - - -#else /* default definitions */ - -/* - * Number definitions - */ - -/* Unsigned integer numbers */ -typedef unsigned char icUInt8Number; -typedef unsigned short icUInt16Number; -typedef unsigned long icUInt32Number; -typedef unsigned long icUInt64Number[2]; - -/* Signed numbers */ -typedef char icInt8Number; -typedef short icInt16Number; -typedef long icInt32Number; -typedef long icInt64Number[2]; - - -#endif /* default defs */ -#endif -#endif -#endif - -/* Base types */ - -typedef icInt32Number icSignature; -typedef icInt32Number icS15Fixed16Number; -typedef icUInt32Number icU16Fixed16Number; - - -/*------------------------------------------------------------------------*/ -/* public tags and sizes */ -typedef enum { - icSigAToB0Tag = 0x41324230L, /* 'A2B0' */ - icSigAToB1Tag = 0x41324231L, /* 'A2B1' */ - icSigAToB2Tag = 0x41324232L, /* 'A2B2' */ - icSigBlueColorantTag = 0x6258595AL, /* 'bXYZ' */ - icSigBlueTRCTag = 0x62545243L, /* 'bTRC' */ - icSigBToA0Tag = 0x42324130L, /* 'B2A0' */ - icSigBToA1Tag = 0x42324131L, /* 'B2A1' */ - icSigBToA2Tag = 0x42324132L, /* 'B2A2' */ - icSigCalibrationDateTimeTag = 0x63616C74L, /* 'calt' */ - icSigCharTargetTag = 0x74617267L, /* 'targ' */ - icSigCopyrightTag = 0x63707274L, /* 'cprt' */ - icSigCrdInfoTag = 0x63726469L, /* 'crdi' */ - icSigDeviceMfgDescTag = 0x646D6E64L, /* 'dmnd' */ - icSigDeviceModelDescTag = 0x646D6464L, /* 'dmdd' */ - icSigGamutTag = 0x67616D74L, /* 'gamt ' */ - icSigGrayTRCTag = 0x6b545243L, /* 'kTRC' */ - icSigGreenColorantTag = 0x6758595AL, /* 'gXYZ' */ - icSigGreenTRCTag = 0x67545243L, /* 'gTRC' */ - icSigLuminanceTag = 0x6C756d69L, /* 'lumi' */ - icSigMeasurementTag = 0x6D656173L, /* 'meas' */ - icSigMediaBlackPointTag = 0x626B7074L, /* 'bkpt' */ - icSigMediaWhitePointTag = 0x77747074L, /* 'wtpt' */ - icSigNamedColorTag = 0x6E636f6CL, /* 'ncol' - * OBSOLETE, use ncl2 */ - icSigNamedColor2Tag = 0x6E636C32L, /* 'ncl2' */ - icSigPreview0Tag = 0x70726530L, /* 'pre0' */ - icSigPreview1Tag = 0x70726531L, /* 'pre1' */ - icSigPreview2Tag = 0x70726532L, /* 'pre2' */ - icSigProfileDescriptionTag = 0x64657363L, /* 'desc' */ - icSigProfileSequenceDescTag = 0x70736571L, /* 'pseq' */ - icSigPs2CRD0Tag = 0x70736430L, /* 'psd0' */ - icSigPs2CRD1Tag = 0x70736431L, /* 'psd1' */ - icSigPs2CRD2Tag = 0x70736432L, /* 'psd2' */ - icSigPs2CRD3Tag = 0x70736433L, /* 'psd3' */ - icSigPs2CSATag = 0x70733273L, /* 'ps2s' */ - icSigPs2RenderingIntentTag = 0x70733269L, /* 'ps2i' */ - icSigRedColorantTag = 0x7258595AL, /* 'rXYZ' */ - icSigRedTRCTag = 0x72545243L, /* 'rTRC' */ - icSigScreeningDescTag = 0x73637264L, /* 'scrd' */ - icSigScreeningTag = 0x7363726EL, /* 'scrn' */ - icSigTechnologyTag = 0x74656368L, /* 'tech' */ - icSigUcrBgTag = 0x62666420L, /* 'bfd ' */ - icSigViewingCondDescTag = 0x76756564L, /* 'vued' */ - icSigViewingConditionsTag = 0x76696577L, /* 'view' */ - icMaxEnumTag = 0xFFFFFFFFL -} icTagSignature; - -/* technology signature descriptions */ -typedef enum { - icSigDigitalCamera = 0x6463616DL, /* 'dcam' */ - icSigFilmScanner = 0x6673636EL, /* 'fscn' */ - icSigReflectiveScanner = 0x7273636EL, /* 'rscn' */ - icSigInkJetPrinter = 0x696A6574L, /* 'ijet' */ - icSigThermalWaxPrinter = 0x74776178L, /* 'twax' */ - icSigElectrophotographicPrinter = 0x6570686FL, /* 'epho' */ - icSigElectrostaticPrinter = 0x65737461L, /* 'esta' */ - icSigDyeSublimationPrinter = 0x64737562L, /* 'dsub' */ - icSigPhotographicPaperPrinter = 0x7270686FL, /* 'rpho' */ - icSigFilmWriter = 0x6670726EL, /* 'fprn' */ - icSigVideoMonitor = 0x7669646DL, /* 'vidm' */ - icSigVideoCamera = 0x76696463L, /* 'vidc' */ - icSigProjectionTelevision = 0x706A7476L, /* 'pjtv' */ - icSigCRTDisplay = 0x43525420L, /* 'CRT ' */ - icSigPMDisplay = 0x504D4420L, /* 'PMD ' */ - icSigAMDisplay = 0x414D4420L, /* 'AMD ' */ - icSigPhotoCD = 0x4B504344L, /* 'KPCD' */ - icSigPhotoImageSetter = 0x696D6773L, /* 'imgs' */ - icSigGravure = 0x67726176L, /* 'grav' */ - icSigOffsetLithography = 0x6F666673L, /* 'offs' */ - icSigSilkscreen = 0x73696C6BL, /* 'silk' */ - icSigFlexography = 0x666C6578L, /* 'flex' */ - icMaxEnumTechnology = 0xFFFFFFFFL -} icTechnologySignature; - -/* type signatures */ -typedef enum { - icSigCurveType = 0x63757276L, /* 'curv' */ - icSigDataType = 0x64617461L, /* 'data' */ - icSigDateTimeType = 0x6474696DL, /* 'dtim' */ - icSigLut16Type = 0x6d667432L, /* 'mft2' */ - icSigLut8Type = 0x6d667431L, /* 'mft1' */ - icSigMeasurementType = 0x6D656173L, /* 'meas' */ - icSigNamedColorType = 0x6E636f6CL, /* 'ncol' - * OBSOLETE, use ncl2 */ - icSigProfileSequenceDescType = 0x70736571L, /* 'pseq' */ - icSigS15Fixed16ArrayType = 0x73663332L, /* 'sf32' */ - icSigScreeningType = 0x7363726EL, /* 'scrn' */ - icSigSignatureType = 0x73696720L, /* 'sig ' */ - icSigTextType = 0x74657874L, /* 'text' */ - icSigTextDescriptionType = 0x64657363L, /* 'desc' */ - icSigU16Fixed16ArrayType = 0x75663332L, /* 'uf32' */ - icSigUcrBgType = 0x62666420L, /* 'bfd ' */ - icSigUInt16ArrayType = 0x75693136L, /* 'ui16' */ - icSigUInt32ArrayType = 0x75693332L, /* 'ui32' */ - icSigUInt64ArrayType = 0x75693634L, /* 'ui64' */ - icSigUInt8ArrayType = 0x75693038L, /* 'ui08' */ - icSigViewingConditionsType = 0x76696577L, /* 'view' */ - icSigXYZType = 0x58595A20L, /* 'XYZ ' */ - icSigXYZArrayType = 0x58595A20L, /* 'XYZ ' */ - icSigNamedColor2Type = 0x6E636C32L, /* 'ncl2' */ - icSigCrdInfoType = 0x63726469L, /* 'crdi' */ - icMaxEnumType = 0xFFFFFFFFL -} icTagTypeSignature; - -/* - * Color Space Signatures - * Note that only icSigXYZData and icSigLabData are valid - * Profile Connection Spaces (PCSs) - */ -typedef enum { - icSigXYZData = 0x58595A20L, /* 'XYZ ' */ - icSigLabData = 0x4C616220L, /* 'Lab ' */ - icSigLuvData = 0x4C757620L, /* 'Luv ' */ - icSigYCbCrData = 0x59436272L, /* 'YCbr' */ - icSigYxyData = 0x59787920L, /* 'Yxy ' */ - icSigRgbData = 0x52474220L, /* 'RGB ' */ - icSigGrayData = 0x47524159L, /* 'GRAY' */ - icSigHsvData = 0x48535620L, /* 'HSV ' */ - icSigHlsData = 0x484C5320L, /* 'HLS ' */ - icSigCmykData = 0x434D594BL, /* 'CMYK' */ - icSigCmyData = 0x434D5920L, /* 'CMY ' */ - icSig2colorData = 0x32434C52L, /* '2CLR' */ - icSig3colorData = 0x33434C52L, /* '3CLR' */ - icSig4colorData = 0x34434C52L, /* '4CLR' */ - icSig5colorData = 0x35434C52L, /* '5CLR' */ - icSig6colorData = 0x36434C52L, /* '6CLR' */ - icSig7colorData = 0x37434C52L, /* '7CLR' */ - icSig8colorData = 0x38434C52L, /* '8CLR' */ - icSig9colorData = 0x39434C52L, /* '9CLR' */ - icSig10colorData = 0x41434C52L, /* 'ACLR' */ - icSig11colorData = 0x42434C52L, /* 'BCLR' */ - icSig12colorData = 0x43434C52L, /* 'CCLR' */ - icSig13colorData = 0x44434C52L, /* 'DCLR' */ - icSig14colorData = 0x45434C52L, /* 'ECLR' */ - icSig15colorData = 0x46434C52L, /* 'FCLR' */ - icMaxEnumData = 0xFFFFFFFFL -} icColorSpaceSignature; - -/* profileClass enumerations */ -typedef enum { - icSigInputClass = 0x73636E72L, /* 'scnr' */ - icSigDisplayClass = 0x6D6E7472L, /* 'mntr' */ - icSigOutputClass = 0x70727472L, /* 'prtr' */ - icSigLinkClass = 0x6C696E6BL, /* 'link' */ - icSigAbstractClass = 0x61627374L, /* 'abst' */ - icSigColorSpaceClass = 0x73706163L, /* 'spac' */ - icSigNamedColorClass = 0x6e6d636cL, /* 'nmcl' */ - icMaxEnumClass = 0xFFFFFFFFL -} icProfileClassSignature; - -/* Platform Signatures */ -typedef enum { - icSigMacintosh = 0x4150504CL, /* 'APPL' */ - icSigMicrosoft = 0x4D534654L, /* 'MSFT' */ - icSigSolaris = 0x53554E57L, /* 'SUNW' */ - icSigSGI = 0x53474920L, /* 'SGI ' */ - icSigTaligent = 0x54474E54L, /* 'TGNT' */ - icMaxEnumPlatform = 0xFFFFFFFFL -} icPlatformSignature; - -/*------------------------------------------------------------------------*/ -/* - * Other enums - */ - -/* Measurement Flare, used in the measurmentType tag */ -typedef enum { - icFlare0 = 0x00000000L, /* 0% flare */ - icFlare100 = 0x00000001L, /* 100% flare */ - icMaxFlare = 0xFFFFFFFFL -} icMeasurementFlare; - -/* Measurement Geometry, used in the measurmentType tag */ -typedef enum { - icGeometryUnknown = 0x00000000L, /* Unknown */ - icGeometry045or450 = 0x00000001L, /* 0/45, 45/0 */ - icGeometry0dord0 = 0x00000002L, /* 0/d or d/0 */ - icMaxGeometry = 0xFFFFFFFFL -} icMeasurementGeometry; - -/* Rendering Intents, used in the profile header */ -typedef enum { - icPerceptual = 0, - icRelativeColorimetric = 1, - icSaturation = 2, - icAbsoluteColorimetric = 3, - icMaxEnumIntent = 0xFFFFFFFFL -} icRenderingIntent; - -/* Different Spot Shapes currently defined, used for screeningType */ -typedef enum { - icSpotShapeUnknown = 0, - icSpotShapePrinterDefault = 1, - icSpotShapeRound = 2, - icSpotShapeDiamond = 3, - icSpotShapeEllipse = 4, - icSpotShapeLine = 5, - icSpotShapeSquare = 6, - icSpotShapeCross = 7, - icMaxEnumSpot = 0xFFFFFFFFL -} icSpotShape; - -/* Standard Observer, used in the measurmentType tag */ -typedef enum { - icStdObsUnknown = 0x00000000L, /* Unknown */ - icStdObs1931TwoDegrees = 0x00000001L, /* 2 deg */ - icStdObs1964TenDegrees = 0x00000002L, /* 10 deg */ - icMaxStdObs = 0xFFFFFFFFL -} icStandardObserver; - -/* Pre-defined illuminants, used in measurement and viewing conditions type */ -typedef enum { - icIlluminantUnknown = 0x00000000L, - icIlluminantD50 = 0x00000001L, - icIlluminantD65 = 0x00000002L, - icIlluminantD93 = 0x00000003L, - icIlluminantF2 = 0x00000004L, - icIlluminantD55 = 0x00000005L, - icIlluminantA = 0x00000006L, - icIlluminantEquiPowerE = 0x00000007L, - icIlluminantF8 = 0x00000008L, - icMaxEnumIluminant = 0xFFFFFFFFL -} icIlluminant; - - -/*------------------------------------------------------------------------*/ -/* - * Arrays of numbers - */ - -/* Int8 Array */ -typedef struct { - icInt8Number data[icAny]; /* Variable array of values */ -} icInt8Array; - -/* UInt8 Array */ -typedef struct { - icUInt8Number data[icAny]; /* Variable array of values */ -} icUInt8Array; - -/* uInt16 Array */ -typedef struct { - icUInt16Number data[icAny]; /* Variable array of values */ -} icUInt16Array; - -/* Int16 Array */ -typedef struct { - icInt16Number data[icAny]; /* Variable array of values */ -} icInt16Array; - -/* uInt32 Array */ -typedef struct { - icUInt32Number data[icAny]; /* Variable array of values */ -} icUInt32Array; - -/* Int32 Array */ -typedef struct { - icInt32Number data[icAny]; /* Variable array of values */ -} icInt32Array; - -/* UInt64 Array */ -typedef struct { - icUInt64Number data[icAny]; /* Variable array of values */ -} icUInt64Array; - -/* Int64 Array */ -typedef struct { - icInt64Number data[icAny]; /* Variable array of values */ -} icInt64Array; - -/* u16Fixed16 Array */ -typedef struct { - icU16Fixed16Number data[icAny]; /* Variable array of values */ -} icU16Fixed16Array; - -/* s15Fixed16 Array */ -typedef struct { - icS15Fixed16Number data[icAny]; /* Variable array of values */ -} icS15Fixed16Array; - -/* The base date time number */ -typedef struct { - icUInt16Number year; - icUInt16Number month; - icUInt16Number day; - icUInt16Number hours; - icUInt16Number minutes; - icUInt16Number seconds; -} icDateTimeNumber; - -/* XYZ Number */ -typedef struct { - icS15Fixed16Number X; - icS15Fixed16Number Y; - icS15Fixed16Number Z; -} icXYZNumber; - -/* XYZ Array */ -typedef struct { - icXYZNumber data[icAny]; /* Variable array of XYZ numbers */ -} icXYZArray; - -/* Curve */ -typedef struct { - icUInt32Number count; /* Number of entries */ - icUInt16Number data[icAny]; /* The actual table data, real - * number is determined by count - * Interpretation depends on how - * data is used with a given tag - */ -} icCurve; - -/* Data */ -typedef struct { - icUInt32Number dataFlag; /* 0 = ascii, 1 = binary */ - icInt8Number data[icAny]; /* Data, size from tag */ -} icData; - -/* lut16 */ -typedef struct { - icUInt8Number inputChan; /* Number of input channels */ - icUInt8Number outputChan; /* Number of output channels */ - icUInt8Number clutPoints; /* Number of grid points */ - icInt8Number pad; /* Padding for byte alignment */ - icS15Fixed16Number e00; /* e00 in the 3 * 3 */ - icS15Fixed16Number e01; /* e01 in the 3 * 3 */ - icS15Fixed16Number e02; /* e02 in the 3 * 3 */ - icS15Fixed16Number e10; /* e10 in the 3 * 3 */ - icS15Fixed16Number e11; /* e11 in the 3 * 3 */ - icS15Fixed16Number e12; /* e12 in the 3 * 3 */ - icS15Fixed16Number e20; /* e20 in the 3 * 3 */ - icS15Fixed16Number e21; /* e21 in the 3 * 3 */ - icS15Fixed16Number e22; /* e22 in the 3 * 3 */ - icUInt16Number inputEnt; /* Num of in-table entries */ - icUInt16Number outputEnt; /* Num of out-table entries */ - icUInt16Number data[icAny]; /* Data follows see spec */ -/* - * Data that follows is of this form - * - * icUInt16Number inputTable[inputChan][icAny]; * The in-table - * icUInt16Number clutTable[icAny]; * The clut - * icUInt16Number outputTable[outputChan][icAny]; * The out-table - */ -} icLut16; - -/* lut8, input & output tables are always 256 bytes in length */ -typedef struct { - icUInt8Number inputChan; /* Num of input channels */ - icUInt8Number outputChan; /* Num of output channels */ - icUInt8Number clutPoints; /* Num of grid points */ - icInt8Number pad; - icS15Fixed16Number e00; /* e00 in the 3 * 3 */ - icS15Fixed16Number e01; /* e01 in the 3 * 3 */ - icS15Fixed16Number e02; /* e02 in the 3 * 3 */ - icS15Fixed16Number e10; /* e10 in the 3 * 3 */ - icS15Fixed16Number e11; /* e11 in the 3 * 3 */ - icS15Fixed16Number e12; /* e12 in the 3 * 3 */ - icS15Fixed16Number e20; /* e20 in the 3 * 3 */ - icS15Fixed16Number e21; /* e21 in the 3 * 3 */ - icS15Fixed16Number e22; /* e22 in the 3 * 3 */ - icUInt8Number data[icAny]; /* Data follows see spec */ -/* - * Data that follows is of this form - * - * icUInt8Number inputTable[inputChan][256]; * The in-table - * icUInt8Number clutTable[icAny]; * The clut - * icUInt8Number outputTable[outputChan][256]; * The out-table - */ -} icLut8; - -/* Measurement Data */ -typedef struct { - icStandardObserver stdObserver; /* Standard observer */ - icXYZNumber backing; /* XYZ for backing */ - icMeasurementGeometry geometry; /* Meas. geometry */ - icMeasurementFlare flare; /* Measurement flare */ - icIlluminant illuminant; /* Illuminant */ -} icMeasurement; - -/* Named color */ - -/* - * icNamedColor2 takes the place of icNamedColor - */ -typedef struct { - icUInt32Number vendorFlag; /* Bottom 16 bits for IC use */ - icUInt32Number count; /* Count of named colors */ - icUInt32Number nDeviceCoords; /* Num of device coordinates */ - icInt8Number prefix[32]; /* Prefix for each color name */ - icInt8Number suffix[32]; /* Suffix for each color name */ - icInt8Number data[icAny]; /* Named color data follows */ -/* - * Data that follows is of this form - * - * icInt8Number root1[32]; * Root name for 1st color - * icUInt16Number pcsCoords1[icAny]; * PCS coords of 1st color - * icUInt16Number deviceCoords1[icAny]; * Dev coords of 1st color - * icInt8Number root2[32]; * Root name for 2nd color - * icUInt16Number pcsCoords2[icAny]; * PCS coords of 2nd color - * icUInt16Number deviceCoords2[icAny]; * Dev coords of 2nd color - * : - * : - * Repeat for name and PCS and device color coordinates up to (count-1) - * - * NOTES: - * PCS and device space can be determined from the header. - * - * PCS coordinates are icUInt16 numbers and are described in Annex A of - * the ICC spec. Only 16 bit L*a*b* and XYZ are allowed. The number of - * coordinates is consistent with the headers PCS. - * - * Device coordinates are icUInt16 numbers where 0x0000 represents - * the minimum value and 0xFFFF represents the maximum value. - * If the nDeviceCoords value is 0 this field is not given. - */ -} icNamedColor2; - -/* Profile sequence structure */ -typedef struct { - icSignature deviceMfg; /* Dev Manufacturer */ - icSignature deviceModel; /* Dev Model */ - icUInt64Number attributes; /* Dev attributes */ - icTechnologySignature technology; /* Technology sig */ - icInt8Number data[icAny]; /* Desc text follows */ -/* - * Data that follows is of this form, this is an icInt8Number - * to avoid problems with a compiler generating bad code as - * these arrays are variable in length. - * - * icTextDescription deviceMfgDesc; * Manufacturer text - * icTextDescription modelDesc; * Model text - */ -} icDescStruct; - -/* Profile sequence description */ -typedef struct { - icUInt32Number count; /* Number of descriptions */ - icUInt8Number data[icAny]; /* Array of desc structs */ -} icProfileSequenceDesc; - -/* textDescription */ -typedef struct { - icUInt32Number count; /* Description length */ - icInt8Number data[icAny]; /* Descriptions follow */ -/* - * Data that follows is of this form - * - * icInt8Number desc[count] * NULL terminated ascii string - * icUInt32Number ucLangCode; * UniCode language code - * icUInt32Number ucCount; * UniCode description length - * icInt16Number ucDesc[ucCount];* The UniCode description - * icUInt16Number scCode; * ScriptCode code - * icUInt8Number scCount; * ScriptCode count - * icInt8Number scDesc[67]; * ScriptCode Description - */ -} icTextDescription; - -/* Screening Data */ -typedef struct { - icS15Fixed16Number frequency; /* Frequency */ - icS15Fixed16Number angle; /* Screen angle */ - icSpotShape spotShape; /* Spot Shape encodings below */ -} icScreeningData; - -typedef struct { - icUInt32Number screeningFlag; /* Screening flag */ - icUInt32Number channels; /* Number of channels */ - icScreeningData data[icAny]; /* Array of screening data */ -} icScreening; - -/* Text Data */ -typedef struct { - icInt8Number data[icAny]; /* Variable array of chars */ -} icText; - -/* Structure describing either a UCR or BG curve */ -typedef struct { - icUInt32Number count; /* Curve length */ - icUInt16Number curve[icAny]; /* The array of curve values */ -} icUcrBgCurve; - -/* Under color removal, black generation */ -typedef struct { - icInt8Number data[icAny]; /* The Ucr BG data */ -/* - * Data that follows is of this form, this is a icInt8Number - * to avoid problems with a compiler generating bad code as - * these arrays are variable in length. - * - * icUcrBgCurve ucr; * Ucr curve - * icUcrBgCurve bg; * Bg curve - * icInt8Number string; * UcrBg description - */ -} icUcrBg; - -/* viewingConditionsType */ -typedef struct { - icXYZNumber illuminant; /* In candelas per sq. meter */ - icXYZNumber surround; /* In candelas per sq. meter */ - icIlluminant stdIluminant; /* See icIlluminant defines */ -} icViewingCondition; - -/* CrdInfo type */ -typedef struct { - icUInt32Number count; /* Char count includes NULL */ - icInt8Number desc[icAny]; /* Null terminated string */ -} icCrdInfo; - -/*------------------------------------------------------------------------*/ -/* - * Tag Type definitions - */ - -/* - * Many of the structures contain variable length arrays. This - * is represented by the use of the convention. - * - * type data[icAny]; - */ - -/* The base part of each tag */ -typedef struct { - icTagTypeSignature sig; /* Signature */ - icInt8Number reserved[4]; /* Reserved, set to 0 */ -} icTagBase; - -/* curveType */ -typedef struct { - icTagBase base; /* Signature, "curv" */ - icCurve curve; /* The curve data */ -} icCurveType; - -/* dataType */ -typedef struct { - icTagBase base; /* Signature, "data" */ - icData data; /* The data structure */ -} icDataType; - -/* dateTimeType */ -typedef struct { - icTagBase base; /* Signature, "dtim" */ - icDateTimeNumber date; /* The date */ -} icDateTimeType; - -/* lut16Type */ -typedef struct { - icTagBase base; /* Signature, "mft2" */ - icLut16 lut; /* Lut16 data */ -} icLut16Type; - -/* lut8Type, input & output tables are always 256 bytes in length */ -typedef struct { - icTagBase base; /* Signature, "mft1" */ - icLut8 lut; /* Lut8 data */ -} icLut8Type; - -/* Measurement Type */ -typedef struct { - icTagBase base; /* Signature, "meas" */ - icMeasurement measurement; /* Measurement data */ -} icMeasurementType; - -/* Named color type */ -/* icNamedColor2Type, replaces icNamedColorType */ -typedef struct { - icTagBase base; /* Signature, "ncl2" */ - icNamedColor2 ncolor; /* Named color data */ -} icNamedColor2Type; - -/* Profile sequence description type */ -typedef struct { - icTagBase base; /* Signature, "pseq" */ - icProfileSequenceDesc desc; /* The seq description */ -} icProfileSequenceDescType; - -/* textDescriptionType */ -typedef struct { - icTagBase base; /* Signature, "desc" */ - icTextDescription desc; /* The description */ -} icTextDescriptionType; - -/* s15Fixed16Type */ -typedef struct { - icTagBase base; /* Signature, "sf32" */ - icS15Fixed16Array data; /* Array of values */ -} icS15Fixed16ArrayType; - -typedef struct { - icTagBase base; /* Signature, "scrn" */ - icScreening screen; /* Screening structure */ -} icScreeningType; - -/* sigType */ -typedef struct { - icTagBase base; /* Signature, "sig" */ - icSignature signature; /* The signature data */ -} icSignatureType; - -/* textType */ -typedef struct { - icTagBase base; /* Signature, "text" */ - icText data; /* Variable array of chars */ -} icTextType; - -/* u16Fixed16Type */ -typedef struct { - icTagBase base; /* Signature, "uf32" */ - icU16Fixed16Array data; /* Variable array of values */ -} icU16Fixed16ArrayType; - -/* Under color removal, black generation type */ -typedef struct { - icTagBase base; /* Signature, "bfd " */ - icUcrBg data; /* ucrBg structure */ -} icUcrBgType; - -/* uInt16Type */ -typedef struct { - icTagBase base; /* Signature, "ui16" */ - icUInt16Array data; /* Variable array of values */ -} icUInt16ArrayType; - -/* uInt32Type */ -typedef struct { - icTagBase base; /* Signature, "ui32" */ - icUInt32Array data; /* Variable array of values */ -} icUInt32ArrayType; - -/* uInt64Type */ -typedef struct { - icTagBase base; /* Signature, "ui64" */ - icUInt64Array data; /* Variable array of values */ -} icUInt64ArrayType; - -/* uInt8Type */ -typedef struct { - icTagBase base; /* Signature, "ui08" */ - icUInt8Array data; /* Variable array of values */ -} icUInt8ArrayType; - -/* viewingConditionsType */ -typedef struct { - icTagBase base; /* Signature, "view" */ - icViewingCondition view; /* Viewing conditions */ -} icViewingConditionType; - -/* XYZ Type */ -typedef struct { - icTagBase base; /* Signature, "XYZ" */ - icXYZArray data; /* Variable array of XYZ nums */ -} icXYZType; - -/* CRDInfoType where [0] is the CRD product name count and string and - * [1] -[5] are the rendering intents 0-4 counts and strings - */ -typedef struct { - icTagBase base; /* Signature, "crdi" */ - icCrdInfo info; /* 5 sets of counts & strings */ -}icCrdInfoType; - /* icCrdInfo productName; PS product count/string */ - /* icCrdInfo CRDName0; CRD name for intent 0 */ - /* icCrdInfo CRDName1; CRD name for intent 1 */ - /* icCrdInfo CRDName2; CRD name for intent 2 */ - /* icCrdInfo CRDName3; CRD name for intent 3 */ - -/*------------------------------------------------------------------------*/ - -/* - * Lists of tags, tags, profile header and profile structure - */ - -/* A tag */ -typedef struct { - icTagSignature sig; /* The tag signature */ - icUInt32Number offset; /* Start of tag relative to - * start of header, Spec - * Clause 5 */ - icUInt32Number size; /* Size in bytes */ -} icTag; - -/* A Structure that may be used independently for a list of tags */ -typedef struct { - icUInt32Number count; /* Num tags in the profile */ - icTag tags[icAny]; /* Variable array of tags */ -} icTagList; - -/* The Profile header */ -typedef struct { - icUInt32Number size; /* Prof size in bytes */ - icSignature cmmId; /* CMM for profile */ - icUInt32Number version; /* Format version */ - icProfileClassSignature deviceClass; /* Type of profile */ - icColorSpaceSignature colorSpace; /* Clr space of data */ - icColorSpaceSignature pcs; /* PCS, XYZ or Lab */ - icDateTimeNumber date; /* Creation Date */ - icSignature magic; /* icMagicNumber */ - icPlatformSignature platform; /* Primary Platform */ - icUInt32Number flags; /* Various bits */ - icSignature manufacturer; /* Dev manufacturer */ - icUInt32Number model; /* Dev model number */ - icUInt64Number attributes; /* Device attributes */ - icUInt32Number renderingIntent;/* Rendering intent */ - icXYZNumber illuminant; /* Profile illuminant */ - icSignature creator; /* Profile creator */ - icInt8Number reserved[44]; /* Reserved */ -} icHeader; - -/* - * A profile, - * we can't use icTagList here because its not at the end of the structure - */ -typedef struct { - icHeader header; /* The header */ - icUInt32Number count; /* Num tags in the profile */ - icInt8Number data[icAny]; /* The tagTable and tagData */ -/* - * Data that follows is of the form - * - * icTag tagTable[icAny]; * The tag table - * icInt8Number tagData[icAny]; * The tag data - */ -} icProfile; - -/*------------------------------------------------------------------------*/ -/* Obsolete entries */ - -/* icNamedColor was replaced with icNamedColor2 */ -typedef struct { - icUInt32Number vendorFlag; /* Bottom 16 bits for IC use */ - icUInt32Number count; /* Count of named colors */ - icInt8Number data[icAny]; /* Named color data follows */ -/* - * Data that follows is of this form - * - * icInt8Number prefix[icAny]; * Prefix - * icInt8Number suffix[icAny]; * Suffix - * icInt8Number root1[icAny]; * Root name - * icInt8Number coords1[icAny]; * Color coordinates - * icInt8Number root2[icAny]; * Root name - * icInt8Number coords2[icAny]; * Color coordinates - * : - * : - * Repeat for root name and color coordinates up to (count-1) - */ -} icNamedColor; - -/* icNamedColorType was replaced by icNamedColor2Type */ -typedef struct { - icTagBase base; /* Signature, "ncol" */ - icNamedColor ncolor; /* Named color data */ -} icNamedColorType; - -#endif /* ICC_H */ diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/lcms.h --- a/src/share/native/sun/java2d/cmm/lcms/lcms.h Wed Jan 31 22:55:12 2018 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2099 +0,0 @@ -/* - * 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -// This file is available under and governed by the GNU General Public -// License version 2 only, as published by the Free Software Foundation. -// However, the following notice accompanied the original version of this -// file: -// -// -// Little cms -// Copyright (C) 1998-2007 Marti Maria -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Version 1.18 - -#ifndef __cms_H - -// ********** Configuration toggles **************************************** - -// Optimization mode. -// -// Note that USE_ASSEMBLER Is fastest by far, but it is limited to Pentium. -// USE_FLOAT are the generic floating-point routines. USE_C should work on -// virtually any machine. - -//#define USE_FLOAT 1 -// #define USE_C 1 -#define USE_ASSEMBLER 1 - -// Define this if you are using this package as a DLL (windows only) - -// #define LCMS_DLL 1 -// #define LCMS_DLL_BUILD 1 - -// Uncomment if you are trying the engine in a non-windows environment -// like linux, SGI, VAX, FreeBSD, BeOS, etc. -#define NON_WINDOWS 1 - -// Uncomment this one if you are using big endian machines (only meaningful -// when NON_WINDOWS is used) -// #define USE_BIG_ENDIAN 1 - -// Uncomment this one if your compiler/machine does support the -// "long long" type This will speedup fixed point math. (USE_C only) -#define USE_INT64 1 - -// Some machines does not have a reliable 'swab' function. Usually -// leave commented unless the testbed diagnoses the contrary. -// #define USE_CUSTOM_SWAB 1 - -// Uncomment this if your compiler supports inline -#define USE_INLINE 1 - -// Uncomment this if your compiler doesn't work with fast floor function -// #define USE_DEFAULT_FLOOR_CONVERSION 1 - -// Uncomment this line on multithreading environments -// #define USE_PTHREADS 1 - -// Uncomment this line if you want lcms to use the black point tag in profile, -// if commented, lcms will compute the black point by its own. -// It is safer to leve it commented out -// #define HONOR_BLACK_POINT_TAG 1 - -// ********** End of configuration toggles ****************************** - -#define LCMS_VERSION 118 - -// Microsoft VisualC++ - -// Deal with Microsoft's attempt at deprecating C standard runtime functions -#ifdef _MSC_VER -# undef NON_WINDOWS -# if (_MSC_VER >= 1400) -# ifndef _CRT_SECURE_NO_DEPRECATE -# define _CRT_SECURE_NO_DEPRECATE 1 -# endif -# endif -#endif - -// Borland C - -#ifdef __BORLANDC__ -# undef NON_WINDOWS -#endif - -#include -#include -#include -#include -#include -#include - -// Metroworks CodeWarrior -#ifdef __MWERKS__ -# define unlink remove -# if WIN32 -# define USE_CUSTOM_SWAB 1 -# undef NON_WINDOWS -# else -# define NON_WINDOWS 1 -# endif -#endif - - -// Here comes the Non-Windows settings - -#ifdef NON_WINDOWS - -// Non windows environments. Also avoid indentation on includes. - -#ifdef USE_PTHREADS -# include -typedef pthread_rwlock_t LCMS_RWLOCK_T; -# define LCMS_CREATE_LOCK(x) pthread_rwlock_init((x), NULL) -# define LCMS_FREE_LOCK(x) pthread_rwlock_destroy((x)) -# define LCMS_READ_LOCK(x) pthread_rwlock_rdlock((x)) -# define LCMS_WRITE_LOCK(x) pthread_rwlock_wrlock((x)) -# define LCMS_UNLOCK(x) pthread_rwlock_unlock((x)) -#endif - -#undef LCMS_DLL - -#ifdef USE_ASSEMBLER -# undef USE_ASSEMBLER -# define USE_C 1 -#endif - -#ifdef _HOST_BIG_ENDIAN -# define USE_BIG_ENDIAN 1 -#endif - -#if defined(__sgi__) || defined(__sgi) || defined(__powerpc__) || defined(sparc) || defined(__ppc__) || defined(__s390__) || defined(__s390x__) -# define USE_BIG_ENDIAN 1 -#endif - -#if TARGET_CPU_PPC -# define USE_BIG_ENDIAN 1 -#endif - -#if macintosh -# ifndef __LITTLE_ENDIAN__ -# define USE_BIG_ENDIAN 1 -# endif -#endif - -#ifdef __BIG_ENDIAN__ -# define USE_BIG_ENDIAN 1 -#endif - -#ifdef WORDS_BIGENDIAN -# define USE_BIG_ENDIAN 1 -#endif - -#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) -# include -# define USE_INT64 1 -# define LCMSSLONGLONG int64_t -# define LCMSULONGLONG u_int64_t -#endif - -#ifdef USE_INT64 -# ifndef LCMSULONGLONG -# define LCMSULONGLONG unsigned long long -# define LCMSSLONGLONG long long -# endif -#endif - -#if !defined(__INTEGRITY) -# include -#endif - -#include - -#if defined(__GNUC__) || defined(__FreeBSD__) -# include -#endif - -#ifndef LCMS_WIN_TYPES_ALREADY_DEFINED - -typedef unsigned char BYTE, *LPBYTE; -typedef unsigned short WORD, *LPWORD; -typedef unsigned long DWORD, *LPDWORD; -typedef char *LPSTR; -typedef void *LPVOID; - -#define ZeroMemory(p,l) memset((p),0,(l)) -#define CopyMemory(d,s,l) memcpy((d),(s),(l)) -#define FAR - -#ifndef stricmp -# define stricmp strcasecmp -#endif - - -#ifndef FALSE -# define FALSE 0 -#endif -#ifndef TRUE -# define TRUE 1 -#endif - -#define LOWORD(l) ((WORD)(l)) -#define HIWORD(l) ((WORD)((DWORD)(l) >> 16)) - -#ifndef MAX_PATH -# define MAX_PATH (256) -#endif - -#define cdecl -#endif - -// The specification for "inline" is section 6.7.4 of the C99 standard (ISO/IEC 9899:1999). - -#define LCMS_INLINE static inline - -#else - -// Win32 stuff - -#ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif - -#include - -#ifdef _WIN64 -# ifdef USE_ASSEMBLER -# undef USE_ASSEMBLER -# define USE_C 1 -# endif -#endif - -#ifdef USE_INT64 -# ifndef LCMSULONGLONG -# define LCMSULONGLONG unsigned __int64 -# define LCMSSLONGLONG __int64 -# endif -#endif - -// This works for both VC & BorlandC -#define LCMS_INLINE __inline - -#ifdef USE_PTHREADS -typedef CRITICAL_SECTION LCMS_RWLOCK_T; -# define LCMS_CREATE_LOCK(x) InitializeCriticalSection((x)) -# define LCMS_FREE_LOCK(x) DeleteCriticalSection((x)) -# define LCMS_READ_LOCK(x) EnterCriticalSection((x)) -# define LCMS_WRITE_LOCK(x) EnterCriticalSection((x)) -# define LCMS_UNLOCK(x) LeaveCriticalSection((x)) -#endif - -#endif - -#ifndef USE_PTHREADS -typedef int LCMS_RWLOCK_T; -# define LCMS_CREATE_LOCK(x) -# define LCMS_FREE_LOCK(x) -# define LCMS_READ_LOCK(x) -# define LCMS_WRITE_LOCK(x) -# define LCMS_UNLOCK(x) -#endif - -// Base types - -typedef int LCMSBOOL; -typedef void* LCMSHANDLE; - -#include "icc34.h" // ICC header file - - -// Some tag & type additions - -#define lcmsSignature ((icSignature) 0x6c636d73L) - -#define icSigLuvKData ((icColorSpaceSignature) 0x4C75764BL) // 'LuvK' - -#define icSigHexachromeData ((icColorSpaceSignature) 0x4d434836L) // MCH6 -#define icSigHeptachromeData ((icColorSpaceSignature) 0x4d434837L) // MCH7 -#define icSigOctachromeData ((icColorSpaceSignature) 0x4d434838L) // MCH8 - -#define icSigMCH5Data ((icColorSpaceSignature) 0x4d434835L) // MCH5 -#define icSigMCH6Data ((icColorSpaceSignature) 0x4d434836L) // MCH6 -#define icSigMCH7Data ((icColorSpaceSignature) 0x4d434837L) // MCH7 -#define icSigMCH8Data ((icColorSpaceSignature) 0x4d434838L) // MCH8 -#define icSigMCH9Data ((icColorSpaceSignature) 0x4d434839L) // MCH9 -#define icSigMCHAData ((icColorSpaceSignature) 0x4d434841L) // MCHA -#define icSigMCHBData ((icColorSpaceSignature) 0x4d434842L) // MCHB -#define icSigMCHCData ((icColorSpaceSignature) 0x4d434843L) // MCHC -#define icSigMCHDData ((icColorSpaceSignature) 0x4d434844L) // MCHD -#define icSigMCHEData ((icColorSpaceSignature) 0x4d434845L) // MCHE -#define icSigMCHFData ((icColorSpaceSignature) 0x4d434846L) // MCHF - -#define icSigChromaticityTag ((icTagSignature) 0x6368726dL) // As per Addendum 2 to Spec. ICC.1:1998-09 -#define icSigChromaticAdaptationTag ((icTagSignature) 0x63686164L) // 'chad' -#define icSigColorantTableTag ((icTagSignature) 0x636c7274L) // 'clrt' -#define icSigColorantTableOutTag ((icTagSignature) 0x636c6f74L) // 'clot' - -#define icSigParametricCurveType ((icTagTypeSignature) 0x70617261L) // parametric (ICC 4.0) -#define icSigMultiLocalizedUnicodeType ((icTagTypeSignature) 0x6D6C7563L) -#define icSigS15Fixed16ArrayType ((icTagTypeSignature) 0x73663332L) -#define icSigChromaticityType ((icTagTypeSignature) 0x6368726dL) -#define icSiglutAtoBType ((icTagTypeSignature) 0x6d414220L) // mAB -#define icSiglutBtoAType ((icTagTypeSignature) 0x6d424120L) // mBA -#define icSigColorantTableType ((icTagTypeSignature) 0x636c7274L) // clrt - - -typedef struct { - icUInt8Number gridPoints[16]; // Number of grid points in each dimension. - icUInt8Number prec; // Precision of data elements in bytes. - icUInt8Number pad1; - icUInt8Number pad2; - icUInt8Number pad3; - /*icUInt8Number data[icAny]; Data follows see spec for size */ -} icCLutStruct; - -// icLutAtoB -typedef struct { - icUInt8Number inputChan; // Number of input channels - icUInt8Number outputChan; // Number of output channels - icUInt8Number pad1; - icUInt8Number pad2; - icUInt32Number offsetB; // Offset to first "B" curve - icUInt32Number offsetMat; // Offset to matrix - icUInt32Number offsetM; // Offset to first "M" curve - icUInt32Number offsetC; // Offset to CLUT - icUInt32Number offsetA; // Offset to first "A" curve - /*icUInt8Number data[icAny]; Data follows see spec for size */ -} icLutAtoB; - -// icLutBtoA -typedef struct { - icUInt8Number inputChan; // Number of input channels - icUInt8Number outputChan; // Number of output channels - icUInt8Number pad1; - icUInt8Number pad2; - icUInt32Number offsetB; // Offset to first "B" curve - icUInt32Number offsetMat; // Offset to matrix - icUInt32Number offsetM; // Offset to first "M" curve - icUInt32Number offsetC; // Offset to CLUT - icUInt32Number offsetA; // Offset to first "A" curve - /*icUInt8Number data[icAny]; Data follows see spec for size */ -} icLutBtoA; - - - - - -#ifdef __cplusplus -extern "C" { -#endif - -// Calling convention - -#ifdef NON_WINDOWS -# define LCMSEXPORT -# define LCMSAPI -#else -# ifdef LCMS_DLL -# ifdef __BORLANDC__ -# define LCMSEXPORT __stdcall _export -# define LCMSAPI -# else - // VC++ -# define LCMSEXPORT _stdcall -# ifdef LCMS_DLL_BUILD -# define LCMSAPI __declspec(dllexport) -# else -# define LCMSAPI __declspec(dllimport) -# endif -# endif -# else -# define LCMSEXPORT cdecl -# define LCMSAPI -# endif -#endif - -#ifdef USE_ASSEMBLER -#ifdef __BORLANDC__ - -# define ASM asm -# define RET(v) return(v) -#else - // VC++ -# define ASM __asm -# define RET(v) return -#endif -#endif - -#ifdef _MSC_VER -#ifndef stricmp -# define stricmp _stricmp -#endif -#ifndef unlink -# define unlink _unlink -#endif -#ifndef swab -# define swab _swab -#endif -#ifndef itoa -# define itoa _itoa -#endif -#ifndef fileno -# define fileno _fileno -#endif -#ifndef strupr -# define strupr _strupr -#endif -#ifndef hypot -# define hypot _hypot -#endif -#ifndef snprintf -# define snprintf _snprintf -#endif -#ifndef vsnprintf -# define vsnprintf _vsnprintf -#endif - - -#endif - - -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - -#ifndef LOGE -# define LOGE 0.4342944819 -#endif - -// ********** Little cms API *************************************************** - -typedef LCMSHANDLE cmsHPROFILE; // Opaque typedefs to hide internals -typedef LCMSHANDLE cmsHTRANSFORM; - -#define MAXCHANNELS 16 // Maximum number of channels - -// Format of pixel is defined by one DWORD, using bit fields as follows -// -// D TTTTT U Y F P X S EEE CCCC BBB -// -// D: Use dither (8 bits only) -// T: Pixeltype -// F: Flavor 0=MinIsBlack(Chocolate) 1=MinIsWhite(Vanilla) -// P: Planar? 0=Chunky, 1=Planar -// X: swap 16 bps endianess? -// S: Do swap? ie, BGR, KYMC -// E: Extra samples -// C: Channels (Samples per pixel) -// B: Bytes per sample -// Y: Swap first - changes ABGR to BGRA and KCMY to CMYK - - -#define DITHER_SH(s) ((s) << 22) -#define COLORSPACE_SH(s) ((s) << 16) -#define SWAPFIRST_SH(s) ((s) << 14) -#define FLAVOR_SH(s) ((s) << 13) -#define PLANAR_SH(p) ((p) << 12) -#define ENDIAN16_SH(e) ((e) << 11) -#define DOSWAP_SH(e) ((e) << 10) -#define EXTRA_SH(e) ((e) << 7) -#define CHANNELS_SH(c) ((c) << 3) -#define BYTES_SH(b) (b) - -// Pixel types - -#define PT_ANY 0 // Don't check colorspace - // 1 & 2 are reserved -#define PT_GRAY 3 -#define PT_RGB 4 -#define PT_CMY 5 -#define PT_CMYK 6 -#define PT_YCbCr 7 -#define PT_YUV 8 // Lu'v' -#define PT_XYZ 9 -#define PT_Lab 10 -#define PT_YUVK 11 // Lu'v'K -#define PT_HSV 12 -#define PT_HLS 13 -#define PT_Yxy 14 -#define PT_HiFi 15 -#define PT_HiFi7 16 -#define PT_HiFi8 17 -#define PT_HiFi9 18 -#define PT_HiFi10 19 -#define PT_HiFi11 20 -#define PT_HiFi12 21 -#define PT_HiFi13 22 -#define PT_HiFi14 23 -#define PT_HiFi15 24 - -#define NOCOLORSPACECHECK(x) ((x) & 0xFFFF) - -// Some (not all!) representations - -#ifndef TYPE_RGB_8 // TYPE_RGB_8 is a very common identifier, so don't include ours - // if user has it already defined. - -#define TYPE_GRAY_8 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)) -#define TYPE_GRAY_8_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1)) -#define TYPE_GRAY_16 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) -#define TYPE_GRAY_16_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1)) -#define TYPE_GRAY_16_SE (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_GRAYA_8 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)) -#define TYPE_GRAYA_16 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)) -#define TYPE_GRAYA_16_SE (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_GRAYA_8_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_GRAYA_16_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PLANAR_SH(1)) - -#define TYPE_RGB_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_RGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_BGR_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_BGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) -#define TYPE_RGB_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_RGB_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_RGB_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_BGR_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_BGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) -#define TYPE_BGR_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -#define TYPE_RGBA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_RGBA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_RGBA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_RGBA_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_RGBA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -#define TYPE_ARGB_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_ARGB_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) - -#define TYPE_ABGR_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_ABGR_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_ABGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) -#define TYPE_ABGR_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -#define TYPE_BGRA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_BGRA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_BGRA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|SWAPFIRST_SH(1)) - -#define TYPE_CMY_8 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_CMY_8_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_CMY_16 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_CMY_16_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_CMY_16_SE (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -#define TYPE_CMYK_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)) -#define TYPE_CMYKA_8 (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(1)) -#define TYPE_CMYK_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)) -#define TYPE_YUVK_8 TYPE_CMYK_8_REV -#define TYPE_CMYK_8_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_CMYK_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) -#define TYPE_CMYK_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)) -#define TYPE_YUVK_16 TYPE_CMYK_16_REV -#define TYPE_CMYK_16_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_CMYK_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)) - -#define TYPE_KYMC_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -#define TYPE_KCMY_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_KCMY_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_KCMY_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1)) -#define TYPE_KCMY_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_KCMY_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)|SWAPFIRST_SH(1)) - - -// HiFi separations, Thanks to Steven Greaves for providing the code, -// the colorspace is not checked -#define TYPE_CMYK5_8 (CHANNELS_SH(5)|BYTES_SH(1)) -#define TYPE_CMYK5_16 (CHANNELS_SH(5)|BYTES_SH(2)) -#define TYPE_CMYK5_16_SE (CHANNELS_SH(5)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC5_8 (CHANNELS_SH(5)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC5_16 (CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC5_16_SE (CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -#define TYPE_CMYKcm_8 (CHANNELS_SH(6)|BYTES_SH(1)) -#define TYPE_CMYKcm_8_PLANAR (CHANNELS_SH(6)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_CMYKcm_16 (CHANNELS_SH(6)|BYTES_SH(2)) -#define TYPE_CMYKcm_16_PLANAR (CHANNELS_SH(6)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_CMYKcm_16_SE (CHANNELS_SH(6)|BYTES_SH(2)|ENDIAN16_SH(1)) - -// Separations with more than 6 channels aren't very standarized, -// Except most start with CMYK and add other colors, so I just used -// then total number of channels after CMYK i.e CMYK8_8 - -#define TYPE_CMYK7_8 (CHANNELS_SH(7)|BYTES_SH(1)) -#define TYPE_CMYK7_16 (CHANNELS_SH(7)|BYTES_SH(2)) -#define TYPE_CMYK7_16_SE (CHANNELS_SH(7)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC7_8 (CHANNELS_SH(7)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC7_16 (CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC7_16_SE (CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK8_8 (CHANNELS_SH(8)|BYTES_SH(1)) -#define TYPE_CMYK8_16 (CHANNELS_SH(8)|BYTES_SH(2)) -#define TYPE_CMYK8_16_SE (CHANNELS_SH(8)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC8_8 (CHANNELS_SH(8)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC8_16 (CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC8_16_SE (CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK9_8 (CHANNELS_SH(9)|BYTES_SH(1)) -#define TYPE_CMYK9_16 (CHANNELS_SH(9)|BYTES_SH(2)) -#define TYPE_CMYK9_16_SE (CHANNELS_SH(9)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC9_8 (CHANNELS_SH(9)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC9_16 (CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC9_16_SE (CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK10_8 (CHANNELS_SH(10)|BYTES_SH(1)) -#define TYPE_CMYK10_16 (CHANNELS_SH(10)|BYTES_SH(2)) -#define TYPE_CMYK10_16_SE (CHANNELS_SH(10)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC10_8 (CHANNELS_SH(10)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC10_16 (CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC10_16_SE (CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK11_8 (CHANNELS_SH(11)|BYTES_SH(1)) -#define TYPE_CMYK11_16 (CHANNELS_SH(11)|BYTES_SH(2)) -#define TYPE_CMYK11_16_SE (CHANNELS_SH(11)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC11_8 (CHANNELS_SH(11)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC11_16 (CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC11_16_SE (CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK12_8 (CHANNELS_SH(12)|BYTES_SH(1)) -#define TYPE_CMYK12_16 (CHANNELS_SH(12)|BYTES_SH(2)) -#define TYPE_CMYK12_16_SE (CHANNELS_SH(12)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC12_8 (CHANNELS_SH(12)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC12_16 (CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC12_16_SE (CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -// Colorimetric - -#define TYPE_XYZ_16 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_Lab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_ALab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)) -#define TYPE_Lab_16 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_Yxy_16 (COLORSPACE_SH(PT_Yxy)|CHANNELS_SH(3)|BYTES_SH(2)) - -// YCbCr - -#define TYPE_YCbCr_8 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_YCbCr_8_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_YCbCr_16 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_YCbCr_16_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_YCbCr_16_SE (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -// YUV - -#define TYPE_YUV_8 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_YUV_8_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_YUV_16 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_YUV_16_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_YUV_16_SE (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -// HLS - -#define TYPE_HLS_8 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_HLS_8_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_HLS_16 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_HLS_16_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_HLS_16_SE (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - - -// HSV - -#define TYPE_HSV_8 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_HSV_8_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_HSV_16 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_HSV_16_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_HSV_16_SE (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -// Named color index. Only 16 bits allowed (don't check colorspace) - -#define TYPE_NAMED_COLOR_INDEX (CHANNELS_SH(1)|BYTES_SH(2)) - -// Double values. Painful slow, but sometimes helpful. NOTE THAT 'BYTES' FIELD IS SET TO ZERO! - -#define TYPE_XYZ_DBL (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(0)) -#define TYPE_Lab_DBL (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0)) -#define TYPE_GRAY_DBL (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(0)) -#define TYPE_RGB_DBL (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)) -#define TYPE_CMYK_DBL (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(0)) - -#endif - - -// Gamma table parameters - -typedef struct { - - unsigned int Crc32; // Has my table been touched? - - // Keep initial parameters for further serialization - - int Type; - double Params[10]; - - } LCMSGAMMAPARAMS, FAR* LPLCMSGAMMAPARAMS; - -// Gamma tables. - -typedef struct { - - LCMSGAMMAPARAMS Seed; // Parameters used for table creation - - // Table-based representation follows - - int nEntries; - WORD GammaTable[1]; - - } GAMMATABLE; - -typedef GAMMATABLE FAR* LPGAMMATABLE; - -// Sampled curves (1D) -typedef struct { - - int nItems; - double* Values; - - } SAMPLEDCURVE; - -typedef SAMPLEDCURVE FAR* LPSAMPLEDCURVE; - -// Vectors -typedef struct { // Float Vector - - double n[3]; - - } VEC3; - -typedef VEC3 FAR* LPVEC3; - - -typedef struct { // Matrix - - VEC3 v[3]; - - } MAT3; - -typedef MAT3 FAR* LPMAT3; - -// Colorspace values -typedef struct { - - double X; - double Y; - double Z; - - } cmsCIEXYZ; - -typedef cmsCIEXYZ FAR* LPcmsCIEXYZ; - -typedef struct { - - double x; - double y; - double Y; - - } cmsCIExyY; - -typedef cmsCIExyY FAR* LPcmsCIExyY; - -typedef struct { - - double L; - double a; - double b; - - } cmsCIELab; - -typedef cmsCIELab FAR* LPcmsCIELab; - -typedef struct { - - double L; - double C; - double h; - - } cmsCIELCh; - -typedef cmsCIELCh FAR* LPcmsCIELCh; - -typedef struct { - - double J; - double C; - double h; - - } cmsJCh; - -typedef cmsJCh FAR* LPcmsJCh; - -// Primaries -typedef struct { - - cmsCIEXYZ Red; - cmsCIEXYZ Green; - cmsCIEXYZ Blue; - - } cmsCIEXYZTRIPLE; - -typedef cmsCIEXYZTRIPLE FAR* LPcmsCIEXYZTRIPLE; - - -typedef struct { - - cmsCIExyY Red; - cmsCIExyY Green; - cmsCIExyY Blue; - - } cmsCIExyYTRIPLE; - -typedef cmsCIExyYTRIPLE FAR* LPcmsCIExyYTRIPLE; - - - -// Following ICC spec - -#define D50X (0.9642) -#define D50Y (1.0) -#define D50Z (0.8249) - -#define PERCEPTUAL_BLACK_X (0.00336) -#define PERCEPTUAL_BLACK_Y (0.0034731) -#define PERCEPTUAL_BLACK_Z (0.00287) - -// Does return pointers to constant structs - -LCMSAPI LPcmsCIEXYZ LCMSEXPORT cmsD50_XYZ(void); -LCMSAPI LPcmsCIExyY LCMSEXPORT cmsD50_xyY(void); - - -// Input/Output - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess); -LCMSAPI cmsHPROFILE LCMSEXPORT cmsOpenProfileFromMem(LPVOID MemPtr, DWORD dwSize); -LCMSAPI LCMSBOOL LCMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile); - -// Predefined run-time profiles - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateRGBProfile(LPcmsCIExyY WhitePoint, - LPcmsCIExyYTRIPLE Primaries, - LPGAMMATABLE TransferFunction[3]); - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateGrayProfile(LPcmsCIExyY WhitePoint, - LPGAMMATABLE TransferFunction); - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateLinearizationDeviceLink(icColorSpaceSignature ColorSpace, - LPGAMMATABLE TransferFunctions[]); - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateInkLimitingDeviceLink(icColorSpaceSignature ColorSpace, - double Limit); - - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateLabProfile(LPcmsCIExyY WhitePoint); -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateLab4Profile(LPcmsCIExyY WhitePoint); - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateXYZProfile(void); -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreate_sRGBProfile(void); - - - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints, - double Bright, - double Contrast, - double Hue, - double Saturation, - int TempSrc, - int TempDest); - -LCMSAPI cmsHPROFILE LCMSEXPORT cmsCreateNULLProfile(void); - - -// Colorimetric space conversions - -LCMSAPI void LCMSEXPORT cmsXYZ2xyY(LPcmsCIExyY Dest, const cmsCIEXYZ* Source); -LCMSAPI void LCMSEXPORT cmsxyY2XYZ(LPcmsCIEXYZ Dest, const cmsCIExyY* Source); -LCMSAPI void LCMSEXPORT cmsXYZ2Lab(LPcmsCIEXYZ WhitePoint, LPcmsCIELab Lab, const cmsCIEXYZ* xyz); -LCMSAPI void LCMSEXPORT cmsLab2XYZ(LPcmsCIEXYZ WhitePoint, LPcmsCIEXYZ xyz, const cmsCIELab* Lab); -LCMSAPI void LCMSEXPORT cmsLab2LCh(LPcmsCIELCh LCh, const cmsCIELab* Lab); -LCMSAPI void LCMSEXPORT cmsLCh2Lab(LPcmsCIELab Lab, const cmsCIELCh* LCh); - - -// CIELab handling - -LCMSAPI double LCMSEXPORT cmsDeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); -LCMSAPI double LCMSEXPORT cmsCIE94DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); -LCMSAPI double LCMSEXPORT cmsBFDdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); -LCMSAPI double LCMSEXPORT cmsCMCdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2); -LCMSAPI double LCMSEXPORT cmsCIE2000DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2, double Kl, double Kc, double Kh); - -LCMSAPI void LCMSEXPORT cmsClampLab(LPcmsCIELab Lab, double amax, double amin, double bmax, double bmin); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsAdaptToIlluminant(LPcmsCIEXYZ Result, - LPcmsCIEXYZ SourceWhitePt, - LPcmsCIEXYZ Illuminant, - LPcmsCIEXYZ Value); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsBuildRGB2XYZtransferMatrix(LPMAT3 r, - LPcmsCIExyY WhitePoint, - LPcmsCIExyYTRIPLE Primaries); - -// Viewing conditions - -#define AVG_SURROUND_4 0 -#define AVG_SURROUND 1 -#define DIM_SURROUND 2 -#define DARK_SURROUND 3 -#define CUTSHEET_SURROUND 4 - -#define D_CALCULATE (-1) -#define D_CALCULATE_DISCOUNT (-2) - -typedef struct { - - cmsCIEXYZ whitePoint; - double Yb; - double La; - int surround; - double D_value; - - } cmsViewingConditions; - -typedef cmsViewingConditions FAR* LPcmsViewingConditions; - -// CIECAM97s - -LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC2); -LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel); -LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut); -LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut); - - -// CIECAM02 - -LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC); -LCMSAPI void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel); -LCMSAPI void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut); -LCMSAPI void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut); - - -// Gamma - -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma); -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[]); -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries); -LCMSAPI void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma); -LCMSAPI void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3]); -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE Src); -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma); -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma); -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma, int nPoints); -LCMSAPI LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda); -LCMSAPI double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t); -LCMSAPI double LCMSEXPORT cmsEstimateGammaEx(LPWORD Table, int nEntries, double Thereshold); -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsReadICCGamma(cmsHPROFILE hProfile, icTagSignature sig); -LCMSAPI LPGAMMATABLE LCMSEXPORT cmsReadICCGammaReversed(cmsHPROFILE hProfile, icTagSignature sig); - -// Access to Profile data. - -LCMSAPI LCMSBOOL LCMSEXPORT cmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile); -LCMSAPI LCMSBOOL LCMSEXPORT cmsTakeMediaBlackPoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile); -LCMSAPI LCMSBOOL LCMSEXPORT cmsTakeIluminant(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile); -LCMSAPI LCMSBOOL LCMSEXPORT cmsTakeColorants(LPcmsCIEXYZTRIPLE Dest, cmsHPROFILE hProfile); -LCMSAPI DWORD LCMSEXPORT cmsTakeHeaderFlags(cmsHPROFILE hProfile); -LCMSAPI DWORD LCMSEXPORT cmsTakeHeaderAttributes(cmsHPROFILE hProfile); - -LCMSAPI void LCMSEXPORT cmsSetLanguage(const char LanguageCode[4], const char CountryCode[4]); -LCMSAPI const char* LCMSEXPORT cmsTakeProductName(cmsHPROFILE hProfile); -LCMSAPI const char* LCMSEXPORT cmsTakeProductDesc(cmsHPROFILE hProfile); -LCMSAPI const char* LCMSEXPORT cmsTakeProductInfo(cmsHPROFILE hProfile); -LCMSAPI const char* LCMSEXPORT cmsTakeManufacturer(cmsHPROFILE hProfile); -LCMSAPI const char* LCMSEXPORT cmsTakeModel(cmsHPROFILE hProfile); -LCMSAPI const char* LCMSEXPORT cmsTakeCopyright(cmsHPROFILE hProfile); -LCMSAPI const BYTE* LCMSEXPORT cmsTakeProfileID(cmsHPROFILE hProfile); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsTakeCreationDateTime(struct tm *Dest, cmsHPROFILE hProfile); -LCMSAPI LCMSBOOL LCMSEXPORT cmsTakeCalibrationDateTime(struct tm *Dest, cmsHPROFILE hProfile); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIsTag(cmsHPROFILE hProfile, icTagSignature sig); -LCMSAPI int LCMSEXPORT cmsTakeRenderingIntent(cmsHPROFILE hProfile); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsTakeCharTargetData(cmsHPROFILE hProfile, char** Data, size_t* len); - -LCMSAPI int LCMSEXPORT cmsReadICCTextEx(cmsHPROFILE hProfile, icTagSignature sig, char *Text, size_t size); -LCMSAPI int LCMSEXPORT cmsReadICCText(cmsHPROFILE hProfile, icTagSignature sig, char *Text); - - -#define LCMS_DESC_MAX 512 - -typedef struct { - - icSignature deviceMfg; - icSignature deviceModel; - icUInt32Number attributes[2]; - icTechnologySignature technology; - - char Manufacturer[LCMS_DESC_MAX]; - char Model[LCMS_DESC_MAX]; - - } cmsPSEQDESC, FAR *LPcmsPSEQDESC; - -typedef struct { - - int n; - cmsPSEQDESC seq[1]; - - } cmsSEQ, FAR *LPcmsSEQ; - - -LCMSAPI LPcmsSEQ LCMSEXPORT cmsReadProfileSequenceDescription(cmsHPROFILE hProfile); -LCMSAPI void LCMSEXPORT cmsFreeProfileSequenceDescription(LPcmsSEQ pseq); - - -// Translate form/to our notation to ICC -LCMSAPI icColorSpaceSignature LCMSEXPORT _cmsICCcolorSpace(int OurNotation); -LCMSAPI int LCMSEXPORT _cmsLCMScolorSpace(icColorSpaceSignature ProfileSpace); -LCMSAPI int LCMSEXPORT _cmsChannelsOf(icColorSpaceSignature ColorSpace); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsIsMatrixShaper(cmsHPROFILE hProfile); - -// How profiles may be used -#define LCMS_USED_AS_INPUT 0 -#define LCMS_USED_AS_OUTPUT 1 -#define LCMS_USED_AS_PROOF 2 - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, int Intent, int UsedDirection); - -LCMSAPI icColorSpaceSignature LCMSEXPORT cmsGetPCS(cmsHPROFILE hProfile); -LCMSAPI icColorSpaceSignature LCMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile); -LCMSAPI icProfileClassSignature LCMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile); -LCMSAPI DWORD LCMSEXPORT cmsGetProfileICCversion(cmsHPROFILE hProfile); -LCMSAPI void LCMSEXPORT cmsSetProfileICCversion(cmsHPROFILE hProfile, DWORD Version); -LCMSAPI icInt32Number LCMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile); -LCMSAPI icTagSignature LCMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, icInt32Number n); - - -LCMSAPI void LCMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, icProfileClassSignature sig); -LCMSAPI void LCMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, icColorSpaceSignature sig); -LCMSAPI void LCMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, icColorSpaceSignature pcs); -LCMSAPI void LCMSEXPORT cmsSetRenderingIntent(cmsHPROFILE hProfile, int RenderingIntent); -LCMSAPI void LCMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, DWORD Flags); -LCMSAPI void LCMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, DWORD Flags); -LCMSAPI void LCMSEXPORT cmsSetProfileID(cmsHPROFILE hProfile, LPBYTE ProfileID); - -// Intents - -#define INTENT_PERCEPTUAL 0 -#define INTENT_RELATIVE_COLORIMETRIC 1 -#define INTENT_SATURATION 2 -#define INTENT_ABSOLUTE_COLORIMETRIC 3 - -// Flags - -#define cmsFLAGS_MATRIXINPUT 0x0001 -#define cmsFLAGS_MATRIXOUTPUT 0x0002 -#define cmsFLAGS_MATRIXONLY (cmsFLAGS_MATRIXINPUT|cmsFLAGS_MATRIXOUTPUT) - -#define cmsFLAGS_NOWHITEONWHITEFIXUP 0x0004 // Don't hot fix scum dot -#define cmsFLAGS_NOPRELINEARIZATION 0x0010 // Don't create prelinearization tables - // on precalculated transforms (internal use) - -#define cmsFLAGS_GUESSDEVICECLASS 0x0020 // Guess device class (for transform2devicelink) - -#define cmsFLAGS_NOTCACHE 0x0040 // Inhibit 1-pixel cache - -#define cmsFLAGS_NOTPRECALC 0x0100 -#define cmsFLAGS_NULLTRANSFORM 0x0200 // Don't transform anyway -#define cmsFLAGS_HIGHRESPRECALC 0x0400 // Use more memory to give better accurancy -#define cmsFLAGS_LOWRESPRECALC 0x0800 // Use less memory to minimize resouces - - -#define cmsFLAGS_WHITEBLACKCOMPENSATION 0x2000 -#define cmsFLAGS_BLACKPOINTCOMPENSATION cmsFLAGS_WHITEBLACKCOMPENSATION - -// Proofing flags - -#define cmsFLAGS_GAMUTCHECK 0x1000 // Out of Gamut alarm -#define cmsFLAGS_SOFTPROOFING 0x4000 // Do softproofing - -// Black preservation - -#define cmsFLAGS_PRESERVEBLACK 0x8000 - -// CRD special - -#define cmsFLAGS_NODEFAULTRESOURCEDEF 0x01000000 - -// Gridpoints - -#define cmsFLAGS_GRIDPOINTS(n) (((n) & 0xFF) << 16) - - -// Transforms - -LCMSAPI cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input, - DWORD InputFormat, - cmsHPROFILE Output, - DWORD OutputFormat, - int Intent, - DWORD dwFlags); - -LCMSAPI cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input, - DWORD InputFormat, - cmsHPROFILE Output, - DWORD OutputFormat, - cmsHPROFILE Proofing, - int Intent, - int ProofingIntent, - DWORD dwFlags); - -LCMSAPI cmsHTRANSFORM LCMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], - int nProfiles, - DWORD InputFormat, - DWORD OutputFormat, - int Intent, - DWORD dwFlags); - -LCMSAPI void LCMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform); - -LCMSAPI void LCMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, - LPVOID InputBuffer, - LPVOID OutputBuffer, - unsigned int Size); - -LCMSAPI void LCMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, DWORD InputFormat, DWORD dwOutputFormat); - -LCMSAPI void LCMSEXPORT cmsSetAlarmCodes(int r, int g, int b); -LCMSAPI void LCMSEXPORT cmsGetAlarmCodes(int *r, int *g, int *b); - - -// Adaptation state for absolute colorimetric intent - -LCMSAPI double LCMSEXPORT cmsSetAdaptationState(double d); - - -// Primary preservation strategy - -#define LCMS_PRESERVE_PURE_K 0 -#define LCMS_PRESERVE_K_PLANE 1 - -LCMSAPI int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n); - -// Named color support -typedef struct { - char Name[MAX_PATH]; - WORD PCS[3]; - WORD DeviceColorant[MAXCHANNELS]; - - - } cmsNAMEDCOLOR, FAR* LPcmsNAMEDCOLOR; - -typedef struct { - int nColors; - int Allocated; - int ColorantCount; - char Prefix[33]; - char Suffix[33]; - - cmsNAMEDCOLOR List[1]; - - } cmsNAMEDCOLORLIST, FAR* LPcmsNAMEDCOLORLIST; - -// Named color support - -LCMSAPI int LCMSEXPORT cmsNamedColorCount(cmsHTRANSFORM xform); -LCMSAPI LCMSBOOL LCMSEXPORT cmsNamedColorInfo(cmsHTRANSFORM xform, int nColor, char* Name, char* Prefix, char* Suffix); -LCMSAPI int LCMSEXPORT cmsNamedColorIndex(cmsHTRANSFORM xform, const char* Name); - -// Colorant tables - -LCMSAPI LPcmsNAMEDCOLORLIST LCMSEXPORT cmsReadColorantTable(cmsHPROFILE hProfile, icTagSignature sig); - -// Profile creation - -LCMSAPI LCMSBOOL LCMSEXPORT cmsAddTag(cmsHPROFILE hProfile, icTagSignature sig, const void* data); - -// Converts a transform to a devicelink profile -LCMSAPI cmsHPROFILE LCMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, DWORD dwFlags); - -// Set the 'save as 8-bit' flag -LCMSAPI void LCMSEXPORT _cmsSetLUTdepth(cmsHPROFILE hProfile, int depth); - - -// Save profile -LCMSAPI LCMSBOOL LCMSEXPORT _cmsSaveProfile(cmsHPROFILE hProfile, const char* FileName); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, - size_t* BytesNeeded); - - - -// PostScript ColorRenderingDictionary and ColorSpaceArray - -LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen); -LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen); -LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile, int Intent, DWORD dwFlags, LPVOID Buffer, DWORD dwBufferLen); - - -// Error handling - -#define LCMS_ERROR_ABORT 0 -#define LCMS_ERROR_SHOW 1 -#define LCMS_ERROR_IGNORE 2 - -LCMSAPI int LCMSEXPORT cmsErrorAction(int nAction); - -#define LCMS_ERRC_WARNING 0x1000 -#define LCMS_ERRC_RECOVERABLE 0x2000 -#define LCMS_ERRC_ABORTED 0x3000 - -typedef int (* cmsErrorHandlerFunction)(int ErrorCode, const char *ErrorText); - -LCMSAPI void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn); - - -// LUT manipulation - - -typedef struct _lcms_LUT_struc LUT, FAR* LPLUT; // opaque pointer - -LCMSAPI LPLUT LCMSEXPORT cmsAllocLUT(void); -LCMSAPI LPLUT LCMSEXPORT cmsAllocLinearTable(LPLUT NewLUT, LPGAMMATABLE Tables[], int nTable); -LCMSAPI LPLUT LCMSEXPORT cmsAlloc3DGrid(LPLUT Lut, int clutPoints, int inputChan, int outputChan); -LCMSAPI LPLUT LCMSEXPORT cmsSetMatrixLUT(LPLUT Lut, LPMAT3 M); -LCMSAPI LPLUT LCMSEXPORT cmsSetMatrixLUT4(LPLUT Lut, LPMAT3 M, LPVEC3 off, DWORD dwFlags); -LCMSAPI void LCMSEXPORT cmsFreeLUT(LPLUT Lut); -LCMSAPI void LCMSEXPORT cmsEvalLUT(LPLUT Lut, WORD In[], WORD Out[]); -LCMSAPI double LCMSEXPORT cmsEvalLUTreverse(LPLUT Lut, WORD Target[], WORD Result[], LPWORD Hint); -LCMSAPI LPLUT LCMSEXPORT cmsReadICCLut(cmsHPROFILE hProfile, icTagSignature sig); -LCMSAPI LPLUT LCMSEXPORT cmsDupLUT(LPLUT Orig); - - -// LUT Sampling - -typedef int (* _cmsSAMPLER)(register WORD In[], - register WORD Out[], - register LPVOID Cargo); - -#define SAMPLER_HASTL1 LUT_HASTL1 -#define SAMPLER_HASTL2 LUT_HASTL2 -#define SAMPLER_INSPECT 0x01000000 - -LCMSAPI int LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags); - -// Formatters - -typedef unsigned char* (* cmsFORMATTER)(register void* CMMcargo, - register WORD ToUnroll[], - register LPBYTE Buffer); - -LCMSAPI void LCMSEXPORT cmsSetUserFormatters(cmsHTRANSFORM hTransform, DWORD dwInput, cmsFORMATTER Input, - DWORD dwOutput, cmsFORMATTER Output); - -LCMSAPI void LCMSEXPORT cmsGetUserFormatters(cmsHTRANSFORM hTransform, - LPDWORD InputFormat, cmsFORMATTER* Input, - LPDWORD OutputFormat, cmsFORMATTER* Output); - - -// IT8.7 / CGATS.17-200x handling - -LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8Alloc(void); -LCMSAPI void LCMSEXPORT cmsIT8Free(LCMSHANDLE IT8); - -// Tables - -LCMSAPI int LCMSEXPORT cmsIT8TableCount(LCMSHANDLE IT8); -LCMSAPI int LCMSEXPORT cmsIT8SetTable(LCMSHANDLE IT8, int nTable); - -// Persistence -LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8LoadFromFile(const char* cFileName); -LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8LoadFromMem(void *Ptr, size_t len); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SaveToFile(LCMSHANDLE IT8, const char* cFileName); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SaveToMem(LCMSHANDLE hIT8, void *MemPtr, size_t* BytesNeeded); - -// Properties -LCMSAPI const char* LCMSEXPORT cmsIT8GetSheetType(LCMSHANDLE hIT8); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetSheetType(LCMSHANDLE hIT8, const char* Type); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetComment(LCMSHANDLE hIT8, const char* cComment); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyStr(LCMSHANDLE hIT8, const char* cProp, const char *Str); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyHex(LCMSHANDLE hIT8, const char* cProp, int Val); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char* cSubProp, const char *Val); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyUncooked(LCMSHANDLE hIT8, const char* Key, const char* Buffer); - - -LCMSAPI const char* LCMSEXPORT cmsIT8GetProperty(LCMSHANDLE hIT8, const char* cProp); -LCMSAPI double LCMSEXPORT cmsIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp); -LCMSAPI const char* LCMSEXPORT cmsIT8GetPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char *cSubProp); -LCMSAPI int LCMSEXPORT cmsIT8EnumProperties(LCMSHANDLE hIT8, const char ***PropertyNames); -LCMSAPI int LCMSEXPORT cmsIT8EnumPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char*** SubpropertyNames); - -// Datasets - -LCMSAPI const char* LCMSEXPORT cmsIT8GetDataRowCol(LCMSHANDLE IT8, int row, int col); -LCMSAPI double LCMSEXPORT cmsIT8GetDataRowColDbl(LCMSHANDLE IT8, int row, int col); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataRowCol(LCMSHANDLE hIT8, int row, int col, - const char* Val); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataRowColDbl(LCMSHANDLE hIT8, int row, int col, - double Val); - -LCMSAPI const char* LCMSEXPORT cmsIT8GetData(LCMSHANDLE IT8, const char* cPatch, const char* cSample); - - -LCMSAPI double LCMSEXPORT cmsIT8GetDataDbl(LCMSHANDLE IT8, const char* cPatch, const char* cSample); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetData(LCMSHANDLE IT8, const char* cPatch, - const char* cSample, - const char *Val); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataDbl(LCMSHANDLE hIT8, const char* cPatch, - const char* cSample, - double Val); - -LCMSAPI int LCMSEXPORT cmsIT8GetDataFormat(LCMSHANDLE hIT8, const char* cSample); -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataFormat(LCMSHANDLE IT8, int n, const char *Sample); -LCMSAPI int LCMSEXPORT cmsIT8EnumDataFormat(LCMSHANDLE IT8, char ***SampleNames); - - -LCMSAPI const char* LCMSEXPORT cmsIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer); -LCMSAPI int LCMSEXPORT cmsIT8GetPatchByName(LCMSHANDLE hIT8, const char *cSample); - -// The LABEL extension - -LCMSAPI int LCMSEXPORT cmsIT8SetTableByLabel(LCMSHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType); - -LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetIndexColumn(LCMSHANDLE hIT8, const char* cSample); - -// Formatter for double -LCMSAPI void LCMSEXPORT cmsIT8DefineDblFormat(LCMSHANDLE IT8, const char* Formatter); - - -// *************************************************************************** -// End of Little cms API From here functions are private -// You can use them only if using static libraries, and at your own risk of -// be stripped or changed at futures releases. - -#ifndef LCMS_APIONLY - - -// Compatibility with anterior versions-- not needed anymore -// -- Morge - -LCMSAPI void LCMSEXPORT cmsLabEncoded2Float(LPcmsCIELab Lab, const WORD wLab[3]); -LCMSAPI void LCMSEXPORT cmsLabEncoded2Float4(LPcmsCIELab Lab, const WORD wLab[3]); -LCMSAPI void LCMSEXPORT cmsFloat2LabEncoded(WORD wLab[3], const cmsCIELab* Lab); -LCMSAPI void LCMSEXPORT cmsFloat2LabEncoded4(WORD wLab[3], const cmsCIELab* Lab); -LCMSAPI void LCMSEXPORT cmsXYZEncoded2Float(LPcmsCIEXYZ fxyz, const WORD XYZ[3]); -LCMSAPI void LCMSEXPORT cmsFloat2XYZEncoded(WORD XYZ[3], const cmsCIEXYZ* fXYZ); - - -// Profiling Extensions --- Would be removed from API in future revisions - -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddTextTag(cmsHPROFILE hProfile, icTagSignature sig, const char* Text); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddXYZTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* XYZ); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddLUTTag(cmsHPROFILE hProfile, icTagSignature sig, const void* lut); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddGammaTag(cmsHPROFILE hProfile, icTagSignature sig, LPGAMMATABLE TransferFunction); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddChromaticityTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIExyYTRIPLE Chrm); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddSequenceDescriptionTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsSEQ PSeq); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddNamedColorTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddDateTimeTag(cmsHPROFILE hProfile, icTagSignature sig, struct tm *DateTime); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddColorantTableTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc); -LCMSAPI LCMSBOOL LCMSEXPORT _cmsAddChromaticAdaptationTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* mat); - -// --------------------------------------------------------------------------------------------------- Inline functions - -// Fast floor conversion logic. Thanks to Sree Kotay and Stuart Nixon -// note than this only works in the range ..-32767...+32767 because -// mantissa is interpreted as 15.16 fixed point. -// The union is to avoid pointer aliasing overoptimization. - -LCMS_INLINE int _cmsQuickFloor(double val) -{ -#ifdef USE_DEFAULT_FLOOR_CONVERSION - return (int) floor(val); -#else - const double _lcms_double2fixmagic = 68719476736.0 * 1.5; // 2^36 * 1.5, (52-16=36) uses limited precision to floor - union { - double val; - int halves[2]; - } temp; - - temp.val = val + _lcms_double2fixmagic; - - -#ifdef USE_BIG_ENDIAN - return temp.halves[1] >> 16; -#else - return temp.halves[0] >> 16; -#endif -#endif -} - - - -// Clamp with saturation - -LCMS_INLINE WORD _cmsClampWord(int in) -{ - if (in < 0) return 0; - if (in > 0xFFFF) return 0xFFFFU; // Including marker - return (WORD) in; -} - -#ifndef LCMS_USER_ALLOC - -// Low-level alloc hook - -LCMS_INLINE void* _cmsMalloc(size_t size) -{ - if (size > ((size_t) 1024*1024*500)) return NULL; // Never allow over 500Mb - if (size < 0) return NULL; // Prevent signed size_t exploits - - return (void*) malloc(size); -} - -LCMS_INLINE void* _cmsCalloc(size_t nmemb, size_t size) -{ - size_t alloc = nmemb * size; - - if (size == 0) { - return _cmsMalloc(0); - } - if (alloc / size != nmemb) { - return NULL; - } - return _cmsMalloc(alloc); -} - -LCMS_INLINE void _cmsFree(void *Ptr) -{ - if (Ptr) free(Ptr); -} - -#endif - -// ------------------------------------------------------------------------------------------- end of inline functions - -// Signal error from inside lcms code - -void cdecl cmsSignalError(int ErrorCode, const char *ErrorText, ...); - -// Alignment handling (needed in ReadLUT16 and ReadLUT8) - -typedef struct { - icS15Fixed16Number a; - icUInt16Number b; - - } _cmsTestAlign16; - -#define SIZEOF_UINT16_ALIGNED (sizeof(_cmsTestAlign16) - sizeof(icS15Fixed16Number)) - -typedef struct { - icS15Fixed16Number a; - icUInt8Number b; - - } _cmsTestAlign8; - -#define SIZEOF_UINT8_ALIGNED (sizeof(_cmsTestAlign8) - sizeof(icS15Fixed16Number)) - - -// Fixed point - - -typedef icInt32Number Fixed32; // Fixed 15.16 whith sign - -#define INT_TO_FIXED(x) ((x)<<16) -#define DOUBLE_TO_FIXED(x) ((Fixed32) ((x)*65536.0+0.5)) -#define FIXED_TO_INT(x) ((x)>>16) -#define FIXED_REST_TO_INT(x) ((x)& 0xFFFFU) -#define FIXED_TO_DOUBLE(x) (((double)x)/65536.0) -#define ROUND_FIXED_TO_INT(x) (((x)+0x8000)>>16) - - -Fixed32 cdecl FixedMul(Fixed32 a, Fixed32 b); -Fixed32 cdecl FixedSquare(Fixed32 a); - - -#ifdef USE_INLINE - -LCMS_INLINE Fixed32 ToFixedDomain(int a) { return a + ((a + 0x7fff) / 0xffff); } -LCMS_INLINE int FromFixedDomain(Fixed32 a) { return a - ((a + 0x7fff) >> 16); } - -#else - -Fixed32 cdecl ToFixedDomain(int a); // (a * 65536.0 / 65535.0) -int cdecl FromFixedDomain(Fixed32 a); // (a * 65535.0 + .5) - -#endif - -Fixed32 cdecl FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h); -WORD cdecl FixedScale(WORD a, Fixed32 s); - -// Vector & Matrix operations. I'm using the notation frequently found in -// literature. Mostly 'Graphic Gems' samples. Not to be same routines. - -// Vector members - -#define VX 0 -#define VY 1 -#define VZ 2 - -typedef struct { // Fixed 15.16 bits vector - Fixed32 n[3]; - } WVEC3, FAR* LPWVEC3; - -typedef struct { // Matrix (Fixed 15.16) - WVEC3 v[3]; - } WMAT3, FAR* LPWMAT3; - - - -void cdecl VEC3init(LPVEC3 r, double x, double y, double z); // double version -void cdecl VEC3initF(LPWVEC3 r, double x, double y, double z); // Fix32 version -void cdecl VEC3toFix(LPWVEC3 r, LPVEC3 v); -void cdecl VEC3fromFix(LPVEC3 r, LPWVEC3 v); -void cdecl VEC3scaleFix(LPWORD r, LPWVEC3 Scale); -void cdecl VEC3swap(LPVEC3 a, LPVEC3 b); -void cdecl VEC3divK(LPVEC3 r, LPVEC3 v, double d); -void cdecl VEC3perK(LPVEC3 r, LPVEC3 v, double d); -void cdecl VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 b); -void cdecl VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 b); -LCMSBOOL cdecl VEC3equal(LPWVEC3 a, LPWVEC3 b, double Tolerance); -LCMSBOOL cdecl VEC3equalF(LPVEC3 a, LPVEC3 b, double Tolerance); -void cdecl VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d); -void cdecl VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 v); -void cdecl VEC3saturate(LPVEC3 v); -double cdecl VEC3distance(LPVEC3 a, LPVEC3 b); -double cdecl VEC3length(LPVEC3 a); - -void cdecl MAT3identity(LPMAT3 a); -void cdecl MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b); -void cdecl MAT3perK(LPMAT3 r, LPMAT3 v, double d); -int cdecl MAT3inverse(LPMAT3 a, LPMAT3 b); -LCMSBOOL cdecl MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b); -double cdecl MAT3det(LPMAT3 m); -void cdecl MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 v); -void cdecl MAT3toFix(LPWMAT3 r, LPMAT3 v); -void cdecl MAT3fromFix(LPMAT3 r, LPWMAT3 v); -void cdecl MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v); -LCMSBOOL cdecl MAT3isIdentity(LPWMAT3 a, double Tolerance); -void cdecl MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d); - -// Is a table linear? - -int cdecl cmsIsLinear(WORD Table[], int nEntries); - -// I hold this structures describing domain -// details mainly for optimization purposes. - -struct _lcms_l16params_struc; - -typedef void (* _cms3DLERP)(WORD Input[], - WORD Output[], - WORD LutTable[], - struct _lcms_l16params_struc* p); - - - -typedef struct _lcms_l8opt_struc { // Used on 8 bit interpolations - - unsigned int X0[256], Y0[256], Z0[256]; - WORD rx[256], ry[256], rz[256]; - - } L8PARAMS, FAR* LPL8PARAMS; - -typedef struct _lcms_l16params_struc { // Used on 16 bits interpolations - - int nSamples; // Valid on all kinds of tables - int nInputs; // != 1 only in 3D interpolation - int nOutputs; // != 1 only in 3D interpolation - - WORD Domain; - - int opta1, opta2; - int opta3, opta4; // Optimization for 3D LUT - int opta5, opta6; - int opta7, opta8; - - _cms3DLERP Interp3D; // The interpolation routine - - LPL8PARAMS p8; // Points to some tables for 8-bit speedup - - } L16PARAMS, *LPL16PARAMS; - - -void cdecl cmsCalcL16Params(int nSamples, LPL16PARAMS p); -void cdecl cmsCalcCLUT16Params(int nSamples, int InputChan, int OutputChan, LPL16PARAMS p); -void cdecl cmsCalcCLUT16ParamsEx(int nSamples, int InputChan, int OutputChan, - LCMSBOOL lUseTetrahedral, LPL16PARAMS p); - -WORD cdecl cmsLinearInterpLUT16(WORD Value, WORD LutTable[], LPL16PARAMS p); -Fixed32 cdecl cmsLinearInterpFixed(WORD Value1, WORD LutTable[], LPL16PARAMS p); -WORD cdecl cmsReverseLinearInterpLUT16(WORD Value, WORD LutTable[], LPL16PARAMS p); - -void cdecl cmsTrilinearInterp16(WORD Input[], - WORD Output[], - WORD LutTable[], - LPL16PARAMS p); - -void cdecl cmsTetrahedralInterp16(WORD Input[], - WORD Output[], - WORD LutTable[], LPL16PARAMS p); - -void cdecl cmsTetrahedralInterp8(WORD Input[], - WORD Output[], - WORD LutTable[], LPL16PARAMS p); - -// LUT handling - -#define LUT_HASMATRIX 0x0001 // Do-op Flags -#define LUT_HASTL1 0x0002 -#define LUT_HASTL2 0x0008 -#define LUT_HAS3DGRID 0x0010 - -// New in rev 4.0 of ICC spec - -#define LUT_HASMATRIX3 0x0020 // Matrix + offset for LutAToB -#define LUT_HASMATRIX4 0x0040 // Matrix + offset for LutBToA - -#define LUT_HASTL3 0x0100 // '3' curves for LutAToB -#define LUT_HASTL4 0x0200 // '4' curves for LutBToA - -// V4 emulation - -#define LUT_V4_OUTPUT_EMULATE_V2 0x10000 // Is a V4 output LUT, emulating V2 -#define LUT_V4_INPUT_EMULATE_V2 0x20000 // Is a V4 input LUT, emulating V2 -#define LUT_V2_OUTPUT_EMULATE_V4 0x40000 // Is a V2 output LUT, emulating V4 -#define LUT_V2_INPUT_EMULATE_V4 0x80000 // Is a V2 input LUT, emulating V4 - - -struct _lcms_LUT_struc { - - DWORD wFlags; - WMAT3 Matrix; // 15fixed16 matrix - - unsigned int InputChan; - unsigned int OutputChan; - unsigned int InputEntries; - unsigned int OutputEntries; - unsigned int cLutPoints; - - - LPWORD L1[MAXCHANNELS]; // First linearization - LPWORD L2[MAXCHANNELS]; // Last linearization - - LPWORD T; // 3D CLUT - unsigned int Tsize; // CLUT size in bytes - - // Parameters & Optimizations - - L16PARAMS In16params; - L16PARAMS Out16params; - L16PARAMS CLut16params; - - int Intent; // Accomplished intent - - // New for Rev 4.0 of spec (reserved) - - WMAT3 Mat3; - WVEC3 Ofs3; - LPWORD L3[MAXCHANNELS]; - L16PARAMS L3params; - unsigned int L3Entries; - - WMAT3 Mat4; - WVEC3 Ofs4; - LPWORD L4[MAXCHANNELS]; - L16PARAMS L4params; - unsigned int L4Entries; - - // Gray axes fixup. Only on v2 8-bit Lab LUT - - LCMSBOOL FixGrayAxes; - - - // Parameters used for curve creation - - LCMSGAMMAPARAMS LCurvesSeed[4][MAXCHANNELS]; - - - }; // LUT, FAR* LPLUT; - - -LCMSBOOL cdecl _cmsSmoothEndpoints(LPWORD Table, int nEntries); - - -// CRC of gamma tables - -unsigned int _cmsCrc32OfGammaTable(LPGAMMATABLE Table); - -// Sampled curves - -LPSAMPLEDCURVE cdecl cmsAllocSampledCurve(int nItems); -void cdecl cmsFreeSampledCurve(LPSAMPLEDCURVE p); -LPSAMPLEDCURVE cdecl cmsDupSampledCurve(LPSAMPLEDCURVE p); - -LPSAMPLEDCURVE cdecl cmsConvertGammaToSampledCurve(LPGAMMATABLE Gamma, int nPoints); -LPGAMMATABLE cdecl cmsConvertSampledCurveToGamma(LPSAMPLEDCURVE Sampled, double Max); - -void cdecl cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max); -void cdecl cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max); -LCMSBOOL cdecl cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double SmoothingLambda); -void cdecl cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints); - -LPSAMPLEDCURVE cdecl cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints); - -// Shaper/Matrix handling - -#define MATSHAPER_HASMATRIX 0x0001 // Do-ops flags -#define MATSHAPER_HASSHAPER 0x0002 -#define MATSHAPER_INPUT 0x0004 // Behaviour -#define MATSHAPER_OUTPUT 0x0008 -#define MATSHAPER_HASINPSHAPER 0x0010 -#define MATSHAPER_ALLSMELTED (MATSHAPER_INPUT|MATSHAPER_OUTPUT) - - -typedef struct { - DWORD dwFlags; - - WMAT3 Matrix; - - L16PARAMS p16; // Primary curve - LPWORD L[3]; - - L16PARAMS p2_16; // Secondary curve (used as input in smelted ones) - LPWORD L2[3]; - - } MATSHAPER, FAR* LPMATSHAPER; - -LPMATSHAPER cdecl cmsAllocMatShaper(LPMAT3 matrix, LPGAMMATABLE Shaper[], DWORD Behaviour); -LPMATSHAPER cdecl cmsAllocMatShaper2(LPMAT3 matrix, LPGAMMATABLE In[], LPGAMMATABLE Out[], DWORD Behaviour); - -void cdecl cmsFreeMatShaper(LPMATSHAPER MatShaper); -void cdecl cmsEvalMatShaper(LPMATSHAPER MatShaper, WORD In[], WORD Out[]); - -LCMSBOOL cdecl cmsReadICCMatrixRGB2XYZ(LPMAT3 r, cmsHPROFILE hProfile); - -LPMATSHAPER cdecl cmsBuildInputMatrixShaper(cmsHPROFILE InputProfile); -LPMATSHAPER cdecl cmsBuildOutputMatrixShaper(cmsHPROFILE OutputProfile); - - - -// White Point & Primary chromas handling -LCMSBOOL cdecl cmsAdaptationMatrix(LPMAT3 r, LPMAT3 ConeMatrix, LPcmsCIEXYZ FromIll, LPcmsCIEXYZ ToIll); -LCMSBOOL cdecl cmsAdaptMatrixToD50(LPMAT3 r, LPcmsCIExyY SourceWhitePt); -LCMSBOOL cdecl cmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt); - -LCMSBOOL cdecl cmsReadChromaticAdaptationMatrix(LPMAT3 r, cmsHPROFILE hProfile); - -// Inter-PCS conversion routines. They assume D50 as white point. -void cdecl cmsXYZ2LabEncoded(WORD XYZ[3], WORD Lab[3]); -void cdecl cmsLab2XYZEncoded(WORD Lab[3], WORD XYZ[3]); - -// Retrieve text representation of WP -void cdecl _cmsIdentifyWhitePoint(char *Buffer, LPcmsCIEXYZ WhitePt); - -// Quantize to WORD in a (MaxSamples - 1) domain -WORD cdecl _cmsQuantizeVal(double i, int MaxSamples); - -LPcmsNAMEDCOLORLIST cdecl cmsAllocNamedColorList(int n); -int cdecl cmsReadICCnamedColorList(cmsHTRANSFORM xform, cmsHPROFILE hProfile, icTagSignature sig); -void cdecl cmsFreeNamedColorList(LPcmsNAMEDCOLORLIST List); -LCMSBOOL cdecl cmsAppendNamedColor(cmsHTRANSFORM xform, const char* Name, WORD PCS[3], WORD Colorant[MAXCHANNELS]); - - -// I/O - -#define MAX_TABLE_TAG 100 - -// This is the internal struct holding profile details. - -typedef struct _lcms_iccprofile_struct { - - void* stream; // Associated stream. If NULL, - // tags are supposed to be in - // memory rather than in a file. - - // Only most important items found in ICC profile - - icProfileClassSignature DeviceClass; - icColorSpaceSignature ColorSpace; - icColorSpaceSignature PCS; - icRenderingIntent RenderingIntent; - icUInt32Number flags; - icUInt32Number attributes; - cmsCIEXYZ Illuminant; - - // Additions for V4 profiles - - icUInt32Number Version; - MAT3 ChromaticAdaptation; - cmsCIEXYZ MediaWhitePoint; - cmsCIEXYZ MediaBlackPoint; - BYTE ProfileID[16]; - - - // Dictionary - - icInt32Number TagCount; - icTagSignature TagNames[MAX_TABLE_TAG]; - size_t TagSizes[MAX_TABLE_TAG]; - size_t TagOffsets[MAX_TABLE_TAG]; - LPVOID TagPtrs[MAX_TABLE_TAG]; - - char PhysicalFile[MAX_PATH]; - - LCMSBOOL IsWrite; - LCMSBOOL SaveAs8Bits; - - struct tm Created; - - // I/O handlers - - size_t (* Read)(void *buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc); - - LCMSBOOL (* Seek)(struct _lcms_iccprofile_struct* Icc, size_t offset); - LCMSBOOL (* Close)(struct _lcms_iccprofile_struct* Icc); - size_t (* Tell)(struct _lcms_iccprofile_struct* Icc); - LCMSBOOL (* Grow)(struct _lcms_iccprofile_struct* Icc, size_t amount); - - // Writting - - LCMSBOOL (* Write)(struct _lcms_iccprofile_struct* Icc, size_t size, LPVOID Ptr); - - size_t UsedSpace; - - - } LCMSICCPROFILE, FAR* LPLCMSICCPROFILE; - - -// Create an empty template for virtual profiles -cmsHPROFILE cdecl _cmsCreateProfilePlaceholder(void); - -// Search into tag dictionary -icInt32Number cdecl _cmsSearchTag(LPLCMSICCPROFILE Profile, icTagSignature sig, LCMSBOOL lSignalError); - -// Search for a particular tag, replace if found or add new one else -LPVOID _cmsInitTag(LPLCMSICCPROFILE Icc, icTagSignature sig, size_t size, const void* Init); - - -LPLCMSICCPROFILE cdecl _cmsCreateProfileFromFilePlaceholder(const char* FileName); -LPLCMSICCPROFILE cdecl _cmsCreateProfileFromMemPlaceholder(LPVOID MemPtr, DWORD dwSize); - -void _cmsSetSaveToDisk(LPLCMSICCPROFILE Icc, const char* FileName); -void _cmsSetSaveToMemory(LPLCMSICCPROFILE Icc, LPVOID MemPtr, size_t dwSize); - - - -// These macros unpack format specifiers into integers - -#define T_DITHER(s) (((s)>>22)&1) -#define T_COLORSPACE(s) (((s)>>16)&31) -#define T_SWAPFIRST(s) (((s)>>14)&1) -#define T_FLAVOR(s) (((s)>>13)&1) -#define T_PLANAR(p) (((p)>>12)&1) -#define T_ENDIAN16(e) (((e)>>11)&1) -#define T_DOSWAP(e) (((e)>>10)&1) -#define T_EXTRA(e) (((e)>>7)&7) -#define T_CHANNELS(c) (((c)>>3)&15) -#define T_BYTES(b) ((b)&7) - - - -// Internal XFORM struct -struct _cmstransform_struct; - -// Full xform -typedef void (* _cmsCOLORCALLBACKFN)(struct _cmstransform_struct *Transform, - LPVOID InputBuffer, - LPVOID OutputBuffer, unsigned int Size); - -// intermediate pass, from WORD[] to WORD[] - -typedef void (* _cmsADJFN)(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 b); - -typedef void (* _cmsTRANSFN)(struct _cmstransform_struct *Transform, - WORD In[], WORD Out[]); - -typedef void (* _cmsCNVRT)(WORD In[], WORD Out[]); - -typedef LPBYTE (* _cmsFIXFN)(register struct _cmstransform_struct *info, - register WORD ToUnroll[], - register LPBYTE Buffer); - - - -// Transformation -typedef struct _cmstransform_struct { - - // Keep formats for further reference - DWORD InputFormat, OutputFormat; - - DWORD StrideIn, StrideOut; // Planar support - - int Intent, ProofIntent; - int DoGamutCheck; - - - cmsHPROFILE InputProfile; - cmsHPROFILE OutputProfile; - cmsHPROFILE PreviewProfile; - - icColorSpaceSignature EntryColorSpace; - icColorSpaceSignature ExitColorSpace; - - DWORD dwOriginalFlags; // Flags as specified by user - - WMAT3 m1, m2; // Matrix holding inter PCS operation - WVEC3 of1, of2; // Offset terms - - _cmsCOLORCALLBACKFN xform; - - // Steps in xFORM - - _cmsFIXFN FromInput; - _cmsTRANSFN FromDevice; - _cmsADJFN Stage1; - _cmsADJFN Stage2; - _cmsTRANSFN ToDevice; - _cmsFIXFN ToOutput; - - // LUTs - - LPLUT Device2PCS; - LPLUT PCS2Device; - LPLUT Gamut; // Gamut check - LPLUT Preview; // Preview (Proof) - - LPLUT DeviceLink; // Precalculated grid - device link profile - LPLUT GamutCheck; // Precalculated device -> gamut check - - // Matrix/Shapers - - LPMATSHAPER InMatShaper; - LPMATSHAPER OutMatShaper; - LPMATSHAPER SmeltMatShaper; - - // Phase of Lab/XYZ, Abs/Rel - - int Phase1, Phase2, Phase3; - - // Named color table - - LPcmsNAMEDCOLORLIST NamedColorList; - - // Flag for transform involving v4 profiles - - LCMSBOOL lInputV4Lab, lOutputV4Lab; - - - // 1-pixel cache - - WORD CacheIn[MAXCHANNELS]; - WORD CacheOut[MAXCHANNELS]; - - double AdaptationState; // Figure for v4 incomplete state of adaptation - - LCMS_RWLOCK_T rwlock; - - } _cmsTRANSFORM,FAR *_LPcmsTRANSFORM; - - - -// Packing & Unpacking - -_cmsFIXFN cdecl _cmsIdentifyInputFormat(_LPcmsTRANSFORM xform, DWORD dwInput); -_cmsFIXFN cdecl _cmsIdentifyOutputFormat(_LPcmsTRANSFORM xform, DWORD dwOutput); - - -// Conversion - -#define XYZRel 0 -#define LabRel 1 - - -int cdecl cmsChooseCnvrt(int Absolute, - int Phase1, LPcmsCIEXYZ BlackPointIn, - LPcmsCIEXYZ WhitePointIn, - LPcmsCIEXYZ IlluminantIn, - LPMAT3 ChromaticAdaptationMatrixIn, - - int Phase2, LPcmsCIEXYZ BlackPointOut, - LPcmsCIEXYZ WhitePointOut, - LPcmsCIEXYZ IlluminantOut, - LPMAT3 ChromaticAdaptationMatrixOut, - int DoBPC, - double AdaptationState, - _cmsADJFN *fn1, - LPWMAT3 wm, LPWVEC3 wof); - - - -// Clamping & Gamut handling - -LCMSBOOL cdecl _cmsEndPointsBySpace(icColorSpaceSignature Space, - WORD **White, WORD **Black, int *nOutputs); - -WORD * cdecl _cmsWhiteBySpace(icColorSpaceSignature Space); - - - -WORD cdecl Clamp_L(Fixed32 in); -WORD cdecl Clamp_ab(Fixed32 in); - -// Detection of black point - -#define LCMS_BPFLAGS_D50_ADAPTED 0x0001 - -int cdecl cmsDetectBlackPoint(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, int Intent, DWORD dwFlags); - -// choose reasonable resolution -int cdecl _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD dwFlags); - -// Precalculate device link -LPLUT cdecl _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags); - -// Precalculate black preserving device link -LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags); - -// Precalculate gamut check -LPLUT cdecl _cmsPrecalculateGamutCheck(cmsHTRANSFORM h); - -// Hot fixes bad profiles -LCMSBOOL cdecl _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p); - -// Marks LUT as 8 bit on input -LPLUT cdecl _cmsBlessLUT8(LPLUT Lut); - -// Compute gamut boundary -LPLUT cdecl _cmsComputeGamutLUT(cmsHPROFILE hProfile, int Intent); - -// Compute softproof -LPLUT cdecl _cmsComputeSoftProofLUT(cmsHPROFILE hProfile, int nIntent); - -// Find a suitable prelinearization tables, matching the given transform -void cdecl _cmsComputePrelinearizationTablesFromXFORM(cmsHTRANSFORM h[], int nTransforms, LPLUT Grid); - - -// Build a tone curve for K->K' if possible (only works on CMYK) -LPGAMMATABLE _cmsBuildKToneCurve(cmsHTRANSFORM hCMYK2CMYK, int nPoints); - -// Validates a LUT -LCMSBOOL cdecl _cmsValidateLUT(LPLUT NewLUT); - - -// These are two VITAL macros, from converting between 8 and 16 bit -// representation. - -#define RGB_8_TO_16(rgb) (WORD) ((((WORD) (rgb)) << 8)|(rgb)) -#define RGB_16_TO_8(rgb) (BYTE) ((((rgb) * 65281 + 8388608) >> 24) & 0xFF) - - -#endif // LCMS_APIONLY - - -#define __cms_H - -#ifdef __cplusplus -} -#endif - -#endif - diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/lcms2.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/lcms2.h Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,1940 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +// Version 2.9rc3 +// + +#ifndef _lcms2_H + +// ********** Configuration toggles **************************************** + +// Uncomment this one if you are using big endian machines +// #define CMS_USE_BIG_ENDIAN 1 + +// Uncomment this one if your compiler/machine does NOT support the +// "long long" type. +// #define CMS_DONT_USE_INT64 1 + +// Uncomment this if your compiler doesn't work with fast floor function +// #define CMS_DONT_USE_FAST_FLOOR 1 + +// Uncomment this line if you want lcms to use the black point tag in profile, +// if commented, lcms will compute the black point by its own. +// It is safer to leave it commented out +// #define CMS_USE_PROFILE_BLACK_POINT_TAG 1 + +// Uncomment this line if you are compiling as C++ and want a C++ API +// #define CMS_USE_CPP_API + +// Uncomment this line if you need strict CGATS syntax. Makes CGATS files to +// require "KEYWORD" on undefined identifiers, keep it commented out unless needed +// #define CMS_STRICT_CGATS 1 + +// Uncomment to get rid of the tables for "half" float support +// #define CMS_NO_HALF_SUPPORT 1 + +// Uncomment to get rid of pthreads/windows dependency +// #define CMS_NO_PTHREADS 1 + +// Uncomment this for special windows mutex initialization (see lcms2_internal.h) +// #define CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT + +// ********** End of configuration toggles ****************************** + +// Needed for streams +#include + +// Needed for portability (C99 per 7.1.2) +#include +#include +#include + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus +extern "C" { +# endif +#endif + +// Version/release +#define LCMS_VERSION 2090 + +// I will give the chance of redefining basic types for compilers that are not fully C99 compliant +#ifndef CMS_BASIC_TYPES_ALREADY_DEFINED + +// Base types +typedef unsigned char cmsUInt8Number; // That is guaranteed by the C99 spec +typedef signed char cmsInt8Number; // That is guaranteed by the C99 spec + +#if CHAR_BIT != 8 +# error "Unable to find 8 bit type, unsupported compiler" +#endif + +// IEEE float storage numbers +typedef float cmsFloat32Number; +typedef double cmsFloat64Number; + +// 16-bit base types +#if (USHRT_MAX == 65535U) + typedef unsigned short cmsUInt16Number; +#elif (UINT_MAX == 65535U) + typedef unsigned int cmsUInt16Number; +#else +# error "Unable to find 16 bits unsigned type, unsupported compiler" +#endif + +#if (SHRT_MAX == 32767) + typedef short cmsInt16Number; +#elif (INT_MAX == 32767) + typedef int cmsInt16Number; +#else +# error "Unable to find 16 bits signed type, unsupported compiler" +#endif + +// 32-bit base type +#if (UINT_MAX == 4294967295U) + typedef unsigned int cmsUInt32Number; +#elif (ULONG_MAX == 4294967295U) + typedef unsigned long cmsUInt32Number; +#else +# error "Unable to find 32 bit unsigned type, unsupported compiler" +#endif + +#if (INT_MAX == +2147483647) + typedef int cmsInt32Number; +#elif (LONG_MAX == +2147483647) + typedef long cmsInt32Number; +#else +# error "Unable to find 32 bit signed type, unsupported compiler" +#endif + +// 64-bit base types +#ifndef CMS_DONT_USE_INT64 +# if (ULONG_MAX == 18446744073709551615U) + typedef unsigned long cmsUInt64Number; +# elif (ULLONG_MAX == 18446744073709551615U) + typedef unsigned long long cmsUInt64Number; +# else +# define CMS_DONT_USE_INT64 1 +# endif +# if (LONG_MAX == +9223372036854775807) + typedef long cmsInt64Number; +# elif (LLONG_MAX == +9223372036854775807) + typedef long long cmsInt64Number; +# else +# define CMS_DONT_USE_INT64 1 +# endif +#endif +#endif + +// In the case 64 bit numbers are not supported by the compiler +#ifdef CMS_DONT_USE_INT64 + typedef cmsUInt32Number cmsUInt64Number[2]; + typedef cmsInt32Number cmsInt64Number[2]; +#endif + +// Derivative types +typedef cmsUInt32Number cmsSignature; +typedef cmsUInt16Number cmsU8Fixed8Number; +typedef cmsInt32Number cmsS15Fixed16Number; +typedef cmsUInt32Number cmsU16Fixed16Number; + +// Boolean type, which will be using the native integer +typedef int cmsBool; + +// Try to detect windows +#if defined (_WIN32) || defined(_WIN64) || defined(WIN32) || defined(_WIN32_) +# define CMS_IS_WINDOWS_ 1 +#endif + +#ifdef _MSC_VER +# define CMS_IS_WINDOWS_ 1 +#endif + +#ifdef __BORLANDC__ +# define CMS_IS_WINDOWS_ 1 +#endif + +// Try to detect big endian platforms. This list can be endless, so primarily rely on the configure script +// on Unix-like systems, and allow it to be set on the compiler command line using +// -DCMS_USE_BIG_ENDIAN or something similar +#ifdef CMS_USE_BIG_ENDIAN // set at compiler command line takes overall precedence + +# if CMS_USE_BIG_ENDIAN == 0 +# undef CMS_USE_BIG_ENDIAN +# endif + +#else // CMS_USE_BIG_ENDIAN + +# ifdef WORDS_BIGENDIAN // set by configure (or explicitly on compiler command line) +# define CMS_USE_BIG_ENDIAN 1 +# else // WORDS_BIGENDIAN +// Fall back to platform/compiler specific tests +# if defined(__sgi__) || defined(__sgi) || defined(sparc) +# define CMS_USE_BIG_ENDIAN 1 +# endif + +# if defined(__s390__) || defined(__s390x__) +# define CMS_USE_BIG_ENDIAN 1 +# endif + +# ifdef macintosh +# ifdef __BIG_ENDIAN__ +# define CMS_USE_BIG_ENDIAN 1 +# endif +# ifdef __LITTLE_ENDIAN__ +# undef CMS_USE_BIG_ENDIAN +# endif +# endif +# endif // WORDS_BIGENDIAN + +# if defined(_HOST_BIG_ENDIAN) || defined(__BIG_ENDIAN__) +# define CMS_USE_BIG_ENDIAN 1 +# endif + +#endif // CMS_USE_BIG_ENDIAN + + +// Calling convention -- this is hardly platform and compiler dependent +#ifdef CMS_IS_WINDOWS_ +# if defined(CMS_DLL) || defined(CMS_DLL_BUILD) +# ifdef __BORLANDC__ +# define CMSEXPORT __stdcall _export +# define CMSAPI +# else +# define CMSEXPORT __stdcall +# ifdef CMS_DLL_BUILD +# define CMSAPI __declspec(dllexport) +# else +# define CMSAPI __declspec(dllimport) +# endif +# endif +# else +# define CMSEXPORT +# define CMSAPI +# endif +#else // not Windows +# ifdef HAVE_FUNC_ATTRIBUTE_VISIBILITY +# define CMSEXPORT +# define CMSAPI __attribute__((visibility("default"))) +# else +# define CMSEXPORT +# define CMSAPI +# endif +#endif // CMS_IS_WINDOWS_ + +#ifdef HasTHREADS +# if HasTHREADS == 1 +# undef CMS_NO_PTHREADS +# else +# define CMS_NO_PTHREADS 1 +# endif +#endif + +// Some common definitions +#define cmsMAX_PATH 256 + +#ifndef FALSE +# define FALSE 0 +#endif +#ifndef TRUE +# define TRUE 1 +#endif + +// D50 XYZ normalized to Y=1.0 +#define cmsD50X 0.9642 +#define cmsD50Y 1.0 +#define cmsD50Z 0.8249 + +// V4 perceptual black +#define cmsPERCEPTUAL_BLACK_X 0.00336 +#define cmsPERCEPTUAL_BLACK_Y 0.0034731 +#define cmsPERCEPTUAL_BLACK_Z 0.00287 + +// Definitions in ICC spec +#define cmsMagicNumber 0x61637370 // 'acsp' +#define lcmsSignature 0x6c636d73 // 'lcms' + + +// Base ICC type definitions +typedef enum { + cmsSigChromaticityType = 0x6368726D, // 'chrm' + cmsSigColorantOrderType = 0x636C726F, // 'clro' + cmsSigColorantTableType = 0x636C7274, // 'clrt' + cmsSigCrdInfoType = 0x63726469, // 'crdi' + cmsSigCurveType = 0x63757276, // 'curv' + cmsSigDataType = 0x64617461, // 'data' + cmsSigDictType = 0x64696374, // 'dict' + cmsSigDateTimeType = 0x6474696D, // 'dtim' + cmsSigDeviceSettingsType = 0x64657673, // 'devs' + cmsSigLut16Type = 0x6d667432, // 'mft2' + cmsSigLut8Type = 0x6d667431, // 'mft1' + cmsSigLutAtoBType = 0x6d414220, // 'mAB ' + cmsSigLutBtoAType = 0x6d424120, // 'mBA ' + cmsSigMeasurementType = 0x6D656173, // 'meas' + cmsSigMultiLocalizedUnicodeType = 0x6D6C7563, // 'mluc' + cmsSigMultiProcessElementType = 0x6D706574, // 'mpet' + cmsSigNamedColorType = 0x6E636f6C, // 'ncol' -- DEPRECATED! + cmsSigNamedColor2Type = 0x6E636C32, // 'ncl2' + cmsSigParametricCurveType = 0x70617261, // 'para' + cmsSigProfileSequenceDescType = 0x70736571, // 'pseq' + cmsSigProfileSequenceIdType = 0x70736964, // 'psid' + cmsSigResponseCurveSet16Type = 0x72637332, // 'rcs2' + cmsSigS15Fixed16ArrayType = 0x73663332, // 'sf32' + cmsSigScreeningType = 0x7363726E, // 'scrn' + cmsSigSignatureType = 0x73696720, // 'sig ' + cmsSigTextType = 0x74657874, // 'text' + cmsSigTextDescriptionType = 0x64657363, // 'desc' + cmsSigU16Fixed16ArrayType = 0x75663332, // 'uf32' + cmsSigUcrBgType = 0x62666420, // 'bfd ' + cmsSigUInt16ArrayType = 0x75693136, // 'ui16' + cmsSigUInt32ArrayType = 0x75693332, // 'ui32' + cmsSigUInt64ArrayType = 0x75693634, // 'ui64' + cmsSigUInt8ArrayType = 0x75693038, // 'ui08' + cmsSigVcgtType = 0x76636774, // 'vcgt' + cmsSigViewingConditionsType = 0x76696577, // 'view' + cmsSigXYZType = 0x58595A20 // 'XYZ ' + + +} cmsTagTypeSignature; + +// Base ICC tag definitions +typedef enum { + cmsSigAToB0Tag = 0x41324230, // 'A2B0' + cmsSigAToB1Tag = 0x41324231, // 'A2B1' + cmsSigAToB2Tag = 0x41324232, // 'A2B2' + cmsSigBlueColorantTag = 0x6258595A, // 'bXYZ' + cmsSigBlueMatrixColumnTag = 0x6258595A, // 'bXYZ' + cmsSigBlueTRCTag = 0x62545243, // 'bTRC' + cmsSigBToA0Tag = 0x42324130, // 'B2A0' + cmsSigBToA1Tag = 0x42324131, // 'B2A1' + cmsSigBToA2Tag = 0x42324132, // 'B2A2' + cmsSigCalibrationDateTimeTag = 0x63616C74, // 'calt' + cmsSigCharTargetTag = 0x74617267, // 'targ' + cmsSigChromaticAdaptationTag = 0x63686164, // 'chad' + cmsSigChromaticityTag = 0x6368726D, // 'chrm' + cmsSigColorantOrderTag = 0x636C726F, // 'clro' + cmsSigColorantTableTag = 0x636C7274, // 'clrt' + cmsSigColorantTableOutTag = 0x636C6F74, // 'clot' + cmsSigColorimetricIntentImageStateTag = 0x63696973, // 'ciis' + cmsSigCopyrightTag = 0x63707274, // 'cprt' + cmsSigCrdInfoTag = 0x63726469, // 'crdi' + cmsSigDataTag = 0x64617461, // 'data' + cmsSigDateTimeTag = 0x6474696D, // 'dtim' + cmsSigDeviceMfgDescTag = 0x646D6E64, // 'dmnd' + cmsSigDeviceModelDescTag = 0x646D6464, // 'dmdd' + cmsSigDeviceSettingsTag = 0x64657673, // 'devs' + cmsSigDToB0Tag = 0x44324230, // 'D2B0' + cmsSigDToB1Tag = 0x44324231, // 'D2B1' + cmsSigDToB2Tag = 0x44324232, // 'D2B2' + cmsSigDToB3Tag = 0x44324233, // 'D2B3' + cmsSigBToD0Tag = 0x42324430, // 'B2D0' + cmsSigBToD1Tag = 0x42324431, // 'B2D1' + cmsSigBToD2Tag = 0x42324432, // 'B2D2' + cmsSigBToD3Tag = 0x42324433, // 'B2D3' + cmsSigGamutTag = 0x67616D74, // 'gamt' + cmsSigGrayTRCTag = 0x6b545243, // 'kTRC' + cmsSigGreenColorantTag = 0x6758595A, // 'gXYZ' + cmsSigGreenMatrixColumnTag = 0x6758595A, // 'gXYZ' + cmsSigGreenTRCTag = 0x67545243, // 'gTRC' + cmsSigLuminanceTag = 0x6C756d69, // 'lumi' + cmsSigMeasurementTag = 0x6D656173, // 'meas' + cmsSigMediaBlackPointTag = 0x626B7074, // 'bkpt' + cmsSigMediaWhitePointTag = 0x77747074, // 'wtpt' + cmsSigNamedColorTag = 0x6E636f6C, // 'ncol' // Deprecated by the ICC + cmsSigNamedColor2Tag = 0x6E636C32, // 'ncl2' + cmsSigOutputResponseTag = 0x72657370, // 'resp' + cmsSigPerceptualRenderingIntentGamutTag = 0x72696730, // 'rig0' + cmsSigPreview0Tag = 0x70726530, // 'pre0' + cmsSigPreview1Tag = 0x70726531, // 'pre1' + cmsSigPreview2Tag = 0x70726532, // 'pre2' + cmsSigProfileDescriptionTag = 0x64657363, // 'desc' + cmsSigProfileDescriptionMLTag = 0x6473636d, // 'dscm' + cmsSigProfileSequenceDescTag = 0x70736571, // 'pseq' + cmsSigProfileSequenceIdTag = 0x70736964, // 'psid' + cmsSigPs2CRD0Tag = 0x70736430, // 'psd0' + cmsSigPs2CRD1Tag = 0x70736431, // 'psd1' + cmsSigPs2CRD2Tag = 0x70736432, // 'psd2' + cmsSigPs2CRD3Tag = 0x70736433, // 'psd3' + cmsSigPs2CSATag = 0x70733273, // 'ps2s' + cmsSigPs2RenderingIntentTag = 0x70733269, // 'ps2i' + cmsSigRedColorantTag = 0x7258595A, // 'rXYZ' + cmsSigRedMatrixColumnTag = 0x7258595A, // 'rXYZ' + cmsSigRedTRCTag = 0x72545243, // 'rTRC' + cmsSigSaturationRenderingIntentGamutTag = 0x72696732, // 'rig2' + cmsSigScreeningDescTag = 0x73637264, // 'scrd' + cmsSigScreeningTag = 0x7363726E, // 'scrn' + cmsSigTechnologyTag = 0x74656368, // 'tech' + cmsSigUcrBgTag = 0x62666420, // 'bfd ' + cmsSigViewingCondDescTag = 0x76756564, // 'vued' + cmsSigViewingConditionsTag = 0x76696577, // 'view' + cmsSigVcgtTag = 0x76636774, // 'vcgt' + cmsSigMetaTag = 0x6D657461, // 'meta' + cmsSigArgyllArtsTag = 0x61727473 // 'arts' + +} cmsTagSignature; + + +// ICC Technology tag +typedef enum { + cmsSigDigitalCamera = 0x6463616D, // 'dcam' + cmsSigFilmScanner = 0x6673636E, // 'fscn' + cmsSigReflectiveScanner = 0x7273636E, // 'rscn' + cmsSigInkJetPrinter = 0x696A6574, // 'ijet' + cmsSigThermalWaxPrinter = 0x74776178, // 'twax' + cmsSigElectrophotographicPrinter = 0x6570686F, // 'epho' + cmsSigElectrostaticPrinter = 0x65737461, // 'esta' + cmsSigDyeSublimationPrinter = 0x64737562, // 'dsub' + cmsSigPhotographicPaperPrinter = 0x7270686F, // 'rpho' + cmsSigFilmWriter = 0x6670726E, // 'fprn' + cmsSigVideoMonitor = 0x7669646D, // 'vidm' + cmsSigVideoCamera = 0x76696463, // 'vidc' + cmsSigProjectionTelevision = 0x706A7476, // 'pjtv' + cmsSigCRTDisplay = 0x43525420, // 'CRT ' + cmsSigPMDisplay = 0x504D4420, // 'PMD ' + cmsSigAMDisplay = 0x414D4420, // 'AMD ' + cmsSigPhotoCD = 0x4B504344, // 'KPCD' + cmsSigPhotoImageSetter = 0x696D6773, // 'imgs' + cmsSigGravure = 0x67726176, // 'grav' + cmsSigOffsetLithography = 0x6F666673, // 'offs' + cmsSigSilkscreen = 0x73696C6B, // 'silk' + cmsSigFlexography = 0x666C6578, // 'flex' + cmsSigMotionPictureFilmScanner = 0x6D706673, // 'mpfs' + cmsSigMotionPictureFilmRecorder = 0x6D706672, // 'mpfr' + cmsSigDigitalMotionPictureCamera = 0x646D7063, // 'dmpc' + cmsSigDigitalCinemaProjector = 0x64636A70 // 'dcpj' + +} cmsTechnologySignature; + + +// ICC Color spaces +typedef enum { + cmsSigXYZData = 0x58595A20, // 'XYZ ' + cmsSigLabData = 0x4C616220, // 'Lab ' + cmsSigLuvData = 0x4C757620, // 'Luv ' + cmsSigYCbCrData = 0x59436272, // 'YCbr' + cmsSigYxyData = 0x59787920, // 'Yxy ' + cmsSigRgbData = 0x52474220, // 'RGB ' + cmsSigGrayData = 0x47524159, // 'GRAY' + cmsSigHsvData = 0x48535620, // 'HSV ' + cmsSigHlsData = 0x484C5320, // 'HLS ' + cmsSigCmykData = 0x434D594B, // 'CMYK' + cmsSigCmyData = 0x434D5920, // 'CMY ' + cmsSigMCH1Data = 0x4D434831, // 'MCH1' + cmsSigMCH2Data = 0x4D434832, // 'MCH2' + cmsSigMCH3Data = 0x4D434833, // 'MCH3' + cmsSigMCH4Data = 0x4D434834, // 'MCH4' + cmsSigMCH5Data = 0x4D434835, // 'MCH5' + cmsSigMCH6Data = 0x4D434836, // 'MCH6' + cmsSigMCH7Data = 0x4D434837, // 'MCH7' + cmsSigMCH8Data = 0x4D434838, // 'MCH8' + cmsSigMCH9Data = 0x4D434839, // 'MCH9' + cmsSigMCHAData = 0x4D434841, // 'MCHA' + cmsSigMCHBData = 0x4D434842, // 'MCHB' + cmsSigMCHCData = 0x4D434843, // 'MCHC' + cmsSigMCHDData = 0x4D434844, // 'MCHD' + cmsSigMCHEData = 0x4D434845, // 'MCHE' + cmsSigMCHFData = 0x4D434846, // 'MCHF' + cmsSigNamedData = 0x6e6d636c, // 'nmcl' + cmsSig1colorData = 0x31434C52, // '1CLR' + cmsSig2colorData = 0x32434C52, // '2CLR' + cmsSig3colorData = 0x33434C52, // '3CLR' + cmsSig4colorData = 0x34434C52, // '4CLR' + cmsSig5colorData = 0x35434C52, // '5CLR' + cmsSig6colorData = 0x36434C52, // '6CLR' + cmsSig7colorData = 0x37434C52, // '7CLR' + cmsSig8colorData = 0x38434C52, // '8CLR' + cmsSig9colorData = 0x39434C52, // '9CLR' + cmsSig10colorData = 0x41434C52, // 'ACLR' + cmsSig11colorData = 0x42434C52, // 'BCLR' + cmsSig12colorData = 0x43434C52, // 'CCLR' + cmsSig13colorData = 0x44434C52, // 'DCLR' + cmsSig14colorData = 0x45434C52, // 'ECLR' + cmsSig15colorData = 0x46434C52, // 'FCLR' + cmsSigLuvKData = 0x4C75764B // 'LuvK' + +} cmsColorSpaceSignature; + +// ICC Profile Class +typedef enum { + cmsSigInputClass = 0x73636E72, // 'scnr' + cmsSigDisplayClass = 0x6D6E7472, // 'mntr' + cmsSigOutputClass = 0x70727472, // 'prtr' + cmsSigLinkClass = 0x6C696E6B, // 'link' + cmsSigAbstractClass = 0x61627374, // 'abst' + cmsSigColorSpaceClass = 0x73706163, // 'spac' + cmsSigNamedColorClass = 0x6e6d636c // 'nmcl' + +} cmsProfileClassSignature; + +// ICC Platforms +typedef enum { + cmsSigMacintosh = 0x4150504C, // 'APPL' + cmsSigMicrosoft = 0x4D534654, // 'MSFT' + cmsSigSolaris = 0x53554E57, // 'SUNW' + cmsSigSGI = 0x53474920, // 'SGI ' + cmsSigTaligent = 0x54474E54, // 'TGNT' + cmsSigUnices = 0x2A6E6978 // '*nix' // From argyll -- Not official + +} cmsPlatformSignature; + +// Reference gamut +#define cmsSigPerceptualReferenceMediumGamut 0x70726d67 //'prmg' + +// For cmsSigColorimetricIntentImageStateTag +#define cmsSigSceneColorimetryEstimates 0x73636F65 //'scoe' +#define cmsSigSceneAppearanceEstimates 0x73617065 //'sape' +#define cmsSigFocalPlaneColorimetryEstimates 0x66706365 //'fpce' +#define cmsSigReflectionHardcopyOriginalColorimetry 0x72686F63 //'rhoc' +#define cmsSigReflectionPrintOutputColorimetry 0x72706F63 //'rpoc' + +// Multi process elements types +typedef enum { + cmsSigCurveSetElemType = 0x63767374, //'cvst' + cmsSigMatrixElemType = 0x6D617466, //'matf' + cmsSigCLutElemType = 0x636C7574, //'clut' + + cmsSigBAcsElemType = 0x62414353, // 'bACS' + cmsSigEAcsElemType = 0x65414353, // 'eACS' + + // Custom from here, not in the ICC Spec + cmsSigXYZ2LabElemType = 0x6C327820, // 'l2x ' + cmsSigLab2XYZElemType = 0x78326C20, // 'x2l ' + cmsSigNamedColorElemType = 0x6E636C20, // 'ncl ' + cmsSigLabV2toV4 = 0x32203420, // '2 4 ' + cmsSigLabV4toV2 = 0x34203220, // '4 2 ' + + // Identities + cmsSigIdentityElemType = 0x69646E20, // 'idn ' + + // Float to floatPCS + cmsSigLab2FloatPCS = 0x64326C20, // 'd2l ' + cmsSigFloatPCS2Lab = 0x6C326420, // 'l2d ' + cmsSigXYZ2FloatPCS = 0x64327820, // 'd2x ' + cmsSigFloatPCS2XYZ = 0x78326420, // 'x2d ' + cmsSigClipNegativesElemType = 0x636c7020 // 'clp ' + +} cmsStageSignature; + +// Types of CurveElements +typedef enum { + + cmsSigFormulaCurveSeg = 0x70617266, // 'parf' + cmsSigSampledCurveSeg = 0x73616D66, // 'samf' + cmsSigSegmentedCurve = 0x63757266 // 'curf' + +} cmsCurveSegSignature; + +// Used in ResponseCurveType +#define cmsSigStatusA 0x53746141 //'StaA' +#define cmsSigStatusE 0x53746145 //'StaE' +#define cmsSigStatusI 0x53746149 //'StaI' +#define cmsSigStatusT 0x53746154 //'StaT' +#define cmsSigStatusM 0x5374614D //'StaM' +#define cmsSigDN 0x444E2020 //'DN ' +#define cmsSigDNP 0x444E2050 //'DN P' +#define cmsSigDNN 0x444E4E20 //'DNN ' +#define cmsSigDNNP 0x444E4E50 //'DNNP' + +// Device attributes, currently defined values correspond to the low 4 bytes +// of the 8 byte attribute quantity +#define cmsReflective 0 +#define cmsTransparency 1 +#define cmsGlossy 0 +#define cmsMatte 2 + +// Common structures in ICC tags +typedef struct { + cmsUInt32Number len; + cmsUInt32Number flag; + cmsUInt8Number data[1]; + +} cmsICCData; + +// ICC date time +typedef struct { + cmsUInt16Number year; + cmsUInt16Number month; + cmsUInt16Number day; + cmsUInt16Number hours; + cmsUInt16Number minutes; + cmsUInt16Number seconds; + +} cmsDateTimeNumber; + +// ICC XYZ +typedef struct { + cmsS15Fixed16Number X; + cmsS15Fixed16Number Y; + cmsS15Fixed16Number Z; + +} cmsEncodedXYZNumber; + + +// Profile ID as computed by MD5 algorithm +typedef union { + cmsUInt8Number ID8[16]; + cmsUInt16Number ID16[8]; + cmsUInt32Number ID32[4]; + +} cmsProfileID; + + +// ---------------------------------------------------------------------------------------------- +// ICC profile internal base types. Strictly, shouldn't be declared in this header, but maybe +// somebody want to use this info for accessing profile header directly, so here it is. + +// Profile header -- it is 32-bit aligned, so no issues are expected on alignment +typedef struct { + cmsUInt32Number size; // Profile size in bytes + cmsSignature cmmId; // CMM for this profile + cmsUInt32Number version; // Format version number + cmsProfileClassSignature deviceClass; // Type of profile + cmsColorSpaceSignature colorSpace; // Color space of data + cmsColorSpaceSignature pcs; // PCS, XYZ or Lab only + cmsDateTimeNumber date; // Date profile was created + cmsSignature magic; // Magic Number to identify an ICC profile + cmsPlatformSignature platform; // Primary Platform + cmsUInt32Number flags; // Various bit settings + cmsSignature manufacturer; // Device manufacturer + cmsUInt32Number model; // Device model number + cmsUInt64Number attributes; // Device attributes + cmsUInt32Number renderingIntent;// Rendering intent + cmsEncodedXYZNumber illuminant; // Profile illuminant + cmsSignature creator; // Profile creator + cmsProfileID profileID; // Profile ID using MD5 + cmsInt8Number reserved[28]; // Reserved for future use + +} cmsICCHeader; + +// ICC base tag +typedef struct { + cmsTagTypeSignature sig; + cmsInt8Number reserved[4]; + +} cmsTagBase; + +// A tag entry in directory +typedef struct { + cmsTagSignature sig; // The tag signature + cmsUInt32Number offset; // Start of tag + cmsUInt32Number size; // Size in bytes + +} cmsTagEntry; + +// ---------------------------------------------------------------------------------------------- + +// Little CMS specific typedefs + +typedef void* cmsHANDLE ; // Generic handle +typedef void* cmsHPROFILE; // Opaque typedefs to hide internals +typedef void* cmsHTRANSFORM; + +#define cmsMAXCHANNELS 16 // Maximum number of channels in ICC profiles + +// Format of pixel is defined by one cmsUInt32Number, using bit fields as follows +// +// 2 1 0 +// 3 2 10987 6 5 4 3 2 1 098 7654 321 +// A O TTTTT U Y F P X S EEE CCCC BBB +// +// A: Floating point -- With this flag we can differentiate 16 bits as float and as int +// O: Optimized -- previous optimization already returns the final 8-bit value +// T: Pixeltype +// F: Flavor 0=MinIsBlack(Chocolate) 1=MinIsWhite(Vanilla) +// P: Planar? 0=Chunky, 1=Planar +// X: swap 16 bps endianness? +// S: Do swap? ie, BGR, KYMC +// E: Extra samples +// C: Channels (Samples per pixel) +// B: bytes per sample +// Y: Swap first - changes ABGR to BGRA and KCMY to CMYK + +#define FLOAT_SH(a) ((a) << 22) +#define OPTIMIZED_SH(s) ((s) << 21) +#define COLORSPACE_SH(s) ((s) << 16) +#define SWAPFIRST_SH(s) ((s) << 14) +#define FLAVOR_SH(s) ((s) << 13) +#define PLANAR_SH(p) ((p) << 12) +#define ENDIAN16_SH(e) ((e) << 11) +#define DOSWAP_SH(e) ((e) << 10) +#define EXTRA_SH(e) ((e) << 7) +#define CHANNELS_SH(c) ((c) << 3) +#define BYTES_SH(b) (b) + +// These macros unpack format specifiers into integers +#define T_FLOAT(a) (((a)>>22)&1) +#define T_OPTIMIZED(o) (((o)>>21)&1) +#define T_COLORSPACE(s) (((s)>>16)&31) +#define T_SWAPFIRST(s) (((s)>>14)&1) +#define T_FLAVOR(s) (((s)>>13)&1) +#define T_PLANAR(p) (((p)>>12)&1) +#define T_ENDIAN16(e) (((e)>>11)&1) +#define T_DOSWAP(e) (((e)>>10)&1) +#define T_EXTRA(e) (((e)>>7)&7) +#define T_CHANNELS(c) (((c)>>3)&15) +#define T_BYTES(b) ((b)&7) + + +// Pixel types +#define PT_ANY 0 // Don't check colorspace + // 1 & 2 are reserved +#define PT_GRAY 3 +#define PT_RGB 4 +#define PT_CMY 5 +#define PT_CMYK 6 +#define PT_YCbCr 7 +#define PT_YUV 8 // Lu'v' +#define PT_XYZ 9 +#define PT_Lab 10 +#define PT_YUVK 11 // Lu'v'K +#define PT_HSV 12 +#define PT_HLS 13 +#define PT_Yxy 14 + +#define PT_MCH1 15 +#define PT_MCH2 16 +#define PT_MCH3 17 +#define PT_MCH4 18 +#define PT_MCH5 19 +#define PT_MCH6 20 +#define PT_MCH7 21 +#define PT_MCH8 22 +#define PT_MCH9 23 +#define PT_MCH10 24 +#define PT_MCH11 25 +#define PT_MCH12 26 +#define PT_MCH13 27 +#define PT_MCH14 28 +#define PT_MCH15 29 + +#define PT_LabV2 30 // Identical to PT_Lab, but using the V2 old encoding + +// Some (not all!) representations + +#ifndef TYPE_RGB_8 // TYPE_RGB_8 is a very common identifier, so don't include ours + // if user has it already defined. + +#define TYPE_GRAY_8 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)) +#define TYPE_GRAY_8_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1)) +#define TYPE_GRAY_16 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_GRAY_16_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1)) +#define TYPE_GRAY_16_SE (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_GRAYA_8 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)) +#define TYPE_GRAYA_16 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_GRAYA_16_SE (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_GRAYA_8_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_GRAYA_16_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PLANAR_SH(1)) + +#define TYPE_RGB_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_RGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_BGR_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_BGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_RGB_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGB_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_RGB_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_BGR_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_BGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_BGR_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_RGBA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_RGBA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_RGBA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGBA_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_RGBA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_ARGB_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ARGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) +#define TYPE_ARGB_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) + +#define TYPE_ABGR_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_ABGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_ABGR_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_ABGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_ABGR_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_BGRA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) +#define TYPE_BGRA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) + +#define TYPE_CMY_8 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_CMY_8_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMY_16 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_CMY_16_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMY_16_SE (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_CMYK_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)) +#define TYPE_CMYKA_8 (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(1)) +#define TYPE_CMYK_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)) +#define TYPE_YUVK_8 TYPE_CMYK_8_REV +#define TYPE_CMYK_8_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMYK_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) +#define TYPE_CMYK_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)) +#define TYPE_YUVK_16 TYPE_CMYK_16_REV +#define TYPE_CMYK_16_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMYK_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_KYMC_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_KCMY_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)|SWAPFIRST_SH(1)) + +#define TYPE_CMYK5_8 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(1)) +#define TYPE_CMYK5_16 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)) +#define TYPE_CMYK5_16_SE (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC5_8 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC5_16 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC5_16_SE (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK6_8 (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(1)) +#define TYPE_CMYK6_8_PLANAR (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMYK6_16 (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)) +#define TYPE_CMYK6_16_PLANAR (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMYK6_16_SE (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_CMYK7_8 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(1)) +#define TYPE_CMYK7_16 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)) +#define TYPE_CMYK7_16_SE (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC7_8 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC7_16 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC7_16_SE (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK8_8 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(1)) +#define TYPE_CMYK8_16 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)) +#define TYPE_CMYK8_16_SE (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC8_8 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC8_16 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC8_16_SE (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK9_8 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(1)) +#define TYPE_CMYK9_16 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)) +#define TYPE_CMYK9_16_SE (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC9_8 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC9_16 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC9_16_SE (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK10_8 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(1)) +#define TYPE_CMYK10_16 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)) +#define TYPE_CMYK10_16_SE (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC10_8 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC10_16 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC10_16_SE (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK11_8 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(1)) +#define TYPE_CMYK11_16 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)) +#define TYPE_CMYK11_16_SE (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC11_8 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC11_16 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC11_16_SE (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK12_8 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(1)) +#define TYPE_CMYK12_16 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)) +#define TYPE_CMYK12_16_SE (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC12_8 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC12_16 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC12_16_SE (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +// Colorimetric +#define TYPE_XYZ_16 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_Lab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_LabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)) + +#define TYPE_ALab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ALabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_Lab_16 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_LabV2_16 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_Yxy_16 (COLORSPACE_SH(PT_Yxy)|CHANNELS_SH(3)|BYTES_SH(2)) + +// YCbCr +#define TYPE_YCbCr_8 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_YCbCr_8_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_YCbCr_16 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_YCbCr_16_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_YCbCr_16_SE (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// YUV +#define TYPE_YUV_8 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_YUV_8_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_YUV_16 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_YUV_16_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_YUV_16_SE (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// HLS +#define TYPE_HLS_8 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_HLS_8_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_HLS_16 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_HLS_16_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_HLS_16_SE (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// HSV +#define TYPE_HSV_8 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_HSV_8_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_HSV_16 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_HSV_16_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_HSV_16_SE (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// Named color index. Only 16 bits allowed (don't check colorspace) +#define TYPE_NAMED_COLOR_INDEX (CHANNELS_SH(1)|BYTES_SH(2)) + +// Float formatters. +#define TYPE_XYZ_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_Lab_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_LabA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_GRAY_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)) +#define TYPE_RGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)) + +#define TYPE_RGBA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_ARGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1)) +#define TYPE_BGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) +#define TYPE_BGRA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ABGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) + +#define TYPE_CMYK_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4)) + +// Floating point formatters. +// NOTE THAT 'BYTES' FIELD IS SET TO ZERO ON DLB because 8 bytes overflows the bitfield +#define TYPE_XYZ_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_Lab_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_GRAY_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(0)) +#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_BGR_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)|DOSWAP_SH(1)) +#define TYPE_CMYK_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(0)) + +// IEEE 754-2008 "half" +#define TYPE_GRAY_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_RGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_CMYK_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) + +#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_ARGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) +#define TYPE_BGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_BGRA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ABGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) + +#endif + +// Colorspaces +typedef struct { + cmsFloat64Number X; + cmsFloat64Number Y; + cmsFloat64Number Z; + + } cmsCIEXYZ; + +typedef struct { + cmsFloat64Number x; + cmsFloat64Number y; + cmsFloat64Number Y; + + } cmsCIExyY; + +typedef struct { + cmsFloat64Number L; + cmsFloat64Number a; + cmsFloat64Number b; + + } cmsCIELab; + +typedef struct { + cmsFloat64Number L; + cmsFloat64Number C; + cmsFloat64Number h; + + } cmsCIELCh; + +typedef struct { + cmsFloat64Number J; + cmsFloat64Number C; + cmsFloat64Number h; + + } cmsJCh; + +typedef struct { + cmsCIEXYZ Red; + cmsCIEXYZ Green; + cmsCIEXYZ Blue; + + } cmsCIEXYZTRIPLE; + +typedef struct { + cmsCIExyY Red; + cmsCIExyY Green; + cmsCIExyY Blue; + + } cmsCIExyYTRIPLE; + +// Illuminant types for structs below +#define cmsILLUMINANT_TYPE_UNKNOWN 0x0000000 +#define cmsILLUMINANT_TYPE_D50 0x0000001 +#define cmsILLUMINANT_TYPE_D65 0x0000002 +#define cmsILLUMINANT_TYPE_D93 0x0000003 +#define cmsILLUMINANT_TYPE_F2 0x0000004 +#define cmsILLUMINANT_TYPE_D55 0x0000005 +#define cmsILLUMINANT_TYPE_A 0x0000006 +#define cmsILLUMINANT_TYPE_E 0x0000007 +#define cmsILLUMINANT_TYPE_F8 0x0000008 + +typedef struct { + cmsUInt32Number Observer; // 0 = unknown, 1=CIE 1931, 2=CIE 1964 + cmsCIEXYZ Backing; // Value of backing + cmsUInt32Number Geometry; // 0=unknown, 1=45/0, 0/45 2=0d, d/0 + cmsFloat64Number Flare; // 0..1.0 + cmsUInt32Number IlluminantType; + + } cmsICCMeasurementConditions; + +typedef struct { + cmsCIEXYZ IlluminantXYZ; // Not the same struct as CAM02, + cmsCIEXYZ SurroundXYZ; // This is for storing the tag + cmsUInt32Number IlluminantType; // viewing condition + + } cmsICCViewingConditions; + +// Get LittleCMS version (for shared objects) ----------------------------------------------------------------------------- + +CMSAPI int CMSEXPORT cmsGetEncodedCMMversion(void); + +// Support of non-standard functions -------------------------------------------------------------------------------------- + +CMSAPI int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2); +CMSAPI long int CMSEXPORT cmsfilelength(FILE* f); + + +// Context handling -------------------------------------------------------------------------------------------------------- + +// Each context holds its owns globals and its own plug-ins. There is a global context with the id = 0 for lecacy compatibility +// though using the global context is not recommended. Proper context handling makes lcms more thread-safe. + +typedef struct _cmsContext_struct* cmsContext; + +CMSAPI cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData); +CMSAPI void CMSEXPORT cmsDeleteContext(cmsContext ContexID); +CMSAPI cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData); +CMSAPI void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID); + +// Plug-In registering -------------------------------------------------------------------------------------------------- + +CMSAPI cmsBool CMSEXPORT cmsPlugin(void* Plugin); +CMSAPI cmsBool CMSEXPORT cmsPluginTHR(cmsContext ContextID, void* Plugin); +CMSAPI void CMSEXPORT cmsUnregisterPlugins(void); +CMSAPI void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID); + +// Error logging ---------------------------------------------------------------------------------------------------------- + +// There is no error handling at all. When a function fails, it returns proper value. +// For example, all create functions does return NULL on failure. Other may return FALSE. +// It may be interesting, for the developer, to know why the function is failing. +// for that reason, lcms2 does offer a logging function. This function will get +// an ENGLISH string with some clues on what is going wrong. You can show this +// info to the end user if you wish, or just create some sort of log on disk. +// The logging function should NOT terminate the program, as this obviously can leave +// unfreed resources. It is the programmer's responsibility to check each function +// return code to make sure it didn't fail. + +#define cmsERROR_UNDEFINED 0 +#define cmsERROR_FILE 1 +#define cmsERROR_RANGE 2 +#define cmsERROR_INTERNAL 3 +#define cmsERROR_NULL 4 +#define cmsERROR_READ 5 +#define cmsERROR_SEEK 6 +#define cmsERROR_WRITE 7 +#define cmsERROR_UNKNOWN_EXTENSION 8 +#define cmsERROR_COLORSPACE_CHECK 9 +#define cmsERROR_ALREADY_DEFINED 10 +#define cmsERROR_BAD_SIGNATURE 11 +#define cmsERROR_CORRUPTION_DETECTED 12 +#define cmsERROR_NOT_SUITABLE 13 + +// Error logger is called with the ContextID when a message is raised. This gives the +// chance to know which thread is responsible of the warning and any environment associated +// with it. Non-multithreading applications may safely ignore this parameter. +// Note that under certain special circumstances, ContextID may be NULL. +typedef void (* cmsLogErrorHandlerFunction)(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); + +// Allows user to set any specific logger +CMSAPI void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn); +CMSAPI void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn); + +// Conversions -------------------------------------------------------------------------------------------------------------- + +// Returns pointers to constant structs +CMSAPI const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void); +CMSAPI const cmsCIExyY* CMSEXPORT cmsD50_xyY(void); + +// Colorimetric space conversions +CMSAPI void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source); +CMSAPI void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source); +CMSAPI void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz); +CMSAPI void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsLab2LCh(cmsCIELCh*LCh, const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh); + +// Encoding /Decoding on PCS +CMSAPI void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3]); +CMSAPI void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3]); +CMSAPI void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fxyz, const cmsUInt16Number XYZ[3]); +CMSAPI void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ); + +// DeltaE metrics +CMSAPI cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c); +CMSAPI cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh); + +// Temperature <-> Chromaticity (Black body) +CMSAPI cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK); +CMSAPI cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint); + +// Chromatic adaptation +CMSAPI cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, const cmsCIEXYZ* SourceWhitePt, + const cmsCIEXYZ* Illuminant, + const cmsCIEXYZ* Value); + +// CIECAM02 --------------------------------------------------------------------------------------------------- + +// Viewing conditions. Please note those are CAM model viewing conditions, and not the ICC tag viewing +// conditions, which I'm naming cmsICCViewingConditions to make differences evident. Unfortunately, the tag +// cannot deal with surround La, Yb and D value so is basically useless to store CAM02 viewing conditions. + + +#define AVG_SURROUND 1 +#define DIM_SURROUND 2 +#define DARK_SURROUND 3 +#define CUTSHEET_SURROUND 4 + +#define D_CALCULATE (-1) + +typedef struct { + cmsCIEXYZ whitePoint; + cmsFloat64Number Yb; + cmsFloat64Number La; + cmsUInt32Number surround; + cmsFloat64Number D_value; + + } cmsViewingConditions; + +CMSAPI cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC); +CMSAPI void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel); +CMSAPI void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut); +CMSAPI void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut); + + +// Tone curves ----------------------------------------------------------------------------------------- + +// This describes a curve segment. For a table of supported types, see the manual. User can increase the number of +// available types by using a proper plug-in. Parametric segments allow 10 parameters at most + +typedef struct { + cmsFloat32Number x0, x1; // Domain; for x0 < x <= x1 + cmsInt32Number Type; // Parametric type, Type == 0 means sampled segment. Negative values are reserved + cmsFloat64Number Params[10]; // Parameters if Type != 0 + cmsUInt32Number nGridPoints; // Number of grid points if Type == 0 + cmsFloat32Number* SampledPoints; // Points to an array of floats if Type == 0 + +} cmsCurveSegment; + +// The internal representation is none of your business. +typedef struct _cms_curve_struct cmsToneCurve; + +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, cmsUInt32Number nSegments, const cmsCurveSegment Segments[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsUInt32Number nEntries, const cmsUInt16Number values[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]); +CMSAPI void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve); +CMSAPI void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]); +CMSAPI cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* Src); +CMSAPI cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsUInt32Number nResultSamples, const cmsToneCurve* InGamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, const cmsToneCurve* X, const cmsToneCurve* Y, cmsUInt32Number nPoints); +CMSAPI cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda); +CMSAPI cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v); +CMSAPI cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* InGamma); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t); +CMSAPI cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t); +CMSAPI cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision); + +// Tone curve tabular estimation +CMSAPI cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t); +CMSAPI const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t); + + +// Implements pipelines of multi-processing elements ------------------------------------------------------------- + +// Nothing to see here, move along +typedef struct _cmsPipeline_struct cmsPipeline; +typedef struct _cmsStage_struct cmsStage; + +// Those are hi-level pipelines +CMSAPI cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels); +CMSAPI void CMSEXPORT cmsPipelineFree(cmsPipeline* lut); +CMSAPI cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* Orig); + +CMSAPI cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut); +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut); +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut); + +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut); +CMSAPI cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut); +CMSAPI cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut); + +CMSAPI void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut); +CMSAPI void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut); +CMSAPI cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], cmsFloat32Number Result[], cmsFloat32Number Hint[], const cmsPipeline* lut); +CMSAPI cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2); +CMSAPI cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On); + +// Where to place/locate the stages in the pipeline chain +typedef enum { cmsAT_BEGIN, cmsAT_END } cmsStageLoc; + +CMSAPI cmsBool CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe); +CMSAPI void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe); + +// This function is quite useful to analyze the structure of a Pipeline and retrieve the Stage elements +// that conform the Pipeline. It should be called with the Pipeline, the number of expected elements and +// then a list of expected types followed with a list of double pointers to Stage elements. If +// the function founds a match with current pipeline, it fills the pointers and returns TRUE +// if not, returns FALSE without touching anything. +CMSAPI cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...); + +// Matrix has double precision and CLUT has only float precision. That is because an ICC profile can encode +// matrices with far more precision that CLUTS +CMSAPI cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset); + +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, cmsUInt32Number nGridPoints, cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsUInt16Number* Table); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, cmsUInt32Number nGridPoints, cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table); + +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsUInt16Number* Table); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table); + +CMSAPI cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe); +CMSAPI void CMSEXPORT cmsStageFree(cmsStage* mpe); +CMSAPI cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe); + +CMSAPI cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe); +CMSAPI cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe); +CMSAPI cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe); +CMSAPI void* CMSEXPORT cmsStageData(const cmsStage* mpe); + +// Sampling +typedef cmsInt32Number (* cmsSAMPLER16) (register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register void * Cargo); + +typedef cmsInt32Number (* cmsSAMPLERFLOAT)(register const cmsFloat32Number In[], + register cmsFloat32Number Out[], + register void * Cargo); + +// Use this flag to prevent changes being written to destination +#define SAMPLER_INSPECT 0x01000000 + +// For CLUT only +CMSAPI cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void* Cargo, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void* Cargo, cmsUInt32Number dwFlags); + +// Slicers +CMSAPI cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLER16 Sampler, void * Cargo); + +CMSAPI cmsBool CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLERFLOAT Sampler, void * Cargo); + +// Multilocalized Unicode management --------------------------------------------------------------------------------------- + +typedef struct _cms_MLU_struct cmsMLU; + +#define cmsNoLanguage "\0\0" +#define cmsNoCountry "\0\0" + +CMSAPI cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems); +CMSAPI void CMSEXPORT cmsMLUfree(cmsMLU* mlu); +CMSAPI cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu); + +CMSAPI cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + const char* ASCIIString); +CMSAPI cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + const wchar_t* WideString); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char ObtainedLanguage[3], char ObtainedCountry[3]); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu); + +CMSAPI cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, + cmsUInt32Number idx, + char LanguageCode[3], + char CountryCode[3]); + +// Undercolorremoval & black generation ------------------------------------------------------------------------------------- + +typedef struct { + cmsToneCurve* Ucr; + cmsToneCurve* Bg; + cmsMLU* Desc; + +} cmsUcrBg; + +// Screening ---------------------------------------------------------------------------------------------------------------- + +#define cmsPRINTER_DEFAULT_SCREENS 0x0001 +#define cmsFREQUENCE_UNITS_LINES_CM 0x0000 +#define cmsFREQUENCE_UNITS_LINES_INCH 0x0002 + +#define cmsSPOT_UNKNOWN 0 +#define cmsSPOT_PRINTER_DEFAULT 1 +#define cmsSPOT_ROUND 2 +#define cmsSPOT_DIAMOND 3 +#define cmsSPOT_ELLIPSE 4 +#define cmsSPOT_LINE 5 +#define cmsSPOT_SQUARE 6 +#define cmsSPOT_CROSS 7 + +typedef struct { + cmsFloat64Number Frequency; + cmsFloat64Number ScreenAngle; + cmsUInt32Number SpotShape; + +} cmsScreeningChannel; + +typedef struct { + cmsUInt32Number Flag; + cmsUInt32Number nChannels; + cmsScreeningChannel Channels[cmsMAXCHANNELS]; + +} cmsScreening; + + +// Named color ----------------------------------------------------------------------------------------------------------------- + +typedef struct _cms_NAMEDCOLORLIST_struct cmsNAMEDCOLORLIST; + +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, + cmsUInt32Number n, + cmsUInt32Number ColorantCount, + const char* Prefix, const char* Suffix); + +CMSAPI void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v); +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v); +CMSAPI cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* v, const char* Name, + cmsUInt16Number PCS[3], + cmsUInt16Number Colorant[cmsMAXCHANNELS]); + +CMSAPI cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* v); +CMSAPI cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* v, const char* Name); + +CMSAPI cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, + char* Name, + char* Prefix, + char* Suffix, + cmsUInt16Number* PCS, + cmsUInt16Number* Colorant); + +// Retrieve named color list from transform +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform); + +// Profile sequence ----------------------------------------------------------------------------------------------------- + +// Profile sequence descriptor. Some fields come from profile sequence descriptor tag, others +// come from Profile Sequence Identifier Tag +typedef struct { + + cmsSignature deviceMfg; + cmsSignature deviceModel; + cmsUInt64Number attributes; + cmsTechnologySignature technology; + cmsProfileID ProfileID; + cmsMLU* Manufacturer; + cmsMLU* Model; + cmsMLU* Description; + +} cmsPSEQDESC; + +typedef struct { + + cmsUInt32Number n; + cmsContext ContextID; + cmsPSEQDESC* seq; + +} cmsSEQ; + +CMSAPI cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n); +CMSAPI cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq); +CMSAPI void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq); + +// Dictionaries -------------------------------------------------------------------------------------------------------- + +typedef struct _cmsDICTentry_struct { + + struct _cmsDICTentry_struct* Next; + + cmsMLU *DisplayName; + cmsMLU *DisplayValue; + wchar_t* Name; + wchar_t* Value; + +} cmsDICTentry; + +CMSAPI cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsDictFree(cmsHANDLE hDict); +CMSAPI cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict); + +CMSAPI cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue); +CMSAPI const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict); +CMSAPI const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e); + +// Access to Profile data ---------------------------------------------------------------------------------------------- +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID); + +CMSAPI cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile); +CMSAPI cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile); +CMSAPI cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n); +CMSAPI cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig); + +// Read and write pre-formatted data +CMSAPI void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig); +CMSAPI cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data); +CMSAPI cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest); +CMSAPI cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig); + +// Read and write raw data +CMSAPI cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* Buffer, cmsUInt32Number BufferSize); +CMSAPI cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size); + +// Access header data +#define cmsEmbeddedProfileFalse 0x00000000 +#define cmsEmbeddedProfileTrue 0x00000001 +#define cmsUseAnywhere 0x00000000 +#define cmsUseWithEmbeddedDataOnly 0x00000002 + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags); +CMSAPI void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID); +CMSAPI cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile); + +CMSAPI void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model); +CMSAPI void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags); +CMSAPI void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID); +CMSAPI void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent); + +CMSAPI cmsColorSpaceSignature + CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs); +CMSAPI cmsColorSpaceSignature + CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig); +CMSAPI cmsProfileClassSignature + CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig); +CMSAPI void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version); +CMSAPI cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version); + +// How profiles may be used +#define LCMS_USED_AS_INPUT 0 +#define LCMS_USED_AS_OUTPUT 1 +#define LCMS_USED_AS_PROOF 2 + +CMSAPI cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection); +CMSAPI cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile); +CMSAPI cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection); + +// Translate form/to our notation to ICC +CMSAPI cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation); +CMSAPI int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace); + +CMSAPI cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace); + +// Build a suitable formatter for the colorspace of this profile. nBytes=1 means 8 bits, nBytes=2 means 16 bits. +CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); +CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); + + +// Localized info +typedef enum { + cmsInfoDescription = 0, + cmsInfoManufacturer = 1, + cmsInfoModel = 2, + cmsInfoCopyright = 3 +} cmsInfoType; + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize); + +// IO handlers ---------------------------------------------------------------------------------------------------------- + +typedef struct _cms_io_handler cmsIOHANDLER; + +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile); +CMSAPI cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io); + +// MD5 message digest -------------------------------------------------------------------------------------------------- + +CMSAPI cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile); + +// Profile high level functions ------------------------------------------------------------------------------------------ + +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *ICCProfile, const char *sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char* sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char* sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void * MemPtr, cmsUInt32Number dwSize); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void * MemPtr, cmsUInt32Number dwSize); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write); +CMSAPI cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile); + +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName); +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream); +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded); +CMSAPI cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io); + +// Predefined virtual profiles ------------------------------------------------------------------------------------------ + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit); + + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, + cmsUInt32Number nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + cmsUInt32Number TempSrc, + cmsUInt32Number TempDest); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + cmsUInt32Number TempSrc, + cmsUInt32Number TempDest); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void); + +// Converts a transform to a devicelink profile +CMSAPI cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags); + +// Intents ---------------------------------------------------------------------------------------------- + +// ICC Intents +#define INTENT_PERCEPTUAL 0 +#define INTENT_RELATIVE_COLORIMETRIC 1 +#define INTENT_SATURATION 2 +#define INTENT_ABSOLUTE_COLORIMETRIC 3 + +// Non-ICC intents +#define INTENT_PRESERVE_K_ONLY_PERCEPTUAL 10 +#define INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC 11 +#define INTENT_PRESERVE_K_ONLY_SATURATION 12 +#define INTENT_PRESERVE_K_PLANE_PERCEPTUAL 13 +#define INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC 14 +#define INTENT_PRESERVE_K_PLANE_SATURATION 15 + +// Call with NULL as parameters to get the intent count +CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions); + +// Flags + +#define cmsFLAGS_NOCACHE 0x0040 // Inhibit 1-pixel cache +#define cmsFLAGS_NOOPTIMIZE 0x0100 // Inhibit optimizations +#define cmsFLAGS_NULLTRANSFORM 0x0200 // Don't transform anyway + +// Proofing flags +#define cmsFLAGS_GAMUTCHECK 0x1000 // Out of Gamut alarm +#define cmsFLAGS_SOFTPROOFING 0x4000 // Do softproofing + +// Misc +#define cmsFLAGS_BLACKPOINTCOMPENSATION 0x2000 +#define cmsFLAGS_NOWHITEONWHITEFIXUP 0x0004 // Don't fix scum dot +#define cmsFLAGS_HIGHRESPRECALC 0x0400 // Use more memory to give better accurancy +#define cmsFLAGS_LOWRESPRECALC 0x0800 // Use less memory to minimize resources + +// For devicelink creation +#define cmsFLAGS_8BITS_DEVICELINK 0x0008 // Create 8 bits devicelinks +#define cmsFLAGS_GUESSDEVICECLASS 0x0020 // Guess device class (for transform2devicelink) +#define cmsFLAGS_KEEP_SEQUENCE 0x0080 // Keep profile sequence for devicelink creation + +// Specific to a particular optimizations +#define cmsFLAGS_FORCE_CLUT 0x0002 // Force CLUT optimization +#define cmsFLAGS_CLUT_POST_LINEARIZATION 0x0001 // create postlinearization tables if possible +#define cmsFLAGS_CLUT_PRE_LINEARIZATION 0x0010 // create prelinearization tables if possible + +// Specific to unbounded mode +#define cmsFLAGS_NONEGATIVES 0x8000 // Prevent negative numbers in floating point transforms + +// Copy alpha channels when transforming +#define cmsFLAGS_COPY_ALPHA 0x04000000 // Alpha channels are copied on cmsDoTransform() + +// Fine-tune control over number of gridpoints +#define cmsFLAGS_GRIDPOINTS(n) (((n) & 0xFF) << 16) + +// CRD special +#define cmsFLAGS_NODEFAULTRESOURCEDEF 0x01000000 + +// Transforms --------------------------------------------------------------------------------------------------- + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsHPROFILE Proofing, + cmsUInt32Number Intent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsHPROFILE Proofing, + cmsUInt32Number Intent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, + cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsHPROFILE hGamutProfile, + cmsUInt32Number nGamutPCSposition, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number dwFlags); + +CMSAPI void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform); + +CMSAPI void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, + const void * InputBuffer, + void * OutputBuffer, + cmsUInt32Number Size); + +CMSAPI void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, // Deprecated + const void * InputBuffer, + void * OutputBuffer, + cmsUInt32Number Size, + cmsUInt32Number Stride); + +CMSAPI void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + cmsUInt32Number BytesPerLineIn, + cmsUInt32Number BytesPerLineOut, + cmsUInt32Number BytesPerPlaneIn, + cmsUInt32Number BytesPerPlaneOut); + + +CMSAPI void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]); +CMSAPI void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS]); + + +CMSAPI void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, + const cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]); +CMSAPI void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, + cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]); + + + +// Adaptation state for absolute colorimetric intent +CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d); +CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d); + + + +// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed +CMSAPI cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform); + +// Grab the input/output formats +CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform); + +// For backwards compatibility +CMSAPI cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat); + + + +// PostScript ColorRenderingDictionary and ColorSpaceArray ---------------------------------------------------- + +typedef enum { cmsPS_RESOURCE_CSA, cmsPS_RESOURCE_CRD } cmsPSResourceType; + +// lcms2 unified method to access postscript color resources +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, + cmsPSResourceType Type, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* io); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, void* Buffer, cmsUInt32Number dwBufferLen); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, void* Buffer, cmsUInt32Number dwBufferLen); + + +// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- + +CMSAPI cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8); + +// Tables +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8); +CMSAPI cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE hIT8, cmsUInt32Number nTable); + +// Persistence +CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName); +CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len); +// CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromIOhandler(cmsContext ContextID, cmsIOHANDLER* io); + +CMSAPI cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName); +CMSAPI cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded); + +// Properties +CMSAPI const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8); +CMSAPI cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* cComment); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* cProp, const char *Str); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer); + + +CMSAPI const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* cProp); +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp); +CMSAPI const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey); +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames); +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames); + +// Datasets +CMSAPI const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col); +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, + const char* Val); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, + cmsFloat64Number Val); + +CMSAPI const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample); + + +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE hIT8, const char* cPatch, const char* cSample); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + const char *Val); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + cmsFloat64Number Val); + +CMSAPI int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample); +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE hIT8, int n, const char *Sample); +CMSAPI int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames); + +CMSAPI const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer); +CMSAPI int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch); + +// The LABEL extension +CMSAPI int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample); + +// Formatter for double +CMSAPI void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter); + +// Gamut boundary description routines ------------------------------------------------------------------------------ + +CMSAPI cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD); +CMSAPI cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab); +CMSAPI cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGDB, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab); + +// Feature detection ---------------------------------------------------------------------------------------------- + +// Estimate the black point +CMSAPI cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); + +// Estimate total area coverage +CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile); + + +// Poor man's gamut mapping +CMSAPI cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, + double amax, double amin, + double bmax, double bmin); + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus + } +# endif +#endif + +#define _lcms2_H +#endif diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/lcms2_internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/lcms2_internal.h Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,1140 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// + +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#ifndef _lcms_internal_H + +// Include plug-in foundation +#ifndef _lcms_plugin_H +# include "lcms2_plugin.h" +#endif + +// ctype is part of C99 as per 7.1.2 +#include + +// assert macro is part of C99 as per 7.2 +#include + +// Some needed constants +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +#ifndef M_LOG10E +# define M_LOG10E 0.434294481903251827651 +#endif + +// BorlandC 5.5, VC2003 are broken on that +#if defined(__BORLANDC__) || (_MSC_VER < 1400) // 1400 == VC++ 8.0 +#define sinf(x) (float)sin((float)x) +#define sqrtf(x) (float)sqrt((float)x) +#endif + + +// Alignment of ICC file format uses 4 bytes (cmsUInt32Number) +#define _cmsALIGNLONG(x) (((x)+(sizeof(cmsUInt32Number)-1)) & ~(sizeof(cmsUInt32Number)-1)) + +// Alignment to memory pointer + +// (Ultra)SPARC with gcc requires ptr alignment of 8 bytes +// even though sizeof(void *) is only four: for greatest flexibility +// allow the build to specify ptr alignment. +#ifndef CMS_PTR_ALIGNMENT +# define CMS_PTR_ALIGNMENT sizeof(void *) +#endif + +#define _cmsALIGNMEM(x) (((x)+(CMS_PTR_ALIGNMENT - 1)) & ~(CMS_PTR_ALIGNMENT - 1)) + +// Maximum encodeable values in floating point +#define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0) +#define MIN_ENCODEABLE_ab2 (-128.0) +#define MAX_ENCODEABLE_ab2 ((65535.0/256.0) - 128.0) +#define MIN_ENCODEABLE_ab4 (-128.0) +#define MAX_ENCODEABLE_ab4 (127.0) + +// Maximum of channels for internal pipeline evaluation +#define MAX_STAGE_CHANNELS 128 + +// Unused parameter warning suppression +#define cmsUNUSED_PARAMETER(x) ((void)x) + +// The specification for "inline" is section 6.7.4 of the C99 standard (ISO/IEC 9899:1999). +// unfortunately VisualC++ does not conform that +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define cmsINLINE __inline +#else +# define cmsINLINE static inline +#endif + +// Other replacement functions +#ifdef _MSC_VER +# ifndef snprintf +# define snprintf _snprintf +# endif +# ifndef vsnprintf +# define vsnprintf _vsnprintf +# endif + +/// Properly define some macros to accommodate +/// older MSVC versions. +# if _MSC_VER <= 1700 + #include + #define isnan _isnan + #define isinf(x) (!_finite((x))) +# endif + +#endif + +// A fast way to convert from/to 16 <-> 8 bits +#define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb)) +#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((cmsUInt32Number)(rgb) * 65281U + 8388608U) >> 24) & 0xFFU) + +// Code analysis is broken on asserts +#ifdef _MSC_VER +# if (_MSC_VER >= 1500) +# define _cmsAssert(a) { assert((a)); __analysis_assume((a)); } +# else +# define _cmsAssert(a) assert((a)) +# endif +#else +# define _cmsAssert(a) assert((a)) +#endif + +//--------------------------------------------------------------------------------- + +// Determinant lower than that are assumed zero (used on matrix invert) +#define MATRIX_DET_TOLERANCE 0.0001 + +//--------------------------------------------------------------------------------- + +// Fixed point +#define FIXED_TO_INT(x) ((x)>>16) +#define FIXED_REST_TO_INT(x) ((x)&0xFFFFU) +#define ROUND_FIXED_TO_INT(x) (((x)+0x8000)>>16) + +cmsINLINE cmsS15Fixed16Number _cmsToFixedDomain(int a) { return a + ((a + 0x7fff) / 0xffff); } +cmsINLINE int _cmsFromFixedDomain(cmsS15Fixed16Number a) { return a - ((a + 0x7fff) >> 16); } + +// ----------------------------------------------------------------------------------------------------------- + +// Fast floor conversion logic. Thanks to Sree Kotay and Stuart Nixon +// note than this only works in the range ..-32767...+32767 because +// mantissa is interpreted as 15.16 fixed point. +// The union is to avoid pointer aliasing overoptimization. +cmsINLINE int _cmsQuickFloor(cmsFloat64Number val) +{ +#ifdef CMS_DONT_USE_FAST_FLOOR + return (int) floor(val); +#else + const cmsFloat64Number _lcms_double2fixmagic = 68719476736.0 * 1.5; // 2^36 * 1.5, (52-16=36) uses limited precision to floor + union { + cmsFloat64Number val; + int halves[2]; + } temp; + + temp.val = val + _lcms_double2fixmagic; + +#ifdef CMS_USE_BIG_ENDIAN + return temp.halves[1] >> 16; +#else + return temp.halves[0] >> 16; +#endif +#endif +} + +// Fast floor restricted to 0..65535.0 +cmsINLINE cmsUInt16Number _cmsQuickFloorWord(cmsFloat64Number d) +{ + return (cmsUInt16Number) _cmsQuickFloor(d - 32767.0) + 32767U; +} + +// Floor to word, taking care of saturation +cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d) +{ + d += 0.5; + if (d <= 0) return 0; + if (d >= 65535.0) return 0xffff; + + return _cmsQuickFloorWord(d); +} + +// Test bed entry points--------------------------------------------------------------- +#define CMSCHECKPOINT CMSAPI + +// Pthread support -------------------------------------------------------------------- +#ifndef CMS_NO_PTHREADS + +// This is the threading support. Unfortunately, it has to be platform-dependent because +// windows does not support pthreads. +#ifdef CMS_IS_WINDOWS_ + +#define WIN32_LEAN_AND_MEAN 1 +#include + + +// The locking scheme in LCMS requires a single 'top level' mutex +// to work. This is actually implemented on Windows as a +// CriticalSection, because they are lighter weight. With +// pthreads, this is statically inited. Unfortunately, windows +// can't officially statically init critical sections. +// +// We can work around this in 2 ways. +// +// 1) We can use a proper mutex purely to protect the init +// of the CriticalSection. This in turns requires us to protect +// the Mutex creation, which we can do using the snappily +// named InterlockedCompareExchangePointer API (present on +// windows XP and above). +// +// 2) In cases where we want to work on pre-Windows XP, we +// can use an even more horrible hack described below. +// +// So why wouldn't we always use 2)? Because not calling +// the init function for a critical section means it fails +// testing with ApplicationVerifier (and presumably similar +// tools). +// +// We therefore default to 1, and people who want to be able +// to run on pre-Windows XP boxes can build with: +// CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT +// defined. This is automatically set for builds using +// versions of MSVC that don't have this API available. +// +// From: http://locklessinc.com/articles/pthreads_on_windows/ +// The pthreads API has an initialization macro that has no correspondence to anything in +// the windows API. By investigating the internal definition of the critical section type, +// one may work out how to initialize one without calling InitializeCriticalSection(). +// The trick here is that InitializeCriticalSection() is not allowed to fail. It tries +// to allocate a critical section debug object, but if no memory is available, it sets +// the pointer to a specific value. (One would expect that value to be NULL, but it is +// actually (void *)-1 for some reason.) Thus we can use this special value for that +// pointer, and the critical section code will work. + +// The other important part of the critical section type to initialize is the number +// of waiters. This controls whether or not the mutex is locked. Fortunately, this +// part of the critical section is unlikely to change. Apparently, many programs +// already test critical sections to see if they are locked using this value, so +// Microsoft felt that it was necessary to keep it set at -1 for an unlocked critical +// section, even when they changed the underlying algorithm to be more scalable. +// The final parts of the critical section object are unimportant, and can be set +// to zero for their defaults. This yields to an initialization macro: + +typedef CRITICAL_SECTION _cmsMutex; + +#ifdef _MSC_VER +# if (_MSC_VER >= 1800) +# pragma warning(disable : 26135) +# endif +#endif + +#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT +// If we are building with a version of MSVC smaller +// than 1400 (i.e. before VS2005) then we don't have +// the InterlockedCompareExchangePointer API, so use +// the old version. +# ifdef _MSC_VER +# if _MSC_VER < 1400 +# define CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT +# endif +# endif +#endif + +#ifdef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT +# define CMS_MUTEX_INITIALIZER {(PRTL_CRITICAL_SECTION_DEBUG) -1,-1,0,0,0,0} +#else +# define CMS_MUTEX_INITIALIZER {(PRTL_CRITICAL_SECTION_DEBUG)NULL,-1,0,0,0,0} +#endif + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + EnterCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + LeaveCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + InitializeCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + DeleteCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + EnterCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + LeaveCriticalSection(m); + return 0; +} + +#else + +// Rest of the wide world +#include + +#define CMS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +typedef pthread_mutex_t _cmsMutex; + + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + return pthread_mutex_lock(m); +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + return pthread_mutex_unlock(m); +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + return pthread_mutex_init(m, NULL); +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + return pthread_mutex_destroy(m); +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + return pthread_mutex_lock(m); +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + return pthread_mutex_unlock(m); +} + +#endif +#else + +#define CMS_MUTEX_INITIALIZER 0 +typedef int _cmsMutex; + + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} +#endif + +// Plug-In registration --------------------------------------------------------------- + +// Specialized function for plug-in memory management. No pairing free() since whole pool is freed at once. +void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size); + +// Memory management +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Interpolation +cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Parametric curves +cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Formatters management +cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Tag type management +cmsBool _cmsRegisterTagTypePlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Tag management +cmsBool _cmsRegisterTagPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Intent management +cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Multi Process elements +cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Optimization +cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Transform +cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Mutex +cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// --------------------------------------------------------------------------------------------------------- + +// Suballocators. +typedef struct _cmsSubAllocator_chunk_st { + + cmsUInt8Number* Block; + cmsUInt32Number BlockSize; + cmsUInt32Number Used; + + struct _cmsSubAllocator_chunk_st* next; + +} _cmsSubAllocator_chunk; + + +typedef struct { + + cmsContext ContextID; + _cmsSubAllocator_chunk* h; + +} _cmsSubAllocator; + + +_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial); +void _cmsSubAllocDestroy(_cmsSubAllocator* s); +void* _cmsSubAlloc(_cmsSubAllocator* s, cmsUInt32Number size); +void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size); + +// ---------------------------------------------------------------------------------- + +// The context clients. +typedef enum { + + UserPtr, // User-defined pointer + Logger, + AlarmCodesContext, + AdaptationStateContext, + MemPlugin, + InterpPlugin, + CurvesPlugin, + FormattersPlugin, + TagTypePlugin, + TagPlugin, + IntentPlugin, + MPEPlugin, + OptimizationPlugin, + TransformPlugin, + MutexPlugin, + + // Last in list + MemoryClientMax + +} _cmsMemoryClient; + + +// Container for memory management plug-in. +typedef struct { + + _cmsMallocFnPtrType MallocPtr; + _cmsMalloZerocFnPtrType MallocZeroPtr; + _cmsFreeFnPtrType FreePtr; + _cmsReallocFnPtrType ReallocPtr; + _cmsCallocFnPtrType CallocPtr; + _cmsDupFnPtrType DupPtr; + +} _cmsMemPluginChunkType; + +// Copy memory management function pointers from plug-in to chunk, taking care of missing routines +void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr); + +// Internal structure for context +struct _cmsContext_struct { + + struct _cmsContext_struct* Next; // Points to next context in the new style + _cmsSubAllocator* MemPool; // The memory pool that stores context data + + void* chunks[MemoryClientMax]; // array of pointers to client chunks. Memory itself is hold in the suballocator. + // If NULL, then it reverts to global Context0 + + _cmsMemPluginChunkType DefaultMemoryManager; // The allocators used for creating the context itself. Cannot be overridden +}; + +// Returns a pointer to a valid context structure, including the global one if id is zero. +// Verifies the magic number. +struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID); + +// Returns the block assigned to the specific zone. +void* _cmsContextGetClientChunk(cmsContext id, _cmsMemoryClient mc); + + +// Chunks of context memory by plug-in client ------------------------------------------------------- + +// Those structures encapsulates all variables needed by the several context clients (mostly plug-ins) + +// Container for error logger -- not a plug-in +typedef struct { + + cmsLogErrorHandlerFunction LogErrorHandler; // Set to NULL for Context0 fallback + +} _cmsLogErrorChunkType; + +// The global Context0 storage for error logger +extern _cmsLogErrorChunkType _cmsLogErrorChunk; + +// Allocate and init error logger container. +void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for alarm codes -- not a plug-in +typedef struct { + + cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]; + +} _cmsAlarmCodesChunkType; + +// The global Context0 storage for alarm codes +extern _cmsAlarmCodesChunkType _cmsAlarmCodesChunk; + +// Allocate and init alarm codes container. +void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for adaptation state -- not a plug-in +typedef struct { + + cmsFloat64Number AdaptationState; + +} _cmsAdaptationStateChunkType; + +// The global Context0 storage for adaptation state +extern _cmsAdaptationStateChunkType _cmsAdaptationStateChunk; + +// Allocate and init adaptation state container. +void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + + +// The global Context0 storage for memory management +extern _cmsMemPluginChunkType _cmsMemPluginChunk; + +// Allocate and init memory management container. +void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for interpolation plug-in +typedef struct { + + cmsInterpFnFactory Interpolators; + +} _cmsInterpPluginChunkType; + +// The global Context0 storage for interpolation plug-in +extern _cmsInterpPluginChunkType _cmsInterpPluginChunk; + +// Allocate and init interpolation container. +void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for parametric curves plug-in +typedef struct { + + struct _cmsParametricCurvesCollection_st* ParametricCurves; + +} _cmsCurvesPluginChunkType; + +// The global Context0 storage for tone curves plug-in +extern _cmsCurvesPluginChunkType _cmsCurvesPluginChunk; + +// Allocate and init parametric curves container. +void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for formatters plug-in +typedef struct { + + struct _cms_formatters_factory_list* FactoryList; + +} _cmsFormattersPluginChunkType; + +// The global Context0 storage for formatters plug-in +extern _cmsFormattersPluginChunkType _cmsFormattersPluginChunk; + +// Allocate and init formatters container. +void _cmsAllocFormattersPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// This chunk type is shared by TagType plug-in and MPE Plug-in +typedef struct { + + struct _cmsTagTypeLinkedList_st* TagTypes; + +} _cmsTagTypePluginChunkType; + + +// The global Context0 storage for tag types plug-in +extern _cmsTagTypePluginChunkType _cmsTagTypePluginChunk; + + +// The global Context0 storage for mult process elements plug-in +extern _cmsTagTypePluginChunkType _cmsMPETypePluginChunk; + +// Allocate and init Tag types container. +void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); +// Allocate and init MPE container. +void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); +// Container for tag plug-in +typedef struct { + + struct _cmsTagLinkedList_st* Tag; + +} _cmsTagPluginChunkType; + + +// The global Context0 storage for tag plug-in +extern _cmsTagPluginChunkType _cmsTagPluginChunk; + +// Allocate and init Tag container. +void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for intents plug-in +typedef struct { + + struct _cms_intents_list* Intents; + +} _cmsIntentsPluginChunkType; + + +// The global Context0 storage for intents plug-in +extern _cmsIntentsPluginChunkType _cmsIntentsPluginChunk; + +// Allocate and init intents container. +void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for optimization plug-in +typedef struct { + + struct _cmsOptimizationCollection_st* OptimizationCollection; + +} _cmsOptimizationPluginChunkType; + + +// The global Context0 storage for optimizers plug-in +extern _cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk; + +// Allocate and init optimizers container. +void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for transform plug-in +typedef struct { + + struct _cmsTransformCollection_st* TransformCollection; + +} _cmsTransformPluginChunkType; + +// The global Context0 storage for full-transform replacement plug-in +extern _cmsTransformPluginChunkType _cmsTransformPluginChunk; + +// Allocate and init transform container. +void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for mutex plug-in +typedef struct { + + _cmsCreateMutexFnPtrType CreateMutexPtr; + _cmsDestroyMutexFnPtrType DestroyMutexPtr; + _cmsLockMutexFnPtrType LockMutexPtr; + _cmsUnlockMutexFnPtrType UnlockMutexPtr; + +} _cmsMutexPluginChunkType; + +// The global Context0 storage for mutex plug-in +extern _cmsMutexPluginChunkType _cmsMutexPluginChunk; + +// Allocate and init mutex container. +void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// ---------------------------------------------------------------------------------- +// MLU internal representation +typedef struct { + + cmsUInt16Number Language; + cmsUInt16Number Country; + + cmsUInt32Number StrW; // Offset to current unicode string + cmsUInt32Number Len; // Length in bytes + +} _cmsMLUentry; + +struct _cms_MLU_struct { + + cmsContext ContextID; + + // The directory + cmsUInt32Number AllocatedEntries; + cmsUInt32Number UsedEntries; + _cmsMLUentry* Entries; // Array of pointers to strings allocated in MemPool + + // The Pool + cmsUInt32Number PoolSize; // The maximum allocated size + cmsUInt32Number PoolUsed; // The used size + void* MemPool; // Pointer to begin of memory pool +}; + +// Named color list internal representation +typedef struct { + + char Name[cmsMAX_PATH]; + cmsUInt16Number PCS[3]; + cmsUInt16Number DeviceColorant[cmsMAXCHANNELS]; + +} _cmsNAMEDCOLOR; + +struct _cms_NAMEDCOLORLIST_struct { + + cmsUInt32Number nColors; + cmsUInt32Number Allocated; + cmsUInt32Number ColorantCount; + + char Prefix[33]; // Prefix and suffix are defined to be 32 characters at most + char Suffix[33]; + + _cmsNAMEDCOLOR* List; + + cmsContext ContextID; +}; + + +// ---------------------------------------------------------------------------------- + +// This is the internal struct holding profile details. + +// Maximum supported tags in a profile +#define MAX_TABLE_TAG 100 + +typedef struct _cms_iccprofile_struct { + + // I/O handler + cmsIOHANDLER* IOhandler; + + // The thread ID + cmsContext ContextID; + + // Creation time + struct tm Created; + + // Only most important items found in ICC profiles + cmsUInt32Number Version; + cmsProfileClassSignature DeviceClass; + cmsColorSpaceSignature ColorSpace; + cmsColorSpaceSignature PCS; + cmsUInt32Number RenderingIntent; + + cmsUInt32Number flags; + cmsUInt32Number manufacturer, model; + cmsUInt64Number attributes; + cmsUInt32Number creator; + + cmsProfileID ProfileID; + + // Dictionary + cmsUInt32Number TagCount; + cmsTagSignature TagNames[MAX_TABLE_TAG]; + cmsTagSignature TagLinked[MAX_TABLE_TAG]; // The tag to which is linked (0=none) + cmsUInt32Number TagSizes[MAX_TABLE_TAG]; // Size on disk + cmsUInt32Number TagOffsets[MAX_TABLE_TAG]; + cmsBool TagSaveAsRaw[MAX_TABLE_TAG]; // True to write uncooked + void * TagPtrs[MAX_TABLE_TAG]; + cmsTagTypeHandler* TagTypeHandlers[MAX_TABLE_TAG]; // Same structure may be serialized on different types + // depending on profile version, so we keep track of the + // type handler for each tag in the list. + // Special + cmsBool IsWrite; + + // Keep a mutex for cmsReadTag -- Note that this only works if the user includes a mutex plugin + void * UsrMutex; + +} _cmsICCPROFILE; + +// IO helpers for profiles +cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc); +cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace); +int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks); + +// Tag types +cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig); +cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig); +cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig); + +// Error logging --------------------------------------------------------------------------------------------------------- + +void _cmsTagSignature2String(char String[5], cmsTagSignature sig); + +// Interpolation --------------------------------------------------------------------------------------------------------- + +CMSCHECKPOINT cmsInterpParams* CMSEXPORT _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags); +cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags); +CMSCHECKPOINT void CMSEXPORT _cmsFreeInterpParams(cmsInterpParams* p); +cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p); + +// Curves ---------------------------------------------------------------------------------------------------------------- + +// This struct holds information about a segment, plus a pointer to the function that implements the evaluation. +// In the case of table-based, Eval pointer is set to NULL + +// The gamma function main structure +struct _cms_curve_struct { + + cmsInterpParams* InterpParams; // Private optimizations for interpolation + + cmsUInt32Number nSegments; // Number of segments in the curve. Zero for a 16-bit based tables + cmsCurveSegment* Segments; // The segments + cmsInterpParams** SegInterp; // Array of private optimizations for interpolation in table-based segments + + cmsParametricCurveEvaluator* Evals; // Evaluators (one per segment) + + // 16 bit Table-based representation follows + cmsUInt32Number nEntries; // Number of table elements + cmsUInt16Number* Table16; // The table itself. +}; + + +// Pipelines & Stages --------------------------------------------------------------------------------------------- + +// A single stage +struct _cmsStage_struct { + + cmsContext ContextID; + + cmsStageSignature Type; // Identifies the stage + cmsStageSignature Implements; // Identifies the *function* of the stage (for optimizations) + + cmsUInt32Number InputChannels; // Input channels -- for optimization purposes + cmsUInt32Number OutputChannels; // Output channels -- for optimization purposes + + _cmsStageEvalFn EvalPtr; // Points to fn that evaluates the stage (always in floating point) + _cmsStageDupElemFn DupElemPtr; // Points to a fn that duplicates the *data* of the stage + _cmsStageFreeElemFn FreePtr; // Points to a fn that sets the *data* of the stage free + + // A generic pointer to whatever memory needed by the stage + void* Data; + + // Maintains linked list (used internally) + struct _cmsStage_struct* Next; +}; + + +// Special Stages (cannot be saved) +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID); +cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID); +cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan); +cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID); +cmsStage* _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels); + + +// For curve set only +cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe); + + +// Pipeline Evaluator (in floating point) +typedef void (* _cmsPipelineEvalFloatFn)(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const void* Data); + +struct _cmsPipeline_struct { + + cmsStage* Elements; // Points to elements chain + cmsUInt32Number InputChannels, OutputChannels; + + // Data & evaluators + void *Data; + + _cmsOPTeval16Fn Eval16Fn; + _cmsPipelineEvalFloatFn EvalFloatFn; + _cmsFreeUserDataFn FreeDataFn; + _cmsDupUserDataFn DupDataFn; + + cmsContext ContextID; // Environment + + cmsBool SaveAs8Bits; // Implementation-specific: save as 8 bits if possible +}; + +// LUT reading & creation ------------------------------------------------------------------------------------------- + +// Read tags using low-level function, provide necessary glue code to adapt versions, etc. All those return a brand new copy +// of the LUTS, since ownership of original is up to the profile. The user should free allocated resources. + +CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent); +CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent); +CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent); + +// Special values +cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile); +cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile); + +// Profile linker -------------------------------------------------------------------------------------------------- + +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +// Sequence -------------------------------------------------------------------------------------------------------- + +cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile); +cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq); +cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]); + + +// LUT optimization ------------------------------------------------------------------------------------------------ + +CMSCHECKPOINT cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples); + +cmsUInt32Number _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags); + +cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, + cmsUInt16Number **White, + cmsUInt16Number **Black, + cmsUInt32Number *nOutputs); + +cmsBool _cmsOptimizePipeline(cmsContext ContextID, + cmsPipeline** Lut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags ); + + +// Hi level LUT building ---------------------------------------------------------------------------------------------- + +cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number nGamutPCSposition, + cmsHPROFILE hGamut); + + +// Formatters ------------------------------------------------------------------------------------------------------------ + +#define cmsFLAGS_CAN_CHANGE_FORMATTER 0x02000000 // Allow change buffer format + +cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type); +cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type); + +CMSCHECKPOINT cmsFormatter CMSEXPORT _cmsGetFormatter(cmsContext ContextID, + cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags); + + +#ifndef CMS_NO_HALF_SUPPORT + +// Half float +CMSCHECKPOINT cmsFloat32Number CMSEXPORT _cmsHalf2Float(cmsUInt16Number h); +CMSCHECKPOINT cmsUInt16Number CMSEXPORT _cmsFloat2Half(cmsFloat32Number flt); + +#endif + +// Transform logic ------------------------------------------------------------------------------------------------------ + +struct _cmstransform_struct; + +typedef struct { + + // 1-pixel cache (16 bits only) + cmsUInt16Number CacheIn[cmsMAXCHANNELS]; + cmsUInt16Number CacheOut[cmsMAXCHANNELS]; + +} _cmsCACHE; + + + +// Transformation +typedef struct _cmstransform_struct { + + cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference + + // Points to transform code + _cmsTransform2Fn xform; + + // Formatters, cannot be embedded into LUT because cache + cmsFormatter16 FromInput; + cmsFormatter16 ToOutput; + + cmsFormatterFloat FromInputFloat; + cmsFormatterFloat ToOutputFloat; + + // 1-pixel cache seed for zero as input (16 bits, read only) + _cmsCACHE Cache; + + // A Pipeline holding the full (optimized) transform + cmsPipeline* Lut; + + // A Pipeline holding the gamut check. It goes from the input space to bilevel + cmsPipeline* GamutCheck; + + // Colorant tables + cmsNAMEDCOLORLIST* InputColorant; // Input Colorant table + cmsNAMEDCOLORLIST* OutputColorant; // Colorant table (for n chans > CMYK) + + // Informational only + cmsColorSpaceSignature EntryColorSpace; + cmsColorSpaceSignature ExitColorSpace; + + // White points (informative only) + cmsCIEXYZ EntryWhitePoint; + cmsCIEXYZ ExitWhitePoint; + + // Profiles used to create the transform + cmsSEQ* Sequence; + + cmsUInt32Number dwOriginalFlags; + cmsFloat64Number AdaptationState; + + // The intent of this transform. That is usually the last intent in the profilechain, but may differ + cmsUInt32Number RenderingIntent; + + // An id that uniquely identifies the running context. May be null. + cmsContext ContextID; + + // A user-defined pointer that can be used to store data for transform plug-ins + void* UserData; + _cmsFreeUserDataFn FreeUserData; + + // A way to provide backwards compatibility with full xform plugins + _cmsTransformFn OldXform; + +} _cmsTRANSFORM; + +// Copies extra channels from input to output if the original flags in the transform structure +// instructs to do so. This function is called on all standard transform functions. +void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride); + +// ----------------------------------------------------------------------------------------------------------------------- + +cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll); + +cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries); + + +#define _lcms_internal_H +#endif diff -r 52873fd3b5bd -r d544bfa4f3d5 src/share/native/sun/java2d/cmm/lcms/lcms2_plugin.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/native/sun/java2d/cmm/lcms/lcms2_plugin.h Thu Dec 07 09:11:50 2017 -0800 @@ -0,0 +1,694 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2017 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +// This is the plug-in header file. Normal LittleCMS clients should not use it. +// It is provided for plug-in writters that may want to access the support +// functions to do low level operations. All plug-in related structures +// are defined here. Including this file forces to include the standard API too. + +#ifndef _lcms_plugin_H + +// Deal with Microsoft's attempt at deprecating C standard runtime functions +#ifdef _MSC_VER +# if (_MSC_VER >= 1400) +# ifndef _CRT_SECURE_NO_DEPRECATE +# define _CRT_SECURE_NO_DEPRECATE +# endif +# ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS +# endif +# endif +#endif + +#ifndef _lcms2_H +#include "lcms2.h" +#endif + +// We need some standard C functions. +#include +#include +#include +#include +#include + + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus +extern "C" { +# endif +#endif + +// Vector & Matrix operations ----------------------------------------------------------------------- + +// Axis of the matrix/array. No specific meaning at all. +#define VX 0 +#define VY 1 +#define VZ 2 + +// Vectors +typedef struct { + cmsFloat64Number n[3]; + + } cmsVEC3; + +// 3x3 Matrix +typedef struct { + cmsVEC3 v[3]; + + } cmsMAT3; + +CMSAPI void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z); +CMSAPI void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b); +CMSAPI void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b); + +CMSAPI void CMSEXPORT _cmsMAT3identity(cmsMAT3* a); +CMSAPI cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a); +CMSAPI void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b); +CMSAPI cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b); +CMSAPI cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b); +CMSAPI void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v); + + +// Error logging ------------------------------------------------------------------------------------- + +CMSAPI void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...); + +// Memory management ---------------------------------------------------------------------------------- + +CMSAPI void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); +CMSAPI void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr); +CMSAPI void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size); + +// I/O handler ---------------------------------------------------------------------------------- + +struct _cms_io_handler { + + void* stream; // Associated stream, which is implemented differently depending on media. + + cmsContext ContextID; + cmsUInt32Number UsedSpace; + cmsUInt32Number ReportedSize; + char PhysicalFile[cmsMAX_PATH]; + + cmsUInt32Number (* Read)(struct _cms_io_handler* iohandler, void *Buffer, + cmsUInt32Number size, + cmsUInt32Number count); + cmsBool (* Seek)(struct _cms_io_handler* iohandler, cmsUInt32Number offset); + cmsBool (* Close)(struct _cms_io_handler* iohandler); + cmsUInt32Number (* Tell)(struct _cms_io_handler* iohandler); + cmsBool (* Write)(struct _cms_io_handler* iohandler, cmsUInt32Number size, + const void* Buffer); +}; + +// Endianness adjust functions +CMSAPI cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word); +CMSAPI cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number Value); +CMSAPI void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord); + +// Helper IO functions +CMSAPI cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array); + +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array); + +// ICC base tag +typedef struct { + cmsTagTypeSignature sig; + cmsInt8Number reserved[4]; + +} _cmsTagBase; + +// Type base helper functions +CMSAPI cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io); +CMSAPI cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig); + +// Alignment functions +CMSAPI cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io); +CMSAPI cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io); + +// To deal with text streams. 2K at most +CMSAPI cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...); + +// Fixed point helper functions +CMSAPI cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8); +CMSAPI cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val); + +CMSAPI cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32); +CMSAPI cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v); + +// Date/time helper functions +CMSAPI void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source); +CMSAPI void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest); + +//---------------------------------------------------------------------------------------------------------- + +// Shared callbacks for user data +typedef void (* _cmsFreeUserDataFn)(cmsContext ContextID, void* Data); +typedef void* (* _cmsDupUserDataFn)(cmsContext ContextID, const void* Data); + +//---------------------------------------------------------------------------------------------------------- + +// Plug-in foundation +#define cmsPluginMagicNumber 0x61637070 // 'acpp' + +#define cmsPluginMemHandlerSig 0x6D656D48 // 'memH' +#define cmsPluginInterpolationSig 0x696E7048 // 'inpH' +#define cmsPluginParametricCurveSig 0x70617248 // 'parH' +#define cmsPluginFormattersSig 0x66726D48 // 'frmH +#define cmsPluginTagTypeSig 0x74797048 // 'typH' +#define cmsPluginTagSig 0x74616748 // 'tagH' +#define cmsPluginRenderingIntentSig 0x696E7448 // 'intH' +#define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH' +#define cmsPluginOptimizationSig 0x6F707448 // 'optH' +#define cmsPluginTransformSig 0x7A666D48 // 'xfmH' +#define cmsPluginMutexSig 0x6D747A48 // 'mtxH' + +typedef struct _cmsPluginBaseStruct { + + cmsUInt32Number Magic; // 'acpp' signature + cmsUInt32Number ExpectedVersion; // Expected version of LittleCMS + cmsUInt32Number Type; // Type of plug-in + struct _cmsPluginBaseStruct* Next; // For multiple plugin definition. NULL for end of list. + +} cmsPluginBase; + +// Maximum number of types in a plugin array +#define MAX_TYPES_IN_LCMS_PLUGIN 20 + +//---------------------------------------------------------------------------------------------------------- + +// Memory handler. Each new plug-in type replaces current behaviour + +typedef void* (* _cmsMallocFnPtrType)(cmsContext ContextID, cmsUInt32Number size); +typedef void (* _cmsFreeFnPtrType)(cmsContext ContextID, void *Ptr); +typedef void* (* _cmsReallocFnPtrType)(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); + +typedef void* (* _cmsMalloZerocFnPtrType)(cmsContext ContextID, cmsUInt32Number size); +typedef void* (* _cmsCallocFnPtrType)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); +typedef void* (* _cmsDupFnPtrType)(cmsContext ContextID, const void* Org, cmsUInt32Number size); + +typedef struct { + + cmsPluginBase base; + + // Required + _cmsMallocFnPtrType MallocPtr; + _cmsFreeFnPtrType FreePtr; + _cmsReallocFnPtrType ReallocPtr; + + // Optional + _cmsMalloZerocFnPtrType MallocZeroPtr; + _cmsCallocFnPtrType CallocPtr; + _cmsDupFnPtrType DupPtr; + +} cmsPluginMemHandler; + + +// ------------------------------------------------------------------------------------------------------------------ + +// Interpolation. 16 bits and floating point versions. +struct _cms_interp_struc; + +// Interpolation callbacks + +// 16 bits forward interpolation. This function performs precision-limited linear interpolation +// and is supposed to be quite fast. Implementation may be tetrahedral or trilinear, and plug-ins may +// choose to implement any other interpolation algorithm. +typedef void (* _cmsInterpFn16)(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const struct _cms_interp_struc* p); + +// Floating point forward interpolation. Full precision interpolation using floats. This is not a +// time critical function. Implementation may be tetrahedral or trilinear, and plug-ins may +// choose to implement any other interpolation algorithm. +typedef void (* _cmsInterpFnFloat)(cmsFloat32Number const Input[], + cmsFloat32Number Output[], + const struct _cms_interp_struc* p); + + + +// This type holds a pointer to an interpolator that can be either 16 bits or float +typedef union { + _cmsInterpFn16 Lerp16; // Forward interpolation in 16 bits + _cmsInterpFnFloat LerpFloat; // Forward interpolation in floating point +} cmsInterpFunction; + +// Flags for interpolator selection +#define CMS_LERP_FLAGS_16BITS 0x0000 // The default +#define CMS_LERP_FLAGS_FLOAT 0x0001 // Requires different implementation +#define CMS_LERP_FLAGS_TRILINEAR 0x0100 // Hint only + + +#define MAX_INPUT_DIMENSIONS 8 + +typedef struct _cms_interp_struc { // Used on all interpolations. Supplied by lcms2 when calling the interpolation function + + cmsContext ContextID; // The calling thread + + cmsUInt32Number dwFlags; // Keep original flags + cmsUInt32Number nInputs; // != 1 only in 3D interpolation + cmsUInt32Number nOutputs; // != 1 only in 3D interpolation + + cmsUInt32Number nSamples[MAX_INPUT_DIMENSIONS]; // Valid on all kinds of tables + cmsUInt32Number Domain[MAX_INPUT_DIMENSIONS]; // Domain = nSamples - 1 + + cmsUInt32Number opta[MAX_INPUT_DIMENSIONS]; // Optimization for 3D CLUT. This is the number of nodes premultiplied for each + // dimension. For example, in 7 nodes, 7, 7^2 , 7^3, 7^4, etc. On non-regular + // Samplings may vary according of the number of nodes for each dimension. + + const void *Table; // Points to the actual interpolation table + cmsInterpFunction Interpolation; // Points to the function to do the interpolation + + } cmsInterpParams; + +// Interpolators factory +typedef cmsInterpFunction (* cmsInterpFnFactory)(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); + +// The plug-in +typedef struct { + cmsPluginBase base; + + // Points to a user-supplied function which implements the factory + cmsInterpFnFactory InterpolatorsFactory; + +} cmsPluginInterpolation; + +//---------------------------------------------------------------------------------------------------------- + +// Parametric curves. A negative type means same function but analytically inverted. Max. number of params is 10 + +// Evaluator callback for user-supplied parametric curves. May implement more than one type +typedef cmsFloat64Number (* cmsParametricCurveEvaluator)(cmsInt32Number Type, const cmsFloat64Number Params[10], cmsFloat64Number R); + +// Plug-in may implement an arbitrary number of parametric curves +typedef struct { + cmsPluginBase base; + + cmsUInt32Number nFunctions; // Number of supported functions + cmsUInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types + cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function + + cmsParametricCurveEvaluator Evaluator; // The evaluator + +} cmsPluginParametricCurves; +//---------------------------------------------------------------------------------------------------------- + +// Formatters. This plug-in adds new handlers, replacing them if they already exist. Formatters dealing with +// cmsFloat32Number (bps = 4) or double (bps = 0) types are requested via FormatterFloat callback. Others come across +// Formatter16 callback + +struct _cmstransform_struct; + +typedef cmsUInt8Number* (* cmsFormatter16)(register struct _cmstransform_struct* CMMcargo, + register cmsUInt16Number Values[], + register cmsUInt8Number* Buffer, + register cmsUInt32Number Stride); + +typedef cmsUInt8Number* (* cmsFormatterFloat)(struct _cmstransform_struct* CMMcargo, + cmsFloat32Number Values[], + cmsUInt8Number* Buffer, + cmsUInt32Number Stride); + +// This type holds a pointer to a formatter that can be either 16 bits or cmsFloat32Number +typedef union { + cmsFormatter16 Fmt16; + cmsFormatterFloat FmtFloat; + +} cmsFormatter; + +#define CMS_PACK_FLAGS_16BITS 0x0000 +#define CMS_PACK_FLAGS_FLOAT 0x0001 + +typedef enum { cmsFormatterInput=0, cmsFormatterOutput=1 } cmsFormatterDirection; + +typedef cmsFormatter (* cmsFormatterFactory)(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags); // precision + +// Plug-in may implement an arbitrary number of formatters +typedef struct { + cmsPluginBase base; + cmsFormatterFactory FormattersFactory; + +} cmsPluginFormatters; + +//---------------------------------------------------------------------------------------------------------- + +// Tag type handler. Each type is free to return anything it wants, and it is up to the caller to +// know in advance what is the type contained in the tag. +typedef struct _cms_typehandler_struct { + + cmsTagTypeSignature Signature; // The signature of the type + + // Allocates and reads items + void * (* ReadPtr)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number* nItems, + cmsUInt32Number SizeOfTag); + + // Writes n Items + cmsBool (* WritePtr)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Ptr, + cmsUInt32Number nItems); + + // Duplicate an item or array of items + void* (* DupPtr)(struct _cms_typehandler_struct* self, + const void *Ptr, + cmsUInt32Number n); + + // Free all resources + void (* FreePtr)(struct _cms_typehandler_struct* self, + void *Ptr); + + // Additional parameters used by the calling thread + cmsContext ContextID; + cmsUInt32Number ICCVersion; + +} cmsTagTypeHandler; + +// Each plug-in implements a single type +typedef struct { + cmsPluginBase base; + cmsTagTypeHandler Handler; + +} cmsPluginTagType; + +//---------------------------------------------------------------------------------------------------------- + +// This is the tag plugin, which identifies tags. For writing, a pointer to function is provided. +// This function should return the desired type for this tag, given the version of profile +// and the data being serialized. +typedef struct { + + cmsUInt32Number ElemCount; // If this tag needs an array, how many elements should keep + + // For reading. + cmsUInt32Number nSupportedTypes; // In how many types this tag can come (MAX_TYPES_IN_LCMS_PLUGIN maximum) + cmsTagTypeSignature SupportedTypes[MAX_TYPES_IN_LCMS_PLUGIN]; + + // For writing + cmsTagTypeSignature (* DecideType)(cmsFloat64Number ICCVersion, const void *Data); + +} cmsTagDescriptor; + +// Plug-in implements a single tag +typedef struct { + cmsPluginBase base; + + cmsTagSignature Signature; + cmsTagDescriptor Descriptor; + +} cmsPluginTag; + +//---------------------------------------------------------------------------------------------------------- + +// Custom intents. This function should join all profiles specified in the array in +// a single LUT. Any custom intent in the chain redirects to custom function. If more than +// one custom intent is found, the one located first is invoked. Usually users should use only one +// custom intent, so mixing custom intents in same multiprofile transform is not supported. + +typedef cmsPipeline* (* cmsIntentFn)( cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +// Each plug-in defines a single intent number. +typedef struct { + cmsPluginBase base; + cmsUInt32Number Intent; + cmsIntentFn Link; + char Description[256]; + +} cmsPluginRenderingIntent; + + +// The default ICC intents (perceptual, saturation, rel.col and abs.col) +CMSAPI cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +//---------------------------------------------------------------------------------------------------------- + +// Pipelines, Multi Process Elements. + +typedef void (* _cmsStageEvalFn) (const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage* mpe); +typedef void*(* _cmsStageDupElemFn) (cmsStage* mpe); +typedef void (* _cmsStageFreeElemFn) (cmsStage* mpe); + + +// This function allocates a generic MPE +CMSAPI cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, + cmsStageSignature Type, + cmsUInt32Number InputChannels, + cmsUInt32Number OutputChannels, + _cmsStageEvalFn EvalPtr, // Points to fn that evaluates the element (always in floating point) + _cmsStageDupElemFn DupElemPtr, // Points to a fn that duplicates the stage + _cmsStageFreeElemFn FreePtr, // Points to a fn that sets the element free + void* Data); // A generic pointer to whatever memory needed by the element +typedef struct { + cmsPluginBase base; + cmsTagTypeHandler Handler; + +} cmsPluginMultiProcessElement; + + +// Data kept in "Element" member of cmsStage + +// Curves +typedef struct { + cmsUInt32Number nCurves; + cmsToneCurve** TheCurves; + +} _cmsStageToneCurvesData; + +// Matrix +typedef struct { + cmsFloat64Number* Double; // floating point for the matrix + cmsFloat64Number* Offset; // The offset + +} _cmsStageMatrixData; + +// CLUT +typedef struct { + + union { // Can have only one of both representations at same time + cmsUInt16Number* T; // Points to the table 16 bits table + cmsFloat32Number* TFloat; // Points to the cmsFloat32Number table + + } Tab; + + cmsInterpParams* Params; + cmsUInt32Number nEntries; + cmsBool HasFloatValues; + +} _cmsStageCLutData; + + +//---------------------------------------------------------------------------------------------------------- +// Optimization. Using this plug-in, additional optimization strategies may be implemented. +// The function should return TRUE if any optimization is done on the LUT, this terminates +// the optimization search. Or FALSE if it is unable to optimize and want to give a chance +// to the rest of optimizers. + +typedef void (* _cmsOPTeval16Fn)(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* Data); + + +typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + +// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional +// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. + +CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, + _cmsOPTeval16Fn Eval16, + void* PrivateData, + _cmsFreeUserDataFn FreePrivateDataFn, + _cmsDupUserDataFn DupPrivateDataFn); + +typedef struct { + cmsPluginBase base; + + // Optimize entry point + _cmsOPToptimizeFn OptimizePtr; + +} cmsPluginOptimization; + +//---------------------------------------------------------------------------------------------------------- +// Full xform + +typedef struct { + cmsUInt32Number BytesPerLineIn; + cmsUInt32Number BytesPerLineOut; + cmsUInt32Number BytesPerPlaneIn; + cmsUInt32Number BytesPerPlaneOut; + +} cmsStride; + +typedef void (* _cmsTransformFn)(struct _cmstransform_struct *CMMcargo, // Legacy function, handles just ONE scanline. + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size, + cmsUInt32Number Stride); // Stride in bytes to the next plana in planar formats + + +typedef void (*_cmsTransform2Fn)(struct _cmstransform_struct *CMMcargo, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride); + +typedef cmsBool (* _cmsTransformFactory)(_cmsTransformFn* xform, + void** UserData, + _cmsFreeUserDataFn* FreePrivateDataFn, + cmsPipeline** Lut, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + +typedef cmsBool (* _cmsTransform2Factory)(_cmsTransform2Fn* xform, + void** UserData, + _cmsFreeUserDataFn* FreePrivateDataFn, + cmsPipeline** Lut, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + + +// Retrieve user data as specified by the factory +CMSAPI void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn); +CMSAPI void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo); + + +// Retrieve formatters +CMSAPI void CMSEXPORT _cmsGetTransformFormatters16 (struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput); +CMSAPI void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput); + +typedef struct { + cmsPluginBase base; + + // Transform entry point + union { + _cmsTransformFactory legacy_xform; + _cmsTransform2Factory xform; + } factories; + +} cmsPluginTransform; + +//---------------------------------------------------------------------------------------------------------- +// Mutex + +typedef void* (* _cmsCreateMutexFnPtrType)(cmsContext ContextID); +typedef void (* _cmsDestroyMutexFnPtrType)(cmsContext ContextID, void* mtx); +typedef cmsBool (* _cmsLockMutexFnPtrType)(cmsContext ContextID, void* mtx); +typedef void (* _cmsUnlockMutexFnPtrType)(cmsContext ContextID, void* mtx); + +typedef struct { + cmsPluginBase base; + + _cmsCreateMutexFnPtrType CreateMutexPtr; + _cmsDestroyMutexFnPtrType DestroyMutexPtr; + _cmsLockMutexFnPtrType LockMutexPtr; + _cmsUnlockMutexFnPtrType UnlockMutexPtr; + +} cmsPluginMutex; + +CMSAPI void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID); +CMSAPI void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx); +CMSAPI cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx); +CMSAPI void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx); + + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus + } +# endif +#endif + +#define _lcms_plugin_H +#endif