On 18/12/2006, at 22:25, Garreth McDaid wrote: > I'd like to be able to use the functionality provided by icctrans > in PHP > ie take 4 CMYK color channel values and convert them to RGB color > channel values with reference to a CMYK and sRGB ICC profile. Once I > have this, I can compile it into PHP.
The code below might help. Execute program like: ./icc-translate 0 0 0 EuroscaleCoated.icc Adobe-sRGB.icc 3 - 1.0 1.0 0 0 output: ## R=18.07 G=11.20 B=52.42 ## arg 1: 0 = silent or 1 = verbose arg 2: Intent (0=Perceptual, 1=Colorimetric, 2=Saturation, 3=Absolute) arg 3: WhiteBlackCompensation (0=False, 1=True) arg 4: path to input profile arg 5: path to output profile arg 6: proof intent arg 7: path to proof profile, or "-" if not proofing arg 8... input colors, can specify multiple, each value is fraction from zero to one (1 == 100%) for cmyk, 1.0 1.0 0 0 is 100% cyan, 100% yellow, 0% magenta and 0% black Output is percentage, for some unknown reason... I compile it using: CFLAGS=-I/usr/local/lcms/include LDFLAGS=-L/usr/local/lcms/lib gcc -std=c99 $CFLAGS $LDFLAGS -l lcms -o icc-translate icc-translate.c /* Little cms Copyright (C) 1998-2001 Marti Maria THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "lcms.h" #include <stdarg.h> #include <ctype.h> /* ------------------------------------------------------------------------ */ static int Verbose = 0; static int gArgc = 0; static int gArgIdx = 0; static char **gArgv = NULL; static char *cInProf = NULL; static char *cOutProf = NULL; static char *cProofing = NULL; static double outputScale = 255; static int Intent = INTENT_PERCEPTUAL; static int ProofingIntent = INTENT_PERCEPTUAL; static BOOL WhiteBlackCompensation = FALSE; static cmsHPROFILE hInput = NULL; static cmsHPROFILE hOutput = NULL; static cmsHPROFILE hProof = NULL; static cmsHPROFILE hLab = NULL; static cmsHPROFILE hXYZ = NULL; static cmsHTRANSFORM hTrans, hTransXYZ, hTransLab; static icColorSpaceSignature InputColorSpace, OutputColorSpace; static cmsCIEXYZ xyz; static cmsCIELab Lab; #define xisatty(x) isatty( fileno( (x) ) ) #ifndef stricmp #define stricmp strcasecmp #endif static void FatalError(const char *frm,...) { va_list args; va_start(args, frm); vfprintf(stderr, frm, args); va_end(args); printf("Usage: icc-translate\n" " <bool> verbose (0 for false, 1 for true)\n" " <int> intent: 0=Perceptual, 1=Colorimetric, 2=Saturation, 3=Absolute\n" " <bool> black/white compensation\n" " <path> input profile\n" " <path> output profile\n" " <int> proofing intent\n" " <path> proofing profile, '-' for none\n" " <double> color values, floating point from 0 to 1\n" "\n" "Examples:\n" " convert black, gray and white from sRGB to CMYK:\n" " icc-translate 0 3 0 Adobe-sRGB.icc EuroscaleCoated.icc 0 - 0 0 0 0.5 0.5 0.5 1 1 1\n" "\n" " convert cmyk from Euroscale to Lanie printer, proofing Burda:\n" " icc-translate 0 3 0 EuroscaleCoated.icc Lanier.icc \\\n" " 3 burda2001v3.icc 0 0 0 0 0 0 0 1 0 0 1 0\n" ); exit(1); } static char* NextArg() { if (gArgIdx + 1 >= gArgc) FatalError("Too few arguments\n"); return gArgv[++gArgIdx]; } static cmsHPROFILE OpenProfile(const char *File) { if (!File) return cmsCreate_sRGBProfile(); if (stricmp(File, "*Lab") == 0) return cmsCreateLabProfile(NULL); if (stricmp(File, "*LabD65") == 0) { cmsCIExyY D65xyY; cmsWhitePointFromTemp(6504, &D65xyY); return cmsCreateLabProfile(&D65xyY); } if (stricmp(File, "*XYZ") == 0) return cmsCreateXYZProfile(); cmsHPROFILE h = cmsOpenProfileFromFile(File, "r"); if (h == NULL) FatalError("Can't open profile '%s'", File); return h; } static void OpenTransforms(void) { DWORD dwFlags = 0; dwFlags |= cmsFLAGS_NOTPRECALC; hInput = OpenProfile(cInProf); hOutput = OpenProfile(cOutProf); if (cProofing != NULL && *cProofing && strcmp(cProofing, "-")) { hProof = cmsOpenProfileFromFile(cProofing, "r"); dwFlags |= cmsFLAGS_SOFTPROOFING; } InputColorSpace = cmsGetColorSpace(hInput); OutputColorSpace = cmsGetColorSpace(hOutput); if (cmsGetDeviceClass(hInput) == icSigLinkClass || cmsGetDeviceClass(hOutput) == icSigLinkClass) FatalError("Use some flag for devicelink profiles!\n"); hXYZ = cmsCreateXYZProfile(); hLab = cmsCreateLabProfile(NULL); if (Verbose) { printf("From: %s\n", cmsTakeProductName(hInput)); if (hOutput) printf("To: %s\n", cmsTakeProductName(hOutput)); if (hProof) printf("Proof: %s\n", cmsTakeProductName(hProof)); printf("\n"); } DWORD dwIn = BYTES_SH(2) | CHANNELS_SH(_cmsChannelsOf (InputColorSpace)); DWORD dwOut = BYTES_SH(2) | CHANNELS_SH(_cmsChannelsOf (OutputColorSpace)); if (WhiteBlackCompensation) dwFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION; if (hProof) { hTrans = cmsCreateProofingTransform(hInput, dwIn, hOutput, dwOut, hProof, Intent, ProofingIntent, dwFlags); } else { hTrans = cmsCreateTransform(hInput, dwIn, hOutput, dwOut, Intent, dwFlags); } if (Verbose) { hTransXYZ = cmsCreateTransform(hInput, dwIn, hXYZ, TYPE_XYZ_16, Intent, cmsFLAGS_NOTPRECALC); hTransLab = cmsCreateTransform(hInput, dwIn, hLab, TYPE_Lab_16, Intent, cmsFLAGS_NOTPRECALC); } } static void CloseTransforms(void) { cmsDeleteTransform(hTrans); if (hTransLab) cmsDeleteTransform(hTransLab); if (hTransXYZ) cmsDeleteTransform(hTransXYZ); cmsCloseProfile(hInput); if (hOutput) cmsCloseProfile(hOutput); cmsCloseProfile(hXYZ); cmsCloseProfile(hLab); } static void PrintOne(const char *C, double v) { v /= 257.; v /= 255; v *= outputScale; printf("%s=%.2f ", C, v); } static void PrintCooked(const char *C, double v) { printf("%s=%.4f ", C, v); } static void PrintResults(WORD Encoded[], icColorSpaceSignature ColorSpace) { switch (ColorSpace) { case icSigXYZData: cmsXYZEncoded2Float(&xyz, Encoded); PrintCooked("X", xyz.X * 100.); PrintCooked("Y", xyz.Y * 100.); PrintCooked("Z", xyz.Z * 100.); break; case icSigLabData: cmsLabEncoded2Float(&Lab, Encoded); PrintCooked("L*", Lab.L); PrintCooked("a*", Lab.a); PrintCooked("b*", Lab.b); break; case icSigLuvData: PrintOne("L", Encoded[0]); PrintOne("u", Encoded[1]); PrintOne("v", Encoded[2]); break; case icSigYCbCrData: PrintOne("Y", Encoded[0]); PrintOne("Cb", Encoded[1]); PrintOne("Cr", Encoded[2]); break; case icSigYxyData: PrintOne("Y", Encoded[0]); PrintOne("x", Encoded[1]); PrintOne("y", Encoded[2]); break; case icSigRgbData: PrintOne("R", Encoded[0]); PrintOne("G", Encoded[1]); PrintOne("B", Encoded[2]); break; case icSigGrayData: PrintOne("L", Encoded[0]); break; case icSigHsvData: PrintOne("H", Encoded[0]); PrintOne("s", Encoded[1]); PrintOne("v", Encoded[2]); break; case icSigHlsData: PrintOne("H", Encoded[0]); PrintOne("l", Encoded[1]); PrintOne("s", Encoded[2]); break; case icSigCmykData: PrintOne("C", Encoded[0]); PrintOne("M", Encoded[1]); PrintOne("Y", Encoded[2]); PrintOne("K", Encoded[3]); break; case icSigCmyData: PrintOne("C", Encoded[0]); PrintOne("M", Encoded[1]); PrintOne("Y", Encoded[2]); break; case icSig6colorData: PrintOne("C", Encoded[0]); PrintOne("M", Encoded[1]); PrintOne("Y", Encoded[2]); PrintOne("K", Encoded[3]); PrintOne("c", Encoded[1]); PrintOne("m", Encoded[2]); break; default: for (int i = 0; i < _cmsChannelsOf(OutputColorSpace); i++) { char Buffer[10]; sprintf(Buffer, "CHAN%d", i + 1); PrintOne(Buffer, Encoded[i]); } } } static int GetVal(const char *AskFor) { double v1 = atof(NextArg()); double v2 = v1 * 255.0 * 257.0; int v = (int) v2; if (Verbose) { PrintOne("GetVal", v); printf(" GetVal(%s) returns %.2f = %.2f = %d\n", AskFor, v1, v2, v); } return v2; } static double GetDbl(const char *AskFor) { return atof(NextArg()); } static void TakeValues(WORD Encoded[]) { switch (InputColorSpace) { case icSigXYZData: xyz.X = GetDbl("X"); xyz.Y = GetDbl("Y"); xyz.Z = GetDbl("Z"); cmsFloat2XYZEncoded(Encoded, &xyz); break; case icSigLabData: Lab.L = GetDbl("L*"); Lab.a = GetDbl("a*"); Lab.b = GetDbl("b*"); cmsFloat2LabEncoded(Encoded, &Lab); break; case icSigLuvData: Encoded[0] = GetVal("L"); Encoded[1] = GetVal("u"); Encoded[2] = GetVal("v"); break; case icSigYCbCrData: Encoded[0] = GetVal("Y"); Encoded[1] = GetVal("Cb"); Encoded[2] = GetVal("Cr"); break; case icSigYxyData: Encoded[0] = GetVal("Y"); Encoded[1] = GetVal("x"); Encoded[2] = GetVal("y"); break; case icSigRgbData: Encoded[0] = GetVal("R"); Encoded[1] = GetVal("G"); Encoded[2] = GetVal("B"); break; case icSigGrayData: Encoded[0] = GetVal("L"); break; case icSigHsvData: Encoded[0] = GetVal("H"); Encoded[1] = GetVal("s"); Encoded[2] = GetVal("v"); break; case icSigHlsData: Encoded[0] = GetVal("H"); Encoded[1] = GetVal("l"); Encoded[2] = GetVal("s"); break; case icSigCmykData: Encoded[0] = GetVal("C"); Encoded[1] = GetVal("M"); Encoded[2] = GetVal("Y"); Encoded[3] = GetVal("K"); break; case icSigCmyData: Encoded[0] = GetVal("C"); Encoded[1] = GetVal("M"); Encoded[2] = GetVal("Y"); break; case icSig6colorData: FatalError("Hexachrome separations unsupported on input"); break; default: FatalError("Unsupported %d channel profile", _cmsChannelsOf(InputColorSpace)); } } int main(int argc, char *argv[]) { WORD Input[MAXCHANNELS], Output[MAXCHANNELS], PCSLab [MAXCHANNELS], PCSxyz[MAXCHANNELS]; gArgc = argc; gArgv = argv; gArgIdx = 0; Verbose = atoi(NextArg()); Intent = atoi(NextArg()); if (Intent > 3 || Intent < 0) FatalError("Invalid intent"); WhiteBlackCompensation = atoi(NextArg()) != 0; cInProf = NextArg(); cOutProf = NextArg(); ProofingIntent = atoi(NextArg()); if (ProofingIntent > 3 || ProofingIntent < 0) FatalError("Invalid ProofingIntent"); cProofing = NextArg(); OpenTransforms(); while (gArgIdx + 1 < gArgc) { TakeValues(Input); cmsDoTransform(hTrans, Input, Output, 1); if (hTransXYZ) cmsDoTransform(hTransXYZ, Input, PCSxyz, 1); if (hTransLab) cmsDoTransform(hTransLab, Input, PCSLab, 1); outputScale = 100; printf("## "); PrintResults(Output, OutputColorSpace); printf(" ##\n"); if (Verbose) { outputScale = 255; printf("scale255: "); PrintResults(Output, OutputColorSpace); printf("\n"); } if (Verbose && hTransXYZ && hTransLab) { PrintResults(PCSxyz, icSigXYZData); printf("\n"); PrintResults(PCSLab, icSigLabData); printf("\n"); } } return 0; } ---- - Peter Speck ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys - and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ Lcms-user mailing list Lcms-user@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lcms-user