// 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; int i, nColors; cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL; // 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; }
static cmsUInt32Number findLCMStype(char* PILmode) { if (strcmp(PILmode, "RGB") == 0) { return TYPE_RGBA_8; } else if (strcmp(PILmode, "RGBA") == 0) { return TYPE_RGBA_8; } else if (strcmp(PILmode, "RGBX") == 0) { return TYPE_RGBA_8; } else if (strcmp(PILmode, "RGBA;16B") == 0) { return TYPE_RGBA_16; } else if (strcmp(PILmode, "CMYK") == 0) { return TYPE_CMYK_8; } else if (strcmp(PILmode, "L") == 0) { return TYPE_GRAY_8; } else if (strcmp(PILmode, "L;16") == 0) { return TYPE_GRAY_16; } else if (strcmp(PILmode, "L;16B") == 0) { return TYPE_GRAY_16_SE; } else if (strcmp(PILmode, "YCCA") == 0) { return TYPE_YCbCr_8; } else if (strcmp(PILmode, "YCC") == 0) { return TYPE_YCbCr_8; } else if (strcmp(PILmode, "LAB") == 0) { // LabX equvalent like ALab, but not reversed -- no #define in lcms2 return (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)); } else { /* take a wild guess... but you probably should fail instead. */ return TYPE_GRAY_8; /* so there's no buffer overrun... */ } }
/* Transform an entire buffer */ void gscms_transform_color_buffer(gsicc_link_t *icclink, gsicc_bufferdesc_t *input_buff_desc, gsicc_bufferdesc_t *output_buff_desc, void *inputbuffer, void *outputbuffer) { cmsHTRANSFORM hTransform = (cmsHTRANSFORM) icclink->link_handle; DWORD dwInputFormat,dwOutputFormat,curr_input,curr_output; int planar,numbytes,big_endian,hasalpha,k; unsigned char *inputpos, *outputpos; int numchannels; #if DUMP_CMS_BUFFER FILE *fid_in, *fid_out; #endif /* Although little CMS does make assumptions about data types in its transformations you can change it after the fact. */ /* Set us to the proper output type */ /* Note, we could speed this up by passing back the encoded data type to the caller so that we could avoid having to go through this computation each time if they are doing multiple calls to this operation */ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) (LPSTR) hTransform; curr_input = p->InputFormat; curr_output = p->OutputFormat; /* Color space MUST be the same */ dwInputFormat = COLORSPACE_SH(T_COLORSPACE(curr_input)); dwOutputFormat = COLORSPACE_SH(T_COLORSPACE(curr_output)); /* Now set if we have planar, num bytes, endian case, and alpha data to skip */ /* Planar -- pdf14 case for example */ planar = input_buff_desc->is_planar; dwInputFormat = dwInputFormat | PLANAR_SH(planar); planar = output_buff_desc->is_planar; dwOutputFormat = dwOutputFormat | PLANAR_SH(planar); /* 8 or 16 byte input and output */ numbytes = input_buff_desc->bytes_per_chan; if (numbytes>2) numbytes = 0; /* littleCMS encodes float with 0 ToDO. */ dwInputFormat = dwInputFormat | BYTES_SH(numbytes); numbytes = output_buff_desc->bytes_per_chan; if (numbytes>2) numbytes = 0; dwOutputFormat = dwOutputFormat | BYTES_SH(numbytes); /* endian */ big_endian = !input_buff_desc->little_endian; dwInputFormat = dwInputFormat | ENDIAN16_SH(big_endian); big_endian = !output_buff_desc->little_endian; dwOutputFormat = dwOutputFormat | ENDIAN16_SH(big_endian); /* number of channels */ numchannels = input_buff_desc->num_chan; dwInputFormat = dwInputFormat | CHANNELS_SH(numchannels); numchannels = output_buff_desc->num_chan; dwOutputFormat = dwOutputFormat | CHANNELS_SH(numchannels); /* alpha, which is passed through unmolested */ /* ToDo: Right now we always must have alpha last */ /* This is really only going to be an issue when we have interleaved alpha data */ hasalpha = input_buff_desc->has_alpha; dwInputFormat = dwInputFormat | EXTRA_SH(hasalpha); dwOutputFormat = dwOutputFormat | EXTRA_SH(hasalpha); /* Change the formaters */ cmsChangeBuffersFormat(hTransform,dwInputFormat,dwOutputFormat); /* littleCMS knows nothing about word boundarys. As such, we need to do this row by row adjusting for our stride. Output buffer must already be allocated. ToDo: Check issues with plane and row stride and word boundry */ inputpos = (unsigned char *) inputbuffer; outputpos = (unsigned char *) outputbuffer; if(input_buff_desc->is_planar){ /* Do entire buffer. Care must be taken here with respect to row stride, word boundry and number of source versus output channels. We may need to take a closer look at this. */ cmsDoTransform(hTransform,inputpos,outputpos, input_buff_desc->plane_stride); } else { /* Do row by row. */ for(k = 0; k < input_buff_desc->num_rows ; k++){ cmsDoTransform(hTransform,inputpos,outputpos, input_buff_desc->pixels_per_row); inputpos += input_buff_desc->row_stride; outputpos += output_buff_desc->row_stride; } } #if DUMP_CMS_BUFFER fid_in = fopen("CM_Input.raw","ab"); fid_out = fopen("CM_Output.raw","ab"); fwrite((unsigned char*) inputbuffer,sizeof(unsigned char), input_buff_desc->row_stride,fid_in); fwrite((unsigned char*) outputbuffer,sizeof(unsigned char), output_buff_desc->row_stride,fid_out); fclose(fid_in); fclose(fid_out); #endif }
static DWORD ComputeOutputFormatDescriptor(DWORD dwInput, int OutColorSpace, int bps) { int IsPlanar = T_PLANAR(dwInput); int Channels = 0; switch (OutColorSpace) { case PT_GRAY: Channels = 1; break; case PT_RGB: case PT_CMY: case PT_Lab: case PT_YUV: case PT_YCbCr: Channels = 3; break; case PT_CMYK: Channels = 4; break; case PT_HiFi: Channels = 6; break; case PT_HiFi7: Channels = 7; break; case PT_HiFi8: Channels = 8; break; case PT_HiFi9: Channels = 9; break; case PT_HiFi10: Channels = 10; break; case PT_HiFi11: Channels = 11; break; case PT_HiFi12: Channels = 12; break; case PT_HiFi13: Channels = 13; break; case PT_HiFi14: Channels = 14; break; case PT_HiFi15: Channels = 15; break; default: FatalError("Unsupported output color space"); } return (COLORSPACE_SH(OutColorSpace)|PLANAR_SH(IsPlanar)|CHANNELS_SH(Channels)|BYTES_SH(bps)); }
static DWORD GetInputPixelType(TIFF *Bank) { uint16 Photometric, bps, spp, extra, PlanarConfig, *info; uint16 Compression, reverse = 0; int ColorChannels, IsPlanar = 0, pt = 0; TIFFGetField(Bank, TIFFTAG_PHOTOMETRIC, &Photometric); TIFFGetFieldDefaulted(Bank, TIFFTAG_BITSPERSAMPLE, &bps); if (bps == 1) FatalError("Sorry, bilevel TIFFs has nothig to do with ICC profiles"); if (bps != 8 && bps != 16) FatalError("Sorry, 8 or 16 bits per sample only"); TIFFGetFieldDefaulted(Bank, TIFFTAG_SAMPLESPERPIXEL, &spp); TIFFGetFieldDefaulted(Bank, TIFFTAG_PLANARCONFIG, &PlanarConfig); switch (PlanarConfig) { case PLANARCONFIG_CONTIG: IsPlanar = 0; break; case PLANARCONFIG_SEPARATE: IsPlanar = 1; break; default: FatalError("Unsupported planar configuration (=%d) ", (int) PlanarConfig); } // If Samples per pixel == 1, PlanarConfiguration is irrelevant and need // not to be included. if (spp == 1) IsPlanar = 0; // Any alpha? TIFFGetFieldDefaulted(Bank, TIFFTAG_EXTRASAMPLES, &extra, &info); // Read alpha channels as colorant if (StoreAsAlpha) { ColorChannels = spp; extra = 0; } else ColorChannels = spp - extra; switch (Photometric) { case PHOTOMETRIC_MINISWHITE: reverse = 1; case PHOTOMETRIC_MINISBLACK: pt = PT_GRAY; break; case PHOTOMETRIC_RGB: pt = PT_RGB; break; case PHOTOMETRIC_PALETTE: FatalError("Sorry, palette images not supported (at least on this version)"); case PHOTOMETRIC_SEPARATED: if (ColorChannels == 4) pt = PT_CMYK; else if (ColorChannels == 3) pt = PT_CMY; else if (ColorChannels == 6) pt = PT_HiFi; else if (ColorChannels == 7) pt = PT_HiFi7; else if (ColorChannels == 8) pt = PT_HiFi8; else if (ColorChannels == 9) pt = PT_HiFi9; else if (ColorChannels == 10) pt = PT_HiFi10; else if (ColorChannels == 11) pt = PT_HiFi11; else if (ColorChannels == 12) pt = PT_HiFi8; else if (ColorChannels == 13) pt = PT_HiFi13; else if (ColorChannels == 14) pt = PT_HiFi14; else if (ColorChannels == 15) pt = PT_HiFi15; else FatalError("What a weird separation of %d channels?!?!", ColorChannels); break; case PHOTOMETRIC_YCBCR: TIFFGetField(Bank, TIFFTAG_COMPRESSION, &Compression); { uint16 subx, suby; pt = PT_YCbCr; TIFFGetFieldDefaulted(Bank, TIFFTAG_YCBCRSUBSAMPLING, &subx, &suby); if (subx != 1 || suby != 1) FatalError("Sorry, subsampled images not supported"); } break; case 9: pt = PT_Lab; InputLabUsingICC = TRUE; break; case PHOTOMETRIC_CIELAB: pt = PT_Lab; InputLabUsingICC = FALSE; break; case PHOTOMETRIC_LOGLUV: /* CIE Log2(L) (u',v') */ TIFFSetField(Bank, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_16BIT); pt = PT_YUV; // *ICCSpace = icSigLuvData; bps = 16; // 16 bits forced by LibTiff break; default: FatalError("Unsupported TIFF color space (Photometric %d)", Photometric); } // Convert bits per sample to bytes per sample bps >>= 3; return (COLORSPACE_SH(pt)|PLANAR_SH(IsPlanar)|EXTRA_SH(extra)|CHANNELS_SH(ColorChannels)|BYTES_SH(bps)|FLAVOR_SH(reverse)); }
// 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; cmsUInt32Number 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; } 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; // 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) { dwFlags |= cmsFLAGS_FORCE_CLUT; _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); // Put identity curves if needed if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType) if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn))) goto Error; if (cmsPipelineGetPtrToLastStage(LUT) ->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; } 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; } // 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; }
/* Get the link from the CMS, but include proofing and/or a device link profile. Note also, that the source may be a device link profile, in which case we will not have a destination profile but could still have a proof profile or an additional device link profile */ gcmmhlink_t gscms_get_link_proof_devlink(gcmmhprofile_t lcms_srchandle, gcmmhprofile_t lcms_proofhandle, gcmmhprofile_t lcms_deshandle, gcmmhprofile_t lcms_devlinkhandle, gsicc_rendering_param_t *rendering_params, bool src_dev_link, int cmm_flags, gs_memory_t *memory) { cmsUInt32Number src_data_type,des_data_type; cmsColorSpaceSignature src_color_space,des_color_space; int src_nChannels,des_nChannels; int lcms_src_color_space, lcms_des_color_space; cmsHPROFILE hProfiles[5]; int nProfiles = 0; unsigned int flag; /* Check if the rendering intent is something other than relative colorimetric and if we have a proofing profile. In this case we need to create the combined profile a bit different. LCMS does not allow us to use different intents in the cmsCreateMultiprofileTransform transform. Also, don't even think about doing this if someone has snuck in a source based device link profile into the mix */ if (lcms_proofhandle != NULL && rendering_params->rendering_intent != gsRELATIVECOLORIMETRIC && !src_dev_link) { /* First handle the source to proof profile with its particular intent as a device link profile */ cmsHPROFILE src_to_proof; cmsHTRANSFORM temptransform; temptransform = gscms_get_link(lcms_srchandle, lcms_proofhandle, rendering_params, cmm_flags, memory); /* Now mash that to a device link profile */ flag = cmsFLAGS_HIGHRESPRECALC; if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); } src_to_proof = cmsTransform2DeviceLink(temptransform, 3.4, flag); /* Free up the link handle */ cmsDeleteTransform(temptransform); src_color_space = cmsGetColorSpace(src_to_proof); lcms_src_color_space = _cmsLCMScolorSpace(src_color_space); /* littlecms returns -1 for types it does not (but should) understand */ if (lcms_src_color_space < 0) lcms_src_color_space = 0; src_nChannels = cmsChannelsOf(src_color_space); /* For now, just do single byte data, interleaved. We can change this when we use the transformation. */ src_data_type = (COLORSPACE_SH(lcms_src_color_space)| CHANNELS_SH(src_nChannels)|BYTES_SH(2)); if (lcms_devlinkhandle == NULL) { des_color_space = cmsGetColorSpace(lcms_deshandle); } else { des_color_space = cmsGetPCS(lcms_devlinkhandle); } lcms_des_color_space = _cmsLCMScolorSpace(des_color_space); if (lcms_des_color_space < 0) lcms_des_color_space = 0; des_nChannels = cmsChannelsOf(des_color_space); des_data_type = (COLORSPACE_SH(lcms_des_color_space)| CHANNELS_SH(des_nChannels)|BYTES_SH(2)); /* Now, we need to go back through the proofing profile, to the destination and then to the device link profile if there was one. */ hProfiles[nProfiles++] = src_to_proof; /* Src to proof with special intent */ hProfiles[nProfiles++] = lcms_proofhandle; /* Proof to CIELAB */ if (lcms_deshandle != NULL) { hProfiles[nProfiles++] = lcms_deshandle; /* Our destination */ } /* The output device link profile */ if (lcms_devlinkhandle != NULL) { hProfiles[nProfiles++] = lcms_devlinkhandle; } flag = cmsFLAGS_HIGHRESPRECALC; if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); } /* Use relative colorimetric here */ temptransform = cmsCreateMultiprofileTransformTHR((cmsContext)memory, hProfiles, nProfiles, src_data_type, des_data_type, gsRELATIVECOLORIMETRIC, flag); cmsCloseProfile(src_to_proof); return temptransform; } else { /* First handle all the source stuff */ src_color_space = cmsGetColorSpace(lcms_srchandle); lcms_src_color_space = _cmsLCMScolorSpace(src_color_space); /* littlecms returns -1 for types it does not (but should) understand */ if (lcms_src_color_space < 0) lcms_src_color_space = 0; src_nChannels = cmsChannelsOf(src_color_space); /* For now, just do single byte data, interleaved. We can change this when we use the transformation. */ src_data_type = (COLORSPACE_SH(lcms_src_color_space)| CHANNELS_SH(src_nChannels)|BYTES_SH(2)); if (lcms_devlinkhandle == NULL) { if (src_dev_link) { des_color_space = cmsGetPCS(lcms_srchandle); } else { des_color_space = cmsGetColorSpace(lcms_deshandle); } } else { des_color_space = cmsGetPCS(lcms_devlinkhandle); } lcms_des_color_space = _cmsLCMScolorSpace(des_color_space); if (lcms_des_color_space < 0) lcms_des_color_space = 0; des_nChannels = cmsChannelsOf(des_color_space); des_data_type = (COLORSPACE_SH(lcms_des_color_space)| CHANNELS_SH(des_nChannels)|BYTES_SH(2)); /* lcms proofing transform has a clunky API and can't include the device link profile if we have both. So use cmsCreateMultiprofileTransform instead and round trip the proofing profile. */ hProfiles[nProfiles++] = lcms_srchandle; /* Note if source is device link, we cannot do any proofing */ if (lcms_proofhandle != NULL && !src_dev_link) { hProfiles[nProfiles++] = lcms_proofhandle; hProfiles[nProfiles++] = lcms_proofhandle; } /* This should be NULL if we have a source device link */ if (lcms_deshandle != NULL) { hProfiles[nProfiles++] = lcms_deshandle; } /* Someone could have a device link at the output, giving us possibly two device link profiles to smash together */ if (lcms_devlinkhandle != NULL) { hProfiles[nProfiles++] = lcms_devlinkhandle; } flag = cmsFLAGS_HIGHRESPRECALC; if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); } return cmsCreateMultiprofileTransformTHR((cmsContext)memory, hProfiles, nProfiles, src_data_type, des_data_type, rendering_params->rendering_intent, flag); } }
/* Get the link from the CMS. TODO: Add error checking */ gcmmhlink_t gscms_get_link(gcmmhprofile_t lcms_srchandle, gcmmhprofile_t lcms_deshandle, gsicc_rendering_param_t *rendering_params, int cmm_flags, gs_memory_t *memory) { cmsUInt32Number src_data_type,des_data_type; cmsColorSpaceSignature src_color_space,des_color_space; int src_nChannels,des_nChannels; int lcms_src_color_space, lcms_des_color_space; unsigned int flag; /* Check for case of request for a transfrom from a device link profile in that case, the destination profile is NULL */ /* First handle all the source stuff */ src_color_space = cmsGetColorSpace(lcms_srchandle); lcms_src_color_space = _cmsLCMScolorSpace(src_color_space); /* littlecms returns -1 for types it does not (but should) understand */ if (lcms_src_color_space < 0) lcms_src_color_space = 0; src_nChannels = cmsChannelsOf(src_color_space); /* For now, just do single byte data, interleaved. We can change this when we use the transformation. */ src_data_type = (COLORSPACE_SH(lcms_src_color_space)| CHANNELS_SH(src_nChannels)|BYTES_SH(2)); #if 0 src_data_type = src_data_type | ENDIAN16_SH(1); #endif if (lcms_deshandle != NULL) { des_color_space = cmsGetColorSpace(lcms_deshandle); } else { /* We must have a device link profile. */ des_color_space = cmsGetPCS(lcms_deshandle); } lcms_des_color_space = _cmsLCMScolorSpace(des_color_space); if (lcms_des_color_space < 0) lcms_des_color_space = 0; des_nChannels = cmsChannelsOf(des_color_space); des_data_type = (COLORSPACE_SH(lcms_des_color_space)| CHANNELS_SH(des_nChannels)|BYTES_SH(2)); /* endian */ #if 0 des_data_type = des_data_type | ENDIAN16_SH(1); #endif /* Set up the flags */ flag = cmsFLAGS_HIGHRESPRECALC; if (rendering_params->black_point_comp == gsBLACKPTCOMP_ON || rendering_params->black_point_comp == gsBLACKPTCOMP_ON_OR) { flag = (flag | cmsFLAGS_BLACKPOINTCOMPENSATION); } if (rendering_params->preserve_black == gsBLACKPRESERVE_KONLY) { switch (rendering_params->rendering_intent) { case INTENT_PERCEPTUAL: rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_PERCEPTUAL; break; case INTENT_RELATIVE_COLORIMETRIC: rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC; break; case INTENT_SATURATION: rendering_params->rendering_intent = INTENT_PRESERVE_K_ONLY_SATURATION; break; default: break; } } if (rendering_params->preserve_black == gsBLACKPRESERVE_KPLANE) { switch (rendering_params->rendering_intent) { case INTENT_PERCEPTUAL: rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_PERCEPTUAL; break; case INTENT_RELATIVE_COLORIMETRIC: rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC; break; case INTENT_SATURATION: rendering_params->rendering_intent = INTENT_PRESERVE_K_PLANE_SATURATION; break; default: break; } } /* Create the link */ return cmsCreateTransformTHR((cmsContext)memory, lcms_srchandle, src_data_type, lcms_deshandle, des_data_type, rendering_params->rendering_intent, flag | cmm_flags); /* cmsFLAGS_HIGHRESPRECALC) cmsFLAGS_NOTPRECALC cmsFLAGS_LOWRESPRECALC*/ }
/* Transform an entire buffer */ void gscms_transform_color_buffer(gx_device *dev, gsicc_link_t *icclink, gsicc_bufferdesc_t *input_buff_desc, gsicc_bufferdesc_t *output_buff_desc, void *inputbuffer, void *outputbuffer) { cmsHTRANSFORM hTransform = (cmsHTRANSFORM)icclink->link_handle; cmsUInt32Number dwInputFormat, dwOutputFormat, num_src_lcms, num_des_lcms; int planar,numbytes, big_endian, hasalpha, k; unsigned char *inputpos, *outputpos; #if DUMP_CMS_BUFFER FILE *fid_in, *fid_out; #endif /* Although little CMS does make assumptions about data types in its transformations you can change it after the fact. */ /* Set us to the proper output type */ /* Note, we could speed this up by passing back the encoded data type to the caller so that we could avoid having to go through this computation each time if they are doing multiple calls to this operation */ /* Color space MUST be the same */ dwInputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformInputFormat(hTransform))); dwOutputFormat = COLORSPACE_SH(T_COLORSPACE(cmsGetTransformOutputFormat(hTransform))); /* Now set if we have planar, num bytes, endian case, and alpha data to skip */ /* Planar -- pdf14 case for example */ planar = input_buff_desc->is_planar; dwInputFormat = dwInputFormat | PLANAR_SH(planar); planar = output_buff_desc->is_planar; dwOutputFormat = dwOutputFormat | PLANAR_SH(planar); /* 8 or 16 byte input and output */ numbytes = input_buff_desc->bytes_per_chan; if (numbytes>2) numbytes = 0; /* littleCMS encodes float with 0 ToDO. */ dwInputFormat = dwInputFormat | BYTES_SH(numbytes); numbytes = output_buff_desc->bytes_per_chan; if (numbytes>2) numbytes = 0; dwOutputFormat = dwOutputFormat | BYTES_SH(numbytes); /* endian */ big_endian = !input_buff_desc->little_endian; dwInputFormat = dwInputFormat | ENDIAN16_SH(big_endian); big_endian = !output_buff_desc->little_endian; dwOutputFormat = dwOutputFormat | ENDIAN16_SH(big_endian); /* number of channels. This should not really be changing! */ num_src_lcms = T_CHANNELS(cmsGetTransformInputFormat(hTransform)); num_des_lcms = T_CHANNELS(cmsGetTransformOutputFormat(hTransform)); if (num_src_lcms != input_buff_desc->num_chan || num_des_lcms != output_buff_desc->num_chan) { /* We can't transform this. Someone is doing something odd */ return; } dwInputFormat = dwInputFormat | CHANNELS_SH(num_src_lcms); dwOutputFormat = dwOutputFormat | CHANNELS_SH(num_des_lcms); /* alpha, which is passed through unmolested */ /* ToDo: Right now we always must have alpha last */ /* This is really only going to be an issue when we have interleaved alpha data */ hasalpha = input_buff_desc->has_alpha; dwInputFormat = dwInputFormat | EXTRA_SH(hasalpha); dwOutputFormat = dwOutputFormat | EXTRA_SH(hasalpha); /* Change the formatters */ cmsChangeBuffersFormat(hTransform,dwInputFormat,dwOutputFormat); /* littleCMS knows nothing about word boundarys. As such, we need to do this row by row adjusting for our stride. Output buffer must already be allocated. ToDo: Check issues with plane and row stride and word boundry */ inputpos = (byte *) inputbuffer; outputpos = (byte *) outputbuffer; if(input_buff_desc->is_planar) { /* Determine if we can do this in one operation or if we have to break it up. Essentially if the width * height = plane_stride then yes. If we are doing some subsection of a plane then no. */ if (input_buff_desc->num_rows * input_buff_desc->pixels_per_row == input_buff_desc->plane_stride && output_buff_desc->num_rows * output_buff_desc->pixels_per_row == output_buff_desc->plane_stride) { /* Do entire buffer.*/ cmsDoTransform(hTransform, inputpos, outputpos, input_buff_desc->plane_stride); } else { /* We have to do this row by row, with memory transfers */ byte *temp_des, *temp_src; int source_size = input_buff_desc->bytes_per_chan * input_buff_desc->pixels_per_row; int des_size = output_buff_desc->bytes_per_chan * output_buff_desc->pixels_per_row; int y, i; temp_src = (byte*) gs_alloc_bytes(icclink->icc_link_cache->memory, source_size * input_buff_desc->num_chan, "gscms_transform_color_buffer"); if (temp_src == NULL) return; temp_des = (byte*) gs_alloc_bytes(icclink->icc_link_cache->memory, des_size * output_buff_desc->num_chan, "gscms_transform_color_buffer"); if (temp_des == NULL) return; for (y = 0; y < input_buff_desc->num_rows; y++) { byte *src_cm = temp_src; byte *src_buff = inputpos; byte *des_cm = temp_des; byte *des_buff = outputpos; /* Put into planar temp buffer */ for (i = 0; i < input_buff_desc->num_chan; i ++) { memcpy(src_cm, src_buff, source_size); src_cm += source_size; src_buff += input_buff_desc->plane_stride; } /* Transform */ cmsDoTransform(hTransform, temp_src, temp_des, input_buff_desc->pixels_per_row); /* Get out of temp planar buffer */ for (i = 0; i < output_buff_desc->num_chan; i ++) { memcpy(des_buff, des_cm, des_size); des_cm += des_size; des_buff += output_buff_desc->plane_stride; } inputpos += input_buff_desc->row_stride; outputpos += output_buff_desc->row_stride; } gs_free_object(icclink->icc_link_cache->memory, temp_src, "gscms_transform_color_buffer"); gs_free_object(icclink->icc_link_cache->memory, temp_des, "gscms_transform_color_buffer"); } } else { /* Do row by row. */ for(k = 0; k < input_buff_desc->num_rows ; k++) { cmsDoTransform(hTransform, inputpos, outputpos, input_buff_desc->pixels_per_row); inputpos += input_buff_desc->row_stride; outputpos += output_buff_desc->row_stride; } } #if DUMP_CMS_BUFFER fid_in = gp_fopen("CM_Input.raw","ab"); fid_out = gp_fopen("CM_Output.raw","ab"); fwrite((unsigned char*) inputbuffer,sizeof(unsigned char), input_buff_desc->row_stride,fid_in); fwrite((unsigned char*) outputbuffer,sizeof(unsigned char), output_buff_desc->row_stride,fid_out); fclose(fid_in); fclose(fid_out); #endif }
static DWORD MakeFormatDescriptor(icColorSpaceSignature ColorSpace, int Bytes) { int Channels = _cmsChannelsOf(ColorSpace); return COLORSPACE_SH(ICC2LCMS(ColorSpace))|BYTES_SH(Bytes)|CHANNELS_SH(Channels)|PLANAR_SH(1); }