diff options
author | Marti Maria <info@littlecms.com> | 2010-03-16 14:44:25 +0100 |
---|---|---|
committer | Marti Maria <info@littlecms.com> | 2010-03-16 14:44:25 +0100 |
commit | c3934910e193c76237850b5a904d47d5a3946c0f (patch) | |
tree | fd73e73c523ef77dc8dd886b4eea0189db8e24b5 /utils | |
parent | bda1fa929aea10ce50f37aeaf72b579630b07c14 (diff) | |
download | lcms2-c3934910e193c76237850b5a904d47d5a3946c0f.tar.gz |
Added MatLab support
Diffstat (limited to 'utils')
-rwxr-xr-x | utils/matlab/icctrans.c | 689 | ||||
-rwxr-xr-x | utils/matlab/lcms_rsp | 27 |
2 files changed, 716 insertions, 0 deletions
diff --git a/utils/matlab/icctrans.c b/utils/matlab/icctrans.c new file mode 100755 index 0000000..d7cd7d8 --- /dev/null +++ b/utils/matlab/icctrans.c @@ -0,0 +1,689 @@ +// +// Little cms +// Copyright (C) 1998-2010 Marti Maria, Ignacio Ruiz de Conejo +// +// 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 "mex.h" + +#include "lcms2.h" +#include "string.h" +#include "stdarg.h" + +// xgetopt() interface ----------------------------------------------------- + +static int xoptind; +static char *xoptarg; +static int xopterr; +static char *letP; +static char SW = '-'; + +// ------------------------------------------------------------------------ + + +static int Verbose ; // Print some statistics +static char *cInProf; // Input profile +static char *cOutProf; // Output profile +static char *cProofing; // Softproofing profile + + +static int Intent; // Rendering Intent +static int ProofingIntent; // RI for proof + +static int PrecalcMode; // 0 = Not, 1=Normal, 2=Accurate, 3=Fast + +static cmsBool BlackPointCompensation; +static cmsBool lIsDeviceLink; +static cmsBool lMultiProfileChain; // Multiple profile chain + +static cmsHPROFILE hInput, hOutput, hProof; +static cmsHTRANSFORM hColorTransform; +static cmsHPROFILE hProfiles[255]; +static int nProfiles; + +static cmsColorSpaceSignature InputColorSpace, OutputColorSpace; +static int OutputChannels, InputChannels, nBytesDepth; + + +// Error. Print error message and abort + +static +cmsBool FatalError(const char *frm, ...) +{ + va_list args; + char Buffer[1024]; + + va_start(args, frm); + vsprintf(Buffer, frm, args); + mexErrMsgTxt(Buffer); + va_end(args); + + return FALSE; +} + +// This is the handler passed to lcms + +static +void MatLabErrorHandler(cmsContext ContextID, cmsUInt32Number ErrorCode, + const char *Text) +{ + mexErrMsgTxt(Text); +} +// +// Parse the command line options, System V style. +// + +static +void xoptinit() +{ + xoptind = 1; + xopterr = 0; + letP = NULL; +} + + +static +int xgetopt(int argc, char *argv[], char *optionS) +{ + unsigned char ch; + char *optP; + + if (SW == 0) { + SW = '/'; + } + + if (argc > xoptind) { + if (letP == NULL) { + if ((letP = argv[xoptind]) == NULL || + *(letP++) != SW) goto gopEOF; + if (*letP == SW) { + xoptind++; goto gopEOF; + } + } + if (0 == (ch = *(letP++))) { + xoptind++; goto gopEOF; + } + if (':' == ch || (optP = strchr(optionS, ch)) == NULL) + goto gopError; + if (':' == *(++optP)) { + xoptind++; + if (0 == *letP) { + if (argc <= xoptind) goto gopError; + letP = argv[xoptind++]; + } + xoptarg = letP; + letP = NULL; + } else { + if (0 == *letP) { + xoptind++; + letP = NULL; + } + xoptarg = NULL; + } + return ch; + } +gopEOF: + xoptarg = letP = NULL; + return EOF; + +gopError: + xoptarg = NULL; + if (xopterr) + FatalError ("get command line option"); + return ('?'); +} + + +// Return Mathlab type by depth + +static +size_t SizeOfArrayType(const mxArray *Array) +{ + + switch (mxGetClassID(Array)) { + + case mxINT8_CLASS: return 1; + case mxUINT8_CLASS: return 1; + case mxINT16_CLASS: return 2; + case mxUINT16_CLASS: return 2; + case mxSINGLE_CLASS: return 4; + case mxDOUBLE_CLASS: return 0; // Special case -- lcms handles double as size=0 + + + default: + FatalError("Unsupported data type"); + return 0; + } +} + + +// Get number of pixels of input array. Supported arrays are +// organized as NxMxD, being N and M the size of image and D the +// number of components. + +static +size_t GetNumberOfPixels(const mxArray* In) +{ + int nDimensions = mxGetNumberOfDimensions(In); + const int *Dimensions = mxGetDimensions(In); + + switch (nDimensions) { + + case 1: return 1; // It is just a spot color + case 2: return Dimensions[0]; // A scanline + case 3: return Dimensions[0]*Dimensions[1]; // A image + + default: + FatalError("Unsupported array of %d dimensions", nDimensions); + return 0; + } +} + + +// Allocates the output array. Copies the input array modifying the pixel +// definition to match "OutputChannels". + +static +mxArray* AllocateOutputArray(const mxArray* In, int OutputChannels) +{ + + mxArray* Out = mxDuplicateArray(In); // Make a "deep copy" of Input array + int nDimensions = mxGetNumberOfDimensions(In); + const int* Dimensions = mxGetDimensions(In); + int InputChannels = Dimensions[nDimensions-1]; + + + // Modify pixel size only if needed + + if (InputChannels != OutputChannels) { + + + int i, NewSize; + int *ModifiedDimensions = (int*) mxMalloc(nDimensions * sizeof(int)); + + + mexPrintf("Pepitez nOut=%d\n", OutputChannels); + + memmove(ModifiedDimensions, Dimensions, nDimensions * sizeof(int)); + ModifiedDimensions[nDimensions - 1] = OutputChannels; + + switch (mxGetClassID(In)) { + + case mxINT8_CLASS: NewSize = sizeof(char); break; + case mxUINT8_CLASS: NewSize = sizeof(unsigned char); break; + case mxINT16_CLASS: NewSize = sizeof(short); break; + case mxUINT16_CLASS: NewSize = sizeof(unsigned short); break; + + default: + case mxDOUBLE_CLASS: NewSize = sizeof(double); break; + } + + + // NewSize = 1; + for (i=0; i < nDimensions; i++) + NewSize *= ModifiedDimensions[i]; + + + mxSetDimensions(Out, ModifiedDimensions, nDimensions); + mxFree(ModifiedDimensions); + + mxSetPr(Out, mxRealloc(mxGetPr(Out), NewSize)); + + } + + + return Out; +} + + + +// Does create a format descriptor. "Bytes" is the sizeof type in bytes +// +// Bytes Meaning +// ------ -------- +// 0 Floating point (double) +// 1 8-bit samples +// 2 16-bit samples + +static +cmsUInt32Number MakeFormatDescriptor(cmsColorSpaceSignature ColorSpace, int Bytes) +{ + int Channels = cmsChannelsOf(ColorSpace); + return COLORSPACE_SH(_cmsLCMScolorSpace(ColorSpace))|BYTES_SH(Bytes)|CHANNELS_SH(Channels)|PLANAR_SH(1); +} + + +// Opens a profile or proper built-in + +static +cmsHPROFILE OpenProfile(const char* File) +{ + + if (!File) + return cmsCreate_sRGBProfile(); + + if (cmsstrcasecmp(File, "*sRGB") == 0) + return cmsCreate_sRGBProfile(); + + if (cmsstrcasecmp(File, "*Lab") == 0) + return cmsCreateLab4Profile(NULL); + + if (cmsstrcasecmp(File, "*LabD65") == 0) { + + cmsCIExyY D65xyY; + + cmsWhitePointFromTemp(&D65xyY, 6504); + return cmsCreateLab4Profile(&D65xyY); + } + + if (cmsstrcasecmp(File, "*XYZ") == 0) + return cmsCreateXYZProfile(); + + if (cmsstrcasecmp(File, "*Gray22") == 0) { + cmsToneCurve* Gamma = cmsBuildGamma(NULL, 2.2); + cmsHPROFILE hProfile = cmsCreateGrayProfile(cmsD50_xyY(), Gamma); + cmsFreeToneCurve(Gamma); + return hProfile; + + } + + return cmsOpenProfileFromFile(File, "r"); +} + + +static +cmsUInt32Number GetFlags() +{ + cmsUInt32Number dwFlags = 0; + + switch (PrecalcMode) { + + case 0: dwFlags = cmsFLAGS_NOOPTIMIZE; break; + case 2: dwFlags = cmsFLAGS_HIGHRESPRECALC; break; + case 3: dwFlags = cmsFLAGS_LOWRESPRECALC; break; + case 1: break; + + default: FatalError("Unknown precalculation mode '%d'", PrecalcMode); + } + + if (BlackPointCompensation) + dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; + + return dwFlags; +} + +// Create transforms + +static +void OpenTransforms(int argc, char *argv[]) +{ + + cmsUInt32Number dwIn, dwOut, dwFlags; + + + if (lMultiProfileChain) { + + int i; + cmsHTRANSFORM hTmp; + + + nProfiles = argc - xoptind; + for (i=0; i < nProfiles; i++) { + + hProfiles[i] = OpenProfile(argv[i+xoptind]); + } + + + // Create a temporary devicelink + + hTmp = cmsCreateMultiprofileTransform(hProfiles, nProfiles, + 0, 0, Intent, GetFlags()); + + hInput = cmsTransform2DeviceLink(hTmp, 4.2, 0); + hOutput = NULL; + cmsDeleteTransform(hTmp); + + InputColorSpace = cmsGetColorSpace(hInput); + OutputColorSpace = cmsGetPCS(hInput); + lIsDeviceLink = TRUE; + + } + else + if (lIsDeviceLink) { + + hInput = cmsOpenProfileFromFile(cInProf, "r"); + hOutput = NULL; + InputColorSpace = cmsGetColorSpace(hInput); + OutputColorSpace = cmsGetPCS(hInput); + + + } + else { + + hInput = OpenProfile(cInProf); + hOutput = OpenProfile(cOutProf); + + InputColorSpace = cmsGetColorSpace(hInput); + OutputColorSpace = cmsGetColorSpace(hOutput); + + if (cmsGetDeviceClass(hInput) == cmsSigLinkClass || + cmsGetDeviceClass(hOutput) == cmsSigLinkClass) + FatalError("Use %cl flag for devicelink profiles!\n", SW); + + } + + + /* + + if (Verbose) { + + mexPrintf("From: %s\n", cmsTakeProductName(hInput)); + if (hOutput) mexPrintf("To : %s\n\n", cmsTakeProductName(hOutput)); + + } + */ + + + OutputChannels = cmsChannelsOf(OutputColorSpace); + InputChannels = cmsChannelsOf(InputColorSpace); + + + dwIn = MakeFormatDescriptor(InputColorSpace, nBytesDepth); + dwOut = MakeFormatDescriptor(OutputColorSpace, nBytesDepth); + + + dwFlags = GetFlags(); + + if (cProofing != NULL) { + + hProof = OpenProfile(cProofing); + dwFlags |= cmsFLAGS_SOFTPROOFING; + } + + + + + hColorTransform = cmsCreateProofingTransform(hInput, dwIn, + hOutput, dwOut, + hProof, Intent, + ProofingIntent, + dwFlags); + + + +} + + + +static +void ApplyTransforms(const mxArray *In, mxArray *Out) +{ + double *Input = mxGetPr(In); + double *Output = mxGetPr(Out); + size_t nPixels = GetNumberOfPixels(In);; + + cmsDoTransform(hColorTransform, Input, Output, nPixels ); + +} + + +static +void CloseTransforms(void) +{ + int i; + + if (hColorTransform) cmsDeleteTransform(hColorTransform); + if (hInput) cmsCloseProfile(hInput); + if (hOutput) cmsCloseProfile(hOutput); + if (hProof) cmsCloseProfile(hProof); + + for (i=0; i < nProfiles; i++) + cmsCloseProfile(hProfiles[i]); + + hColorTransform = NULL; hInput = NULL; hOutput = NULL; hProof = NULL; +} + + +static +void HandleSwitches(int argc, char *argv[]) +{ + int s; + + xoptinit(); + + while ((s = xgetopt(argc, argv,"C:c:VvbBI:i:O:o:T:t:L:l:r:r:P:p:Mm")) != EOF) { + + + switch (s){ + + case 'b': + case 'B': + BlackPointCompensation = TRUE; + break; + + case 'c': + case 'C': + PrecalcMode = atoi(xoptarg); + if (PrecalcMode < 0 || PrecalcMode > 3) + FatalError("Unknown precalc mode '%d'", PrecalcMode); + break; + + case 'v': + case 'V': + Verbose = TRUE; + break; + + case 'i': + case 'I': + if (lIsDeviceLink) + FatalError("Device-link already specified"); + cInProf = xoptarg; + break; + + case 'o': + case 'O': + if (lIsDeviceLink) + FatalError("Device-link already specified"); + cOutProf = xoptarg; + break; + + case 't': + case 'T': + Intent = atoi(xoptarg); + if (Intent > 3) Intent = 3; + if (Intent < 0) Intent = 0; + break; + + + case 'l': + case 'L': + cInProf = xoptarg; + lIsDeviceLink = TRUE; + break; + + case 'p': + case 'P': + cProofing = xoptarg; + break; + + + + case 'r': + case 'R': + ProofingIntent = atoi(xoptarg); + if (ProofingIntent > 3) ProofingIntent = 3; + if (ProofingIntent < 0) ProofingIntent = 0; + break; + + + case 'm': + case 'M': + lMultiProfileChain = TRUE; + break; + + default: + FatalError("Unknown option."); + } + } + + // For multiprofile, need to specify -m + + if (xoptind < argc) { + + if (!lMultiProfileChain) + FatalError("Use %cm for multiprofile transforms", SW); + } + +} + + + +// -------------------------------------------------- Print some fancy help +static +void PrintHelp(void) +{ + mexPrintf("(MX) little cms ColorSpace conversion tool - v2.0\n\n"); + + mexPrintf("usage: icctrans (mVar, flags)\n\n"); + + mexPrintf("mVar : Matlab array.\n"); + mexPrintf("flags: a string containing one or more of following options.\n\n"); + mexPrintf("\t%cv - Verbose\n", SW); + mexPrintf("\t%ci<profile> - Input profile (defaults to sRGB)\n", SW); + mexPrintf("\t%co<profile> - Output profile (defaults to sRGB)\n", SW); + mexPrintf("\t%cl<profile> - Transform by device-link profile\n", SW); + mexPrintf("\t%cm<profiles> - Apply multiprofile chain\n", SW); + + mexPrintf("\t%ct<n> - Rendering intent\n", SW); + + mexPrintf("\t%cb - Black point compensation\n", SW); + mexPrintf("\t%cc<0,1,2,3> - Optimize transform (0=Off, 1=Normal, 2=Hi-res, 3=Lo-Res) [defaults to 1]\n", SW); + + mexPrintf("\t%cp<profile> - Soft proof profile\n", SW); + mexPrintf("\t%cr<0,1,2,3> - Soft proof intent\n", SW); + + mexPrintf("\nYou can use following built-ins as profiles:\n\n"); + mexPrintf("\t'*sRGB' -> IEC6 1996-2.1 sRGB\n"); + mexPrintf("\t'*Lab' -> D50 based Lab\n"); + mexPrintf("\t'*LabD65' -> D65 based Lab\n"); + mexPrintf("\t'*XYZ' -> XYZ (D50)\n"); + mexPrintf("\t'*Gray22' -> D50 gamma 2.2 grayscale.\n\n"); + + mexPrintf("For suggestions, comments, bug reports etc. send mail to info@littlecms.com\n\n"); + +} + + + +// Main entry point + +void mexFunction( + int nlhs, // Number of left hand side (output) arguments + mxArray *plhs[], // Array of left hand side arguments + int nrhs, // Number of right hand side (input) arguments + const mxArray *prhs[] // Array of right hand side arguments +) +{ + + char CommandLine[4096+1]; + char *pt, *argv[128]; + int argc = 1; + + + if (nrhs != 2) { + + PrintHelp(); + return; + } + + + if(nlhs > 1) { + FatalError("Too many output arguments."); + } + + + // Setup error handler + + cmsSetLogErrorHandler(MatLabErrorHandler); + + // Defaults + + Verbose = 0; + cInProf = NULL; + cOutProf = NULL; + cProofing = NULL; + + lMultiProfileChain = FALSE; + nProfiles = 0; + + Intent = INTENT_PERCEPTUAL; + ProofingIntent = INTENT_ABSOLUTE_COLORIMETRIC; + PrecalcMode = 1; + BlackPointCompensation = FALSE; + lIsDeviceLink = FALSE; + + // Check types. Fist parameter is array of values, second parameter is command line + + if (!mxIsNumeric(prhs[0])) + FatalError("Type mismatch on argument 1 -- Must be numeric"); + + if (!mxIsChar(prhs[1])) + FatalError("Type mismatch on argument 2 -- Must be string"); + + + + + // Unpack string to command line buffer + + if (mxGetString(prhs[1], CommandLine, 4096)) + FatalError("Cannot unpack command string"); + + // Separate to argv[] convention + + argv[0] = NULL; + for (pt = strtok(CommandLine, " "); + pt; + pt = strtok(NULL, " ")) { + + argv[argc++] = pt; + } + + + + // Parse arguments + HandleSwitches(argc, argv); + + + nBytesDepth = SizeOfArrayType(prhs[0]); + + OpenTransforms(argc, argv); + + + plhs[0] = AllocateOutputArray(prhs[0], OutputChannels); + + + ApplyTransforms(prhs[0], plhs[0]); + + CloseTransforms(); + + // Done! +} + + diff --git a/utils/matlab/lcms_rsp b/utils/matlab/lcms_rsp new file mode 100755 index 0000000..c2b8c8d --- /dev/null +++ b/utils/matlab/lcms_rsp @@ -0,0 +1,27 @@ +-O +-I..\..\include +icctrans.c +..\..\src\cmscam02.c +..\..\src\cmscgats.c +..\..\src\cmscnvrt.c +..\..\src\cmserr.c +..\..\src\cmsgamma.c +..\..\src\cmsgmt.c +..\..\src\cmsintrp.c +..\..\src\cmsio0.c +..\..\src\cmsio1.c +..\..\src\cmslut.c +..\..\src\cmsmd5.c +..\..\src\cmsmtrx.c +..\..\src\cmsnamed.c +..\..\src\cmsopt.c +..\..\src\cmspack.c +..\..\src\cmspcs.c +..\..\src\cmsplugin.c +..\..\src\cmsps2.c +..\..\src\cmssamp.c +..\..\src\cmssm.c +..\..\src\cmstypes.c +..\..\src\cmsvirt.c +..\..\src\cmswtpnt.c +..\..\src\cmsxform.c |