cmsvirt.c 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341
  1. //---------------------------------------------------------------------------------
  2. //
  3. // Little Color Management System
  4. // Copyright (c) 1998-2023 Marti Maria Saguer
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining
  7. // a copy of this software and associated documentation files (the "Software"),
  8. // to deal in the Software without restriction, including without limitation
  9. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  10. // and/or sell copies of the Software, and to permit persons to whom the Software
  11. // is furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  18. // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. //
  24. //---------------------------------------------------------------------------------
  25. //
  26. #include "lcms2_internal.h"
  27. // Virtual (built-in) profiles
  28. // -----------------------------------------------------------------------------------
  29. static
  30. cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description)
  31. {
  32. cmsMLU *DescriptionMLU, *CopyrightMLU;
  33. cmsBool rc = FALSE;
  34. cmsContext ContextID = cmsGetProfileContextID(hProfile);
  35. DescriptionMLU = cmsMLUalloc(ContextID, 1);
  36. CopyrightMLU = cmsMLUalloc(ContextID, 1);
  37. if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;
  38. if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error;
  39. if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error;
  40. if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error;
  41. if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error;
  42. rc = TRUE;
  43. Error:
  44. if (DescriptionMLU)
  45. cmsMLUfree(DescriptionMLU);
  46. if (CopyrightMLU)
  47. cmsMLUfree(CopyrightMLU);
  48. return rc;
  49. }
  50. static
  51. cmsBool SetSeqDescTag(cmsHPROFILE hProfile, const char* Model)
  52. {
  53. cmsBool rc = FALSE;
  54. cmsContext ContextID = cmsGetProfileContextID(hProfile);
  55. cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1);
  56. if (Seq == NULL) return FALSE;
  57. Seq->seq[0].deviceMfg = (cmsSignature) 0;
  58. Seq->seq[0].deviceModel = (cmsSignature) 0;
  59. #ifdef CMS_DONT_USE_INT64
  60. Seq->seq[0].attributes[0] = 0;
  61. Seq->seq[0].attributes[1] = 0;
  62. #else
  63. Seq->seq[0].attributes = 0;
  64. #endif
  65. Seq->seq[0].technology = (cmsTechnologySignature) 0;
  66. cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS");
  67. cmsMLUsetASCII( Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model);
  68. if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error;
  69. rc = TRUE;
  70. Error:
  71. if (Seq)
  72. cmsFreeProfileSequenceDescription(Seq);
  73. return rc;
  74. }
  75. // This function creates a profile based on White point, primaries and
  76. // transfer functions.
  77. cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID,
  78. const cmsCIExyY* WhitePoint,
  79. const cmsCIExyYTRIPLE* Primaries,
  80. cmsToneCurve* const TransferFunction[3])
  81. {
  82. cmsHPROFILE hICC;
  83. cmsMAT3 MColorants;
  84. cmsCIEXYZTRIPLE Colorants;
  85. cmsCIExyY MaxWhite;
  86. cmsMAT3 CHAD;
  87. cmsCIEXYZ WhitePointXYZ;
  88. hICC = cmsCreateProfilePlaceholder(ContextID);
  89. if (!hICC) // can't allocate
  90. return NULL;
  91. cmsSetProfileVersion(hICC, 4.4);
  92. cmsSetDeviceClass(hICC, cmsSigDisplayClass);
  93. cmsSetColorSpace(hICC, cmsSigRgbData);
  94. cmsSetPCS(hICC, cmsSigXYZData);
  95. cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
  96. // Implement profile using following tags:
  97. //
  98. // 1 cmsSigProfileDescriptionTag
  99. // 2 cmsSigMediaWhitePointTag
  100. // 3 cmsSigRedColorantTag
  101. // 4 cmsSigGreenColorantTag
  102. // 5 cmsSigBlueColorantTag
  103. // 6 cmsSigRedTRCTag
  104. // 7 cmsSigGreenTRCTag
  105. // 8 cmsSigBlueTRCTag
  106. // 9 Chromatic adaptation Tag
  107. // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II)
  108. // 10 cmsSigChromaticityTag
  109. if (!SetTextTags(hICC, L"RGB built-in")) goto Error;
  110. if (WhitePoint) {
  111. if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
  112. cmsxyY2XYZ(&WhitePointXYZ, WhitePoint);
  113. _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ());
  114. // This is a V4 tag, but many CMM does read and understand it no matter which version
  115. if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error;
  116. }
  117. if (WhitePoint && Primaries) {
  118. MaxWhite.x = WhitePoint -> x;
  119. MaxWhite.y = WhitePoint -> y;
  120. MaxWhite.Y = 1.0;
  121. if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error;
  122. Colorants.Red.X = MColorants.v[0].n[0];
  123. Colorants.Red.Y = MColorants.v[1].n[0];
  124. Colorants.Red.Z = MColorants.v[2].n[0];
  125. Colorants.Green.X = MColorants.v[0].n[1];
  126. Colorants.Green.Y = MColorants.v[1].n[1];
  127. Colorants.Green.Z = MColorants.v[2].n[1];
  128. Colorants.Blue.X = MColorants.v[0].n[2];
  129. Colorants.Blue.Y = MColorants.v[1].n[2];
  130. Colorants.Blue.Z = MColorants.v[2].n[2];
  131. if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error;
  132. if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error;
  133. if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error;
  134. }
  135. if (TransferFunction) {
  136. // Tries to minimize space. Thanks to Richard Hughes for this nice idea
  137. if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error;
  138. if (TransferFunction[1] == TransferFunction[0]) {
  139. if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error;
  140. } else {
  141. if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error;
  142. }
  143. if (TransferFunction[2] == TransferFunction[0]) {
  144. if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error;
  145. } else {
  146. if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error;
  147. }
  148. }
  149. if (Primaries) {
  150. if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error;
  151. }
  152. return hICC;
  153. Error:
  154. if (hICC)
  155. cmsCloseProfile(hICC);
  156. return NULL;
  157. }
  158. cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint,
  159. const cmsCIExyYTRIPLE* Primaries,
  160. cmsToneCurve* const TransferFunction[3])
  161. {
  162. return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction);
  163. }
  164. // This function creates a profile based on White point and transfer function.
  165. cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID,
  166. const cmsCIExyY* WhitePoint,
  167. const cmsToneCurve* TransferFunction)
  168. {
  169. cmsHPROFILE hICC;
  170. cmsCIEXYZ tmp;
  171. hICC = cmsCreateProfilePlaceholder(ContextID);
  172. if (!hICC) // can't allocate
  173. return NULL;
  174. cmsSetProfileVersion(hICC, 4.4);
  175. cmsSetDeviceClass(hICC, cmsSigDisplayClass);
  176. cmsSetColorSpace(hICC, cmsSigGrayData);
  177. cmsSetPCS(hICC, cmsSigXYZData);
  178. cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
  179. // Implement profile using following tags:
  180. //
  181. // 1 cmsSigProfileDescriptionTag
  182. // 2 cmsSigMediaWhitePointTag
  183. // 3 cmsSigGrayTRCTag
  184. // This conforms a standard Gray DisplayProfile
  185. // Fill-in the tags
  186. if (!SetTextTags(hICC, L"gray built-in")) goto Error;
  187. if (WhitePoint) {
  188. cmsxyY2XYZ(&tmp, WhitePoint);
  189. if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error;
  190. }
  191. if (TransferFunction) {
  192. if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error;
  193. }
  194. return hICC;
  195. Error:
  196. if (hICC)
  197. cmsCloseProfile(hICC);
  198. return NULL;
  199. }
  200. cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint,
  201. const cmsToneCurve* TransferFunction)
  202. {
  203. return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction);
  204. }
  205. // This is a devicelink operating in the target colorspace with as many transfer functions as components
  206. cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID,
  207. cmsColorSpaceSignature ColorSpace,
  208. cmsToneCurve* const TransferFunctions[])
  209. {
  210. cmsHPROFILE hICC;
  211. cmsPipeline* Pipeline;
  212. cmsInt32Number nChannels;
  213. hICC = cmsCreateProfilePlaceholder(ContextID);
  214. if (!hICC)
  215. return NULL;
  216. cmsSetProfileVersion(hICC, 4.4);
  217. cmsSetDeviceClass(hICC, cmsSigLinkClass);
  218. cmsSetColorSpace(hICC, ColorSpace);
  219. cmsSetPCS(hICC, ColorSpace);
  220. cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
  221. // Set up channels
  222. nChannels = cmsChannelsOfColorSpace(ColorSpace);
  223. // Creates a Pipeline with prelinearization step only
  224. Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
  225. if (Pipeline == NULL) goto Error;
  226. // Copy tables to Pipeline
  227. if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
  228. goto Error;
  229. // Create tags
  230. if (!SetTextTags(hICC, L"Linearization built-in")) goto Error;
  231. if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error;
  232. if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error;
  233. // Pipeline is already on virtual profile
  234. cmsPipelineFree(Pipeline);
  235. // Ok, done
  236. return hICC;
  237. Error:
  238. cmsPipelineFree(Pipeline);
  239. if (hICC)
  240. cmsCloseProfile(hICC);
  241. return NULL;
  242. }
  243. cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace,
  244. cmsToneCurve* const TransferFunctions[])
  245. {
  246. return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions);
  247. }
  248. // Ink-limiting algorithm
  249. //
  250. // Sum = C + M + Y + K
  251. // If Sum > InkLimit
  252. // Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
  253. // if Ratio <0
  254. // Ratio=0
  255. // endif
  256. // Else
  257. // Ratio=1
  258. // endif
  259. //
  260. // C = Ratio * C
  261. // M = Ratio * M
  262. // Y = Ratio * Y
  263. // K: Does not change
  264. static
  265. int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
  266. {
  267. cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo;
  268. cmsFloat64Number SumCMY, SumCMYK, Ratio;
  269. InkLimit = (InkLimit * 655.35);
  270. SumCMY = (cmsFloat64Number) In[0] + In[1] + In[2];
  271. SumCMYK = SumCMY + In[3];
  272. if (SumCMYK > InkLimit) {
  273. Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
  274. if (Ratio < 0)
  275. Ratio = 0;
  276. }
  277. else Ratio = 1;
  278. Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C
  279. Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M
  280. Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y
  281. Out[3] = In[3]; // K (untouched)
  282. return TRUE;
  283. }
  284. // This is a devicelink operating in CMYK for ink-limiting
  285. cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
  286. cmsColorSpaceSignature ColorSpace,
  287. cmsFloat64Number Limit)
  288. {
  289. cmsHPROFILE hICC;
  290. cmsPipeline* LUT;
  291. cmsStage* CLUT;
  292. cmsInt32Number nChannels;
  293. if (ColorSpace != cmsSigCmykData) {
  294. cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
  295. return NULL;
  296. }
  297. if (Limit < 0.0 || Limit > 400) {
  298. cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 1..400");
  299. if (Limit < 1) Limit = 1;
  300. if (Limit > 400) Limit = 400;
  301. }
  302. hICC = cmsCreateProfilePlaceholder(ContextID);
  303. if (!hICC) // can't allocate
  304. return NULL;
  305. cmsSetProfileVersion(hICC, 4.4);
  306. cmsSetDeviceClass(hICC, cmsSigLinkClass);
  307. cmsSetColorSpace(hICC, ColorSpace);
  308. cmsSetPCS(hICC, ColorSpace);
  309. cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
  310. // Creates a Pipeline with 3D grid only
  311. LUT = cmsPipelineAlloc(ContextID, 4, 4);
  312. if (LUT == NULL) goto Error;
  313. nChannels = cmsChannelsOf(ColorSpace);
  314. CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL);
  315. if (CLUT == NULL) goto Error;
  316. if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error;
  317. if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) ||
  318. !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) ||
  319. !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels)))
  320. goto Error;
  321. // Create tags
  322. if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error;
  323. if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error;
  324. if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error;
  325. // cmsPipeline is already on virtual profile
  326. cmsPipelineFree(LUT);
  327. // Ok, done
  328. return hICC;
  329. Error:
  330. if (LUT != NULL)
  331. cmsPipelineFree(LUT);
  332. if (hICC != NULL)
  333. cmsCloseProfile(hICC);
  334. return NULL;
  335. }
  336. cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit)
  337. {
  338. return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit);
  339. }
  340. // Creates a fake Lab identity.
  341. cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
  342. {
  343. cmsHPROFILE hProfile;
  344. cmsPipeline* LUT = NULL;
  345. hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
  346. if (hProfile == NULL) return NULL;
  347. cmsSetProfileVersion(hProfile, 2.1);
  348. cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
  349. cmsSetColorSpace(hProfile, cmsSigLabData);
  350. cmsSetPCS(hProfile, cmsSigLabData);
  351. if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;
  352. // An identity LUT is all we need
  353. LUT = cmsPipelineAlloc(ContextID, 3, 3);
  354. if (LUT == NULL) goto Error;
  355. if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
  356. goto Error;
  357. if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
  358. cmsPipelineFree(LUT);
  359. return hProfile;
  360. Error:
  361. if (LUT != NULL)
  362. cmsPipelineFree(LUT);
  363. if (hProfile != NULL)
  364. cmsCloseProfile(hProfile);
  365. return NULL;
  366. }
  367. cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint)
  368. {
  369. return cmsCreateLab2ProfileTHR(NULL, WhitePoint);
  370. }
  371. // Creates a fake Lab V4 identity.
  372. cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint)
  373. {
  374. cmsHPROFILE hProfile;
  375. cmsPipeline* LUT = NULL;
  376. hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
  377. if (hProfile == NULL) return NULL;
  378. cmsSetProfileVersion(hProfile, 4.4);
  379. cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
  380. cmsSetColorSpace(hProfile, cmsSigLabData);
  381. cmsSetPCS(hProfile, cmsSigLabData);
  382. if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;
  383. // An empty LUTs is all we need
  384. LUT = cmsPipelineAlloc(ContextID, 3, 3);
  385. if (LUT == NULL) goto Error;
  386. if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
  387. goto Error;
  388. if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
  389. cmsPipelineFree(LUT);
  390. return hProfile;
  391. Error:
  392. if (LUT != NULL)
  393. cmsPipelineFree(LUT);
  394. if (hProfile != NULL)
  395. cmsCloseProfile(hProfile);
  396. return NULL;
  397. }
  398. cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint)
  399. {
  400. return cmsCreateLab4ProfileTHR(NULL, WhitePoint);
  401. }
  402. // Creates a fake XYZ identity
  403. cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID)
  404. {
  405. cmsHPROFILE hProfile;
  406. cmsPipeline* LUT = NULL;
  407. hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
  408. if (hProfile == NULL) return NULL;
  409. cmsSetProfileVersion(hProfile, 4.4);
  410. cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
  411. cmsSetColorSpace(hProfile, cmsSigXYZData);
  412. cmsSetPCS(hProfile, cmsSigXYZData);
  413. if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error;
  414. // An identity LUT is all we need
  415. LUT = cmsPipelineAlloc(ContextID, 3, 3);
  416. if (LUT == NULL) goto Error;
  417. if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3)))
  418. goto Error;
  419. if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
  420. cmsPipelineFree(LUT);
  421. return hProfile;
  422. Error:
  423. if (LUT != NULL)
  424. cmsPipelineFree(LUT);
  425. if (hProfile != NULL)
  426. cmsCloseProfile(hProfile);
  427. return NULL;
  428. }
  429. cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void)
  430. {
  431. return cmsCreateXYZProfileTHR(NULL);
  432. }
  433. //sRGB Curves are defined by:
  434. //
  435. //If R'sRGB,G'sRGB, B'sRGB < 0.04045
  436. //
  437. // R = R'sRGB / 12.92
  438. // G = G'sRGB / 12.92
  439. // B = B'sRGB / 12.92
  440. //
  441. //
  442. //else if R'sRGB,G'sRGB, B'sRGB >= 0.04045
  443. //
  444. // R = ((R'sRGB + 0.055) / 1.055)^2.4
  445. // G = ((G'sRGB + 0.055) / 1.055)^2.4
  446. // B = ((B'sRGB + 0.055) / 1.055)^2.4
  447. static
  448. cmsToneCurve* Build_sRGBGamma(cmsContext ContextID)
  449. {
  450. cmsFloat64Number Parameters[5];
  451. Parameters[0] = 2.4;
  452. Parameters[1] = 1. / 1.055;
  453. Parameters[2] = 0.055 / 1.055;
  454. Parameters[3] = 1. / 12.92;
  455. Parameters[4] = 0.04045;
  456. return cmsBuildParametricToneCurve(ContextID, 4, Parameters);
  457. }
  458. // Create the ICC virtual profile for sRGB space
  459. cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID)
  460. {
  461. cmsCIExyY D65 = { 0.3127, 0.3290, 1.0 };
  462. cmsCIExyYTRIPLE Rec709Primaries = {
  463. {0.6400, 0.3300, 1.0},
  464. {0.3000, 0.6000, 1.0},
  465. {0.1500, 0.0600, 1.0}
  466. };
  467. cmsToneCurve* Gamma22[3];
  468. cmsHPROFILE hsRGB;
  469. // cmsWhitePointFromTemp(&D65, 6504);
  470. Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID);
  471. if (Gamma22[0] == NULL) return NULL;
  472. hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22);
  473. cmsFreeToneCurve(Gamma22[0]);
  474. if (hsRGB == NULL) return NULL;
  475. if (!SetTextTags(hsRGB, L"sRGB built-in")) {
  476. cmsCloseProfile(hsRGB);
  477. return NULL;
  478. }
  479. return hsRGB;
  480. }
  481. cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void)
  482. {
  483. return cmsCreate_sRGBProfileTHR(NULL);
  484. }
  485. /**
  486. * Oklab colorspace profile (experimental)
  487. *
  488. * This virtual profile cannot be saved as an ICC file
  489. */
  490. cmsHPROFILE cmsCreate_OkLabProfile(cmsContext ctx)
  491. {
  492. cmsStage* XYZPCS = _cmsStageNormalizeFromXyzFloat(ctx);
  493. cmsStage* PCSXYZ = _cmsStageNormalizeToXyzFloat(ctx);
  494. const double M_D65_D50[] =
  495. {
  496. 1.047886, 0.022919, -0.050216,
  497. 0.029582, 0.990484, -0.017079,
  498. -0.009252, 0.015073, 0.751678
  499. };
  500. const double M_D50_D65[] =
  501. {
  502. 0.955512609517083, -0.023073214184645, 0.063308961782107,
  503. -0.028324949364887, 1.009942432477107, 0.021054814890112,
  504. 0.012328875695483, -0.020535835374141, 1.330713916450354
  505. };
  506. cmsStage* D65toD50 = cmsStageAllocMatrix(ctx, 3, 3, M_D65_D50, NULL);
  507. cmsStage* D50toD65 = cmsStageAllocMatrix(ctx, 3, 3, M_D50_D65, NULL);
  508. const double M_D65_LMS[] =
  509. {
  510. 0.8189330101, 0.3618667424, -0.1288597137,
  511. 0.0329845436, 0.9293118715, 0.0361456387,
  512. 0.0482003018, 0.2643662691, 0.6338517070
  513. };
  514. const double M_LMS_D65[] =
  515. {
  516. 1.227013851103521, -0.557799980651822, 0.281256148966468,
  517. -0.040580178423281, 1.112256869616830, -0.071676678665601,
  518. -0.076381284505707, -0.421481978418013, 1.586163220440795
  519. };
  520. cmsStage* D65toLMS = cmsStageAllocMatrix(ctx, 3, 3, M_D65_LMS, NULL);
  521. cmsStage* LMStoD65 = cmsStageAllocMatrix(ctx, 3, 3, M_LMS_D65, NULL);
  522. cmsToneCurve* CubeRoot = cmsBuildGamma(ctx, 1.0 / 3.0);
  523. cmsToneCurve* Cube = cmsBuildGamma(ctx, 3.0);
  524. cmsToneCurve* Roots[3] = { CubeRoot, CubeRoot, CubeRoot };
  525. cmsToneCurve* Cubes[3] = { Cube, Cube, Cube };
  526. cmsStage* NonLinearityFw = cmsStageAllocToneCurves(ctx, 3, Roots);
  527. cmsStage* NonLinearityRv = cmsStageAllocToneCurves(ctx, 3, Cubes);
  528. const double M_LMSprime_OkLab[] =
  529. {
  530. 0.2104542553, 0.7936177850, -0.0040720468,
  531. 1.9779984951, -2.4285922050, 0.4505937099,
  532. 0.0259040371, 0.7827717662, -0.8086757660
  533. };
  534. const double M_OkLab_LMSprime[] =
  535. {
  536. 0.999999998450520, 0.396337792173768, 0.215803758060759,
  537. 1.000000008881761, -0.105561342323656, -0.063854174771706,
  538. 1.000000054672411, -0.089484182094966, -1.291485537864092
  539. };
  540. cmsStage* LMSprime_OkLab = cmsStageAllocMatrix(ctx, 3, 3, M_LMSprime_OkLab, NULL);
  541. cmsStage* OkLab_LMSprime = cmsStageAllocMatrix(ctx, 3, 3, M_OkLab_LMSprime, NULL);
  542. cmsPipeline* AToB = cmsPipelineAlloc(ctx, 3, 3);
  543. cmsPipeline* BToA = cmsPipelineAlloc(ctx, 3, 3);
  544. cmsHPROFILE hProfile = cmsCreateProfilePlaceholder(ctx);
  545. cmsSetProfileVersion(hProfile, 4.4);
  546. cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);
  547. cmsSetColorSpace(hProfile, cmsSig3colorData);
  548. cmsSetPCS(hProfile, cmsSigXYZData);
  549. cmsSetHeaderRenderingIntent(hProfile, INTENT_RELATIVE_COLORIMETRIC);
  550. /**
  551. * Conversion PCS (XYZ/D50) to OkLab
  552. */
  553. if (!cmsPipelineInsertStage(BToA, cmsAT_END, PCSXYZ)) goto error;
  554. if (!cmsPipelineInsertStage(BToA, cmsAT_END, D50toD65)) goto error;
  555. if (!cmsPipelineInsertStage(BToA, cmsAT_END, D65toLMS)) goto error;
  556. if (!cmsPipelineInsertStage(BToA, cmsAT_END, NonLinearityFw)) goto error;
  557. if (!cmsPipelineInsertStage(BToA, cmsAT_END, LMSprime_OkLab)) goto error;
  558. if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA)) goto error;
  559. if (!cmsPipelineInsertStage(AToB, cmsAT_END, OkLab_LMSprime)) goto error;
  560. if (!cmsPipelineInsertStage(AToB, cmsAT_END, NonLinearityRv)) goto error;
  561. if (!cmsPipelineInsertStage(AToB, cmsAT_END, LMStoD65)) goto error;
  562. if (!cmsPipelineInsertStage(AToB, cmsAT_END, D65toD50)) goto error;
  563. if (!cmsPipelineInsertStage(AToB, cmsAT_END, XYZPCS)) goto error;
  564. if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB)) goto error;
  565. cmsPipelineFree(BToA);
  566. cmsPipelineFree(AToB);
  567. cmsFreeToneCurve(CubeRoot);
  568. cmsFreeToneCurve(Cube);
  569. return hProfile;
  570. error:
  571. cmsPipelineFree(BToA);
  572. cmsPipelineFree(AToB);
  573. cmsFreeToneCurve(CubeRoot);
  574. cmsFreeToneCurve(Cube);
  575. cmsCloseProfile(hProfile);
  576. return NULL;
  577. }
  578. typedef struct {
  579. cmsFloat64Number Brightness;
  580. cmsFloat64Number Contrast;
  581. cmsFloat64Number Hue;
  582. cmsFloat64Number Saturation;
  583. cmsBool lAdjustWP;
  584. cmsCIEXYZ WPsrc, WPdest;
  585. } BCHSWADJUSTS, *LPBCHSWADJUSTS;
  586. static
  587. int bchswSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
  588. {
  589. cmsCIELab LabIn, LabOut;
  590. cmsCIELCh LChIn, LChOut;
  591. cmsCIEXYZ XYZ;
  592. LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
  593. cmsLabEncoded2Float(&LabIn, In);
  594. cmsLab2LCh(&LChIn, &LabIn);
  595. // Do some adjusts on LCh
  596. LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
  597. LChOut.C = LChIn.C + bchsw -> Saturation;
  598. LChOut.h = LChIn.h + bchsw -> Hue;
  599. cmsLCh2Lab(&LabOut, &LChOut);
  600. // Move white point in Lab
  601. if (bchsw->lAdjustWP) {
  602. cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut);
  603. cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ);
  604. }
  605. // Back to encoded
  606. cmsFloat2LabEncoded(Out, &LabOut);
  607. return TRUE;
  608. }
  609. // Creates an abstract profile operating in Lab space for Brightness,
  610. // contrast, Saturation and white point displacement
  611. cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
  612. cmsUInt32Number nLUTPoints,
  613. cmsFloat64Number Bright,
  614. cmsFloat64Number Contrast,
  615. cmsFloat64Number Hue,
  616. cmsFloat64Number Saturation,
  617. cmsUInt32Number TempSrc,
  618. cmsUInt32Number TempDest)
  619. {
  620. cmsHPROFILE hICC;
  621. cmsPipeline* Pipeline;
  622. BCHSWADJUSTS bchsw;
  623. cmsCIExyY WhitePnt;
  624. cmsStage* CLUT;
  625. cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
  626. cmsUInt32Number i;
  627. bchsw.Brightness = Bright;
  628. bchsw.Contrast = Contrast;
  629. bchsw.Hue = Hue;
  630. bchsw.Saturation = Saturation;
  631. if (TempSrc == TempDest) {
  632. bchsw.lAdjustWP = FALSE;
  633. }
  634. else {
  635. bchsw.lAdjustWP = TRUE;
  636. cmsWhitePointFromTemp(&WhitePnt, TempSrc);
  637. cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
  638. cmsWhitePointFromTemp(&WhitePnt, TempDest);
  639. cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
  640. }
  641. hICC = cmsCreateProfilePlaceholder(ContextID);
  642. if (!hICC) // can't allocate
  643. return NULL;
  644. cmsSetDeviceClass(hICC, cmsSigAbstractClass);
  645. cmsSetColorSpace(hICC, cmsSigLabData);
  646. cmsSetPCS(hICC, cmsSigLabData);
  647. cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL);
  648. // Creates a Pipeline with 3D grid only
  649. Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
  650. if (Pipeline == NULL) {
  651. cmsCloseProfile(hICC);
  652. return NULL;
  653. }
  654. for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
  655. CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
  656. if (CLUT == NULL) goto Error;
  657. if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
  658. // Shouldn't reach here
  659. goto Error;
  660. }
  661. if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
  662. goto Error;
  663. }
  664. // Create tags
  665. if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
  666. cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
  667. cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
  668. // Pipeline is already on virtual profile
  669. cmsPipelineFree(Pipeline);
  670. // Ok, done
  671. return hICC;
  672. Error:
  673. cmsPipelineFree(Pipeline);
  674. cmsCloseProfile(hICC);
  675. return NULL;
  676. }
  677. CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints,
  678. cmsFloat64Number Bright,
  679. cmsFloat64Number Contrast,
  680. cmsFloat64Number Hue,
  681. cmsFloat64Number Saturation,
  682. cmsUInt32Number TempSrc,
  683. cmsUInt32Number TempDest)
  684. {
  685. return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest);
  686. }
  687. // Creates a fake NULL profile. This profile return 1 channel as always 0.
  688. // Is useful only for gamut checking tricks
  689. cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID)
  690. {
  691. cmsHPROFILE hProfile;
  692. cmsPipeline* LUT = NULL;
  693. cmsStage* PostLin;
  694. cmsStage* OutLin;
  695. cmsToneCurve* EmptyTab[3];
  696. cmsUInt16Number Zero[2] = { 0, 0 };
  697. const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 };
  698. hProfile = cmsCreateProfilePlaceholder(ContextID);
  699. if (!hProfile) // can't allocate
  700. return NULL;
  701. cmsSetProfileVersion(hProfile, 4.4);
  702. if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
  703. cmsSetDeviceClass(hProfile, cmsSigOutputClass);
  704. cmsSetColorSpace(hProfile, cmsSigGrayData);
  705. cmsSetPCS(hProfile, cmsSigLabData);
  706. // Create a valid ICC 4 structure
  707. LUT = cmsPipelineAlloc(ContextID, 3, 1);
  708. if (LUT == NULL) goto Error;
  709. EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero);
  710. PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab);
  711. OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab);
  712. cmsFreeToneCurve(EmptyTab[0]);
  713. if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin))
  714. goto Error;
  715. if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL)))
  716. goto Error;
  717. if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin))
  718. goto Error;
  719. if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error;
  720. if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error;
  721. cmsPipelineFree(LUT);
  722. return hProfile;
  723. Error:
  724. if (LUT != NULL)
  725. cmsPipelineFree(LUT);
  726. if (hProfile != NULL)
  727. cmsCloseProfile(hProfile);
  728. return NULL;
  729. }
  730. cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void)
  731. {
  732. return cmsCreateNULLProfileTHR(NULL);
  733. }
  734. static
  735. int IsPCS(cmsColorSpaceSignature ColorSpace)
  736. {
  737. return (ColorSpace == cmsSigXYZData ||
  738. ColorSpace == cmsSigLabData);
  739. }
  740. static
  741. void FixColorSpaces(cmsHPROFILE hProfile,
  742. cmsColorSpaceSignature ColorSpace,
  743. cmsColorSpaceSignature PCS,
  744. cmsUInt32Number dwFlags)
  745. {
  746. if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
  747. if (IsPCS(ColorSpace) && IsPCS(PCS)) {
  748. cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
  749. cmsSetColorSpace(hProfile, ColorSpace);
  750. cmsSetPCS(hProfile, PCS);
  751. return;
  752. }
  753. if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
  754. cmsSetDeviceClass(hProfile, cmsSigOutputClass);
  755. cmsSetPCS(hProfile, ColorSpace);
  756. cmsSetColorSpace(hProfile, PCS);
  757. return;
  758. }
  759. if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
  760. cmsSetDeviceClass(hProfile, cmsSigInputClass);
  761. cmsSetColorSpace(hProfile, ColorSpace);
  762. cmsSetPCS(hProfile, PCS);
  763. return;
  764. }
  765. }
  766. cmsSetDeviceClass(hProfile, cmsSigLinkClass);
  767. cmsSetColorSpace(hProfile, ColorSpace);
  768. cmsSetPCS(hProfile, PCS);
  769. }
  770. // This function creates a named color profile dumping all the contents of transform to a single profile
  771. // In this way, LittleCMS may be used to "group" several named color databases into a single profile.
  772. // It has, however, several minor limitations. PCS is always Lab, which is not very critic since this
  773. // is the normal PCS for named color profiles.
  774. static
  775. cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
  776. {
  777. _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
  778. cmsHPROFILE hICC = NULL;
  779. cmsUInt32Number i, nColors;
  780. cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL;
  781. // Create an empty placeholder
  782. hICC = cmsCreateProfilePlaceholder(v->ContextID);
  783. if (hICC == NULL) return NULL;
  784. // Critical information
  785. cmsSetDeviceClass(hICC, cmsSigNamedColorClass);
  786. cmsSetColorSpace(hICC, v ->ExitColorSpace);
  787. cmsSetPCS(hICC, cmsSigLabData);
  788. // Tag profile with information
  789. if (!SetTextTags(hICC, L"Named color devicelink")) goto Error;
  790. Original = cmsGetNamedColorList(xform);
  791. if (Original == NULL) goto Error;
  792. nColors = cmsNamedColorCount(Original);
  793. nc2 = cmsDupNamedColorList(Original);
  794. if (nc2 == NULL) goto Error;
  795. // Colorant count now depends on the output space
  796. nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut);
  797. // Make sure we have proper formatters
  798. cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
  799. FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
  800. | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOfColorSpace(v ->ExitColorSpace)));
  801. // Apply the transfor to colorants.
  802. for (i=0; i < nColors; i++) {
  803. cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
  804. }
  805. if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error;
  806. cmsFreeNamedColorList(nc2);
  807. return hICC;
  808. Error:
  809. if (hICC != NULL) cmsCloseProfile(hICC);
  810. return NULL;
  811. }
  812. // This structure holds information about which MPU can be stored on a profile based on the version
  813. typedef struct {
  814. cmsBool IsV4; // Is a V4 tag?
  815. cmsTagSignature RequiredTag; // Set to 0 for both types
  816. cmsTagTypeSignature LutType; // The LUT type
  817. int nTypes; // Number of types (up to 5)
  818. cmsStageSignature MpeTypes[5]; // 5 is the maximum number
  819. } cmsAllowedLUT;
  820. #define cmsSig0 ((cmsTagSignature) 0)
  821. static const cmsAllowedLUT AllowedLUTTypes[] = {
  822. { FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
  823. { FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
  824. { FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } },
  825. { TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } },
  826. { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } },
  827. { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } },
  828. { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
  829. { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }},
  830. { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }},
  831. { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }},
  832. { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}
  833. };
  834. #define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT))
  835. // Check a single entry
  836. static
  837. cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
  838. {
  839. cmsStage* mpe;
  840. int n;
  841. for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
  842. if (n >= Tab ->nTypes) return FALSE;
  843. if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
  844. }
  845. return (n == Tab ->nTypes);
  846. }
  847. static
  848. const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag)
  849. {
  850. cmsUInt32Number n;
  851. for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) {
  852. const cmsAllowedLUT* Tab = AllowedLUTTypes + n;
  853. if (IsV4 ^ Tab -> IsV4) continue;
  854. if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue;
  855. if (CheckOne(Tab, Lut)) return Tab;
  856. }
  857. return NULL;
  858. }
  859. // Does convert a transform into a device link profile
  860. cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
  861. {
  862. cmsHPROFILE hProfile = NULL;
  863. cmsUInt32Number FrmIn, FrmOut;
  864. cmsInt32Number ChansIn, ChansOut;
  865. int ColorSpaceBitsIn, ColorSpaceBitsOut;
  866. _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
  867. cmsPipeline* LUT = NULL;
  868. cmsStage* mpe;
  869. cmsContext ContextID = cmsGetTransformContextID(hTransform);
  870. const cmsAllowedLUT* AllowedLUT;
  871. cmsTagSignature DestinationTag;
  872. cmsProfileClassSignature deviceClass;
  873. _cmsAssert(hTransform != NULL);
  874. // Check if the pipeline holding is valid
  875. if (xform -> Lut == NULL) return NULL;
  876. // Get the first mpe to check for named color
  877. mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);
  878. // Check if is a named color transform
  879. if (mpe != NULL) {
  880. if (cmsStageType(mpe) == cmsSigNamedColorElemType) {
  881. return CreateNamedColorDevicelink(hTransform);
  882. }
  883. }
  884. // First thing to do is to get a copy of the transformation
  885. LUT = cmsPipelineDup(xform ->Lut);
  886. if (LUT == NULL) return NULL;
  887. // Time to fix the Lab2/Lab4 issue.
  888. if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) {
  889. if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID)))
  890. goto Error;
  891. }
  892. // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments
  893. if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) {
  894. dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
  895. if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID)))
  896. goto Error;
  897. }
  898. hProfile = cmsCreateProfilePlaceholder(ContextID);
  899. if (!hProfile) goto Error; // can't allocate
  900. cmsSetProfileVersion(hProfile, Version);
  901. FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags);
  902. // Optimize the LUT and precalculate a devicelink
  903. ChansIn = cmsChannelsOfColorSpace(xform -> EntryColorSpace);
  904. ChansOut = cmsChannelsOfColorSpace(xform -> ExitColorSpace);
  905. ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace);
  906. ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
  907. FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2);
  908. FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2);
  909. deviceClass = cmsGetDeviceClass(hProfile);
  910. if (deviceClass == cmsSigOutputClass)
  911. DestinationTag = cmsSigBToA0Tag;
  912. else
  913. DestinationTag = cmsSigAToB0Tag;
  914. // Check if the profile/version can store the result
  915. if (dwFlags & cmsFLAGS_FORCE_CLUT)
  916. AllowedLUT = NULL;
  917. else
  918. AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
  919. if (AllowedLUT == NULL) {
  920. // Try to optimize
  921. _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
  922. AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
  923. }
  924. // If no way, then force CLUT that for sure can be written
  925. if (AllowedLUT == NULL) {
  926. cmsStage* FirstStage;
  927. cmsStage* LastStage;
  928. dwFlags |= cmsFLAGS_FORCE_CLUT;
  929. _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags);
  930. // Put identity curves if needed
  931. FirstStage = cmsPipelineGetPtrToFirstStage(LUT);
  932. if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType)
  933. if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn)))
  934. goto Error;
  935. LastStage = cmsPipelineGetPtrToLastStage(LUT);
  936. if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType)
  937. if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut)))
  938. goto Error;
  939. AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag);
  940. }
  941. // Somethings is wrong...
  942. if (AllowedLUT == NULL) {
  943. goto Error;
  944. }
  945. if (dwFlags & cmsFLAGS_8BITS_DEVICELINK)
  946. cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE);
  947. // Tag profile with information
  948. if (!SetTextTags(hProfile, L"devicelink")) goto Error;
  949. // Store result
  950. if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error;
  951. if (xform -> InputColorant != NULL) {
  952. if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error;
  953. }
  954. if (xform -> OutputColorant != NULL) {
  955. if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error;
  956. }
  957. if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) {
  958. if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error;
  959. }
  960. // Set the white point
  961. if (deviceClass == cmsSigInputClass) {
  962. if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error;
  963. }
  964. else {
  965. if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error;
  966. }
  967. // Per 7.2.15 in spec 4.3
  968. cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent);
  969. cmsPipelineFree(LUT);
  970. return hProfile;
  971. Error:
  972. if (LUT != NULL) cmsPipelineFree(LUT);
  973. cmsCloseProfile(hProfile);
  974. return NULL;
  975. }