cmsxform.c 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458
  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. // Transformations stuff
  28. // -----------------------------------------------------------------------
  29. #define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0
  30. // The Context0 observer adaptation state.
  31. _cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
  32. // Init and duplicate observer adaptation state
  33. void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx,
  34. const struct _cmsContext_struct* src)
  35. {
  36. static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
  37. void* from;
  38. if (src != NULL) {
  39. from = src ->chunks[AdaptationStateContext];
  40. }
  41. else {
  42. from = &AdaptationStateChunk;
  43. }
  44. ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType));
  45. }
  46. // Sets adaptation state for absolute colorimetric intent in the given context. Adaptation state applies on all
  47. // but cmsCreateExtendedTransformTHR(). Little CMS can handle incomplete adaptation states.
  48. cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d)
  49. {
  50. cmsFloat64Number prev;
  51. _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
  52. // Get previous value for return
  53. prev = ptr ->AdaptationState;
  54. // Set the value if d is positive or zero
  55. if (d >= 0.0) {
  56. ptr ->AdaptationState = d;
  57. }
  58. // Always return previous value
  59. return prev;
  60. }
  61. // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
  62. cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
  63. {
  64. return cmsSetAdaptationStateTHR(NULL, d);
  65. }
  66. // -----------------------------------------------------------------------
  67. // Alarm codes for 16-bit transformations, because the fixed range of containers there are
  68. // no values left to mark out of gamut.
  69. #define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  70. _cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
  71. // Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be
  72. // encoded in 16 bits.
  73. void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
  74. {
  75. _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
  76. _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
  77. memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));
  78. }
  79. // Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context.
  80. // Values are meant to be encoded in 16 bits.
  81. void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
  82. {
  83. _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
  84. _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
  85. memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
  86. }
  87. void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
  88. {
  89. _cmsAssert(NewAlarm != NULL);
  90. cmsSetAlarmCodesTHR(NULL, NewAlarm);
  91. }
  92. void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
  93. {
  94. _cmsAssert(OldAlarm != NULL);
  95. cmsGetAlarmCodesTHR(NULL, OldAlarm);
  96. }
  97. // Init and duplicate alarm codes
  98. void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx,
  99. const struct _cmsContext_struct* src)
  100. {
  101. static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
  102. void* from;
  103. if (src != NULL) {
  104. from = src ->chunks[AlarmCodesContext];
  105. }
  106. else {
  107. from = &AlarmCodesChunk;
  108. }
  109. ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));
  110. }
  111. // -----------------------------------------------------------------------
  112. // Get rid of transform resources
  113. void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
  114. {
  115. _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
  116. _cmsAssert(p != NULL);
  117. if (p -> GamutCheck)
  118. cmsPipelineFree(p -> GamutCheck);
  119. if (p -> Lut)
  120. cmsPipelineFree(p -> Lut);
  121. if (p ->InputColorant)
  122. cmsFreeNamedColorList(p ->InputColorant);
  123. if (p -> OutputColorant)
  124. cmsFreeNamedColorList(p ->OutputColorant);
  125. if (p ->Sequence)
  126. cmsFreeProfileSequenceDescription(p ->Sequence);
  127. if (p ->UserData)
  128. p ->FreeUserData(p ->ContextID, p ->UserData);
  129. _cmsFree(p ->ContextID, (void *) p);
  130. }
  131. static
  132. cmsUInt32Number PixelSize(cmsUInt32Number Format)
  133. {
  134. cmsUInt32Number fmt_bytes = T_BYTES(Format);
  135. // For double, the T_BYTES field is zero
  136. if (fmt_bytes == 0)
  137. return sizeof(cmsUInt64Number);
  138. // Otherwise, it is already correct for all formats
  139. return fmt_bytes;
  140. }
  141. // Apply transform.
  142. void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
  143. const void* InputBuffer,
  144. void* OutputBuffer,
  145. cmsUInt32Number Size)
  146. {
  147. _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
  148. cmsStride stride;
  149. stride.BytesPerLineIn = 0; // Not used
  150. stride.BytesPerLineOut = 0;
  151. stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat);
  152. stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat);
  153. p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
  154. }
  155. // This is a legacy stride for planar
  156. void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform,
  157. const void* InputBuffer,
  158. void* OutputBuffer,
  159. cmsUInt32Number Size, cmsUInt32Number Stride)
  160. {
  161. _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
  162. cmsStride stride;
  163. stride.BytesPerLineIn = 0;
  164. stride.BytesPerLineOut = 0;
  165. stride.BytesPerPlaneIn = Stride;
  166. stride.BytesPerPlaneOut = Stride;
  167. p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
  168. }
  169. // This is the "fast" function for plugins
  170. void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform,
  171. const void* InputBuffer,
  172. void* OutputBuffer,
  173. cmsUInt32Number PixelsPerLine,
  174. cmsUInt32Number LineCount,
  175. cmsUInt32Number BytesPerLineIn,
  176. cmsUInt32Number BytesPerLineOut,
  177. cmsUInt32Number BytesPerPlaneIn,
  178. cmsUInt32Number BytesPerPlaneOut)
  179. {
  180. _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
  181. cmsStride stride;
  182. stride.BytesPerLineIn = BytesPerLineIn;
  183. stride.BytesPerLineOut = BytesPerLineOut;
  184. stride.BytesPerPlaneIn = BytesPerPlaneIn;
  185. stride.BytesPerPlaneOut = BytesPerPlaneOut;
  186. p->xform(p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride);
  187. }
  188. // Transform routines ----------------------------------------------------------------------------------------------------------
  189. // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
  190. // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
  191. static
  192. void FloatXFORM(_cmsTRANSFORM* p,
  193. const void* in,
  194. void* out,
  195. cmsUInt32Number PixelsPerLine,
  196. cmsUInt32Number LineCount,
  197. const cmsStride* Stride)
  198. {
  199. cmsUInt8Number* accum;
  200. cmsUInt8Number* output;
  201. cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
  202. cmsFloat32Number OutOfGamut;
  203. cmsUInt32Number i, j, c, strideIn, strideOut;
  204. _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
  205. strideIn = 0;
  206. strideOut = 0;
  207. memset(fIn, 0, sizeof(fIn));
  208. memset(fOut, 0, sizeof(fOut));
  209. for (i = 0; i < LineCount; i++) {
  210. accum = (cmsUInt8Number*)in + strideIn;
  211. output = (cmsUInt8Number*)out + strideOut;
  212. for (j = 0; j < PixelsPerLine; j++) {
  213. accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
  214. // Any gamut check to do?
  215. if (p->GamutCheck != NULL) {
  216. // Evaluate gamut marker.
  217. cmsPipelineEvalFloat(fIn, &OutOfGamut, p->GamutCheck);
  218. // Is current color out of gamut?
  219. if (OutOfGamut > 0.0) {
  220. // Certainly, out of gamut
  221. for (c = 0; c < cmsMAXCHANNELS; c++)
  222. fOut[c] = -1.0;
  223. }
  224. else {
  225. // No, proceed normally
  226. cmsPipelineEvalFloat(fIn, fOut, p->Lut);
  227. }
  228. }
  229. else {
  230. // No gamut check at all
  231. cmsPipelineEvalFloat(fIn, fOut, p->Lut);
  232. }
  233. output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut);
  234. }
  235. strideIn += Stride->BytesPerLineIn;
  236. strideOut += Stride->BytesPerLineOut;
  237. }
  238. }
  239. static
  240. void NullFloatXFORM(_cmsTRANSFORM* p,
  241. const void* in,
  242. void* out,
  243. cmsUInt32Number PixelsPerLine,
  244. cmsUInt32Number LineCount,
  245. const cmsStride* Stride)
  246. {
  247. cmsUInt8Number* accum;
  248. cmsUInt8Number* output;
  249. cmsFloat32Number fIn[cmsMAXCHANNELS];
  250. cmsUInt32Number i, j, strideIn, strideOut;
  251. _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
  252. strideIn = 0;
  253. strideOut = 0;
  254. memset(fIn, 0, sizeof(fIn));
  255. for (i = 0; i < LineCount; i++) {
  256. accum = (cmsUInt8Number*) in + strideIn;
  257. output = (cmsUInt8Number*) out + strideOut;
  258. for (j = 0; j < PixelsPerLine; j++) {
  259. accum = p->FromInputFloat(p, fIn, accum, Stride ->BytesPerPlaneIn);
  260. output = p->ToOutputFloat(p, fIn, output, Stride->BytesPerPlaneOut);
  261. }
  262. strideIn += Stride->BytesPerLineIn;
  263. strideOut += Stride->BytesPerLineOut;
  264. }
  265. }
  266. // 16 bit precision -----------------------------------------------------------------------------------------------------------
  267. // Null transformation, only applies formatters. No cache
  268. static
  269. void NullXFORM(_cmsTRANSFORM* p,
  270. const void* in,
  271. void* out,
  272. cmsUInt32Number PixelsPerLine,
  273. cmsUInt32Number LineCount,
  274. const cmsStride* Stride)
  275. {
  276. cmsUInt8Number* accum;
  277. cmsUInt8Number* output;
  278. cmsUInt16Number wIn[cmsMAXCHANNELS];
  279. cmsUInt32Number i, j, strideIn, strideOut;
  280. _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
  281. strideIn = 0;
  282. strideOut = 0;
  283. memset(wIn, 0, sizeof(wIn));
  284. for (i = 0; i < LineCount; i++) {
  285. accum = (cmsUInt8Number*)in + strideIn;
  286. output = (cmsUInt8Number*)out + strideOut;
  287. for (j = 0; j < PixelsPerLine; j++) {
  288. accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
  289. output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut);
  290. }
  291. strideIn += Stride->BytesPerLineIn;
  292. strideOut += Stride->BytesPerLineOut;
  293. }
  294. }
  295. // No gamut check, no cache, 16 bits
  296. static
  297. void PrecalculatedXFORM(_cmsTRANSFORM* p,
  298. const void* in,
  299. void* out,
  300. cmsUInt32Number PixelsPerLine,
  301. cmsUInt32Number LineCount,
  302. const cmsStride* Stride)
  303. {
  304. CMSREGISTER cmsUInt8Number* accum;
  305. CMSREGISTER cmsUInt8Number* output;
  306. cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
  307. cmsUInt32Number i, j, strideIn, strideOut;
  308. _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
  309. strideIn = 0;
  310. strideOut = 0;
  311. memset(wIn, 0, sizeof(wIn));
  312. memset(wOut, 0, sizeof(wOut));
  313. for (i = 0; i < LineCount; i++) {
  314. accum = (cmsUInt8Number*)in + strideIn;
  315. output = (cmsUInt8Number*)out + strideOut;
  316. for (j = 0; j < PixelsPerLine; j++) {
  317. accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
  318. p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
  319. output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
  320. }
  321. strideIn += Stride->BytesPerLineIn;
  322. strideOut += Stride->BytesPerLineOut;
  323. }
  324. }
  325. // Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
  326. static
  327. void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
  328. const cmsUInt16Number wIn[],
  329. cmsUInt16Number wOut[])
  330. {
  331. cmsUInt16Number wOutOfGamut;
  332. p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
  333. if (wOutOfGamut >= 1) {
  334. cmsUInt32Number i;
  335. _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
  336. for (i=0; i < p ->Lut->OutputChannels; i++) {
  337. wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
  338. }
  339. }
  340. else
  341. p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
  342. }
  343. // Gamut check, No cache, 16 bits.
  344. static
  345. void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
  346. const void* in,
  347. void* out,
  348. cmsUInt32Number PixelsPerLine,
  349. cmsUInt32Number LineCount,
  350. const cmsStride* Stride)
  351. {
  352. cmsUInt8Number* accum;
  353. cmsUInt8Number* output;
  354. cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
  355. cmsUInt32Number i, j, strideIn, strideOut;
  356. _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
  357. strideIn = 0;
  358. strideOut = 0;
  359. memset(wIn, 0, sizeof(wIn));
  360. memset(wOut, 0, sizeof(wOut));
  361. for (i = 0; i < LineCount; i++) {
  362. accum = (cmsUInt8Number*)in + strideIn;
  363. output = (cmsUInt8Number*)out + strideOut;
  364. for (j = 0; j < PixelsPerLine; j++) {
  365. accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
  366. TransformOnePixelWithGamutCheck(p, wIn, wOut);
  367. output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
  368. }
  369. strideIn += Stride->BytesPerLineIn;
  370. strideOut += Stride->BytesPerLineOut;
  371. }
  372. }
  373. // No gamut check, Cache, 16 bits,
  374. static
  375. void CachedXFORM(_cmsTRANSFORM* p,
  376. const void* in,
  377. void* out,
  378. cmsUInt32Number PixelsPerLine,
  379. cmsUInt32Number LineCount,
  380. const cmsStride* Stride)
  381. {
  382. cmsUInt8Number* accum;
  383. cmsUInt8Number* output;
  384. cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
  385. _cmsCACHE Cache;
  386. cmsUInt32Number i, j, strideIn, strideOut;
  387. _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
  388. // Empty buffers for quick memcmp
  389. memset(wIn, 0, sizeof(wIn));
  390. memset(wOut, 0, sizeof(wOut));
  391. // Get copy of zero cache
  392. memcpy(&Cache, &p->Cache, sizeof(Cache));
  393. strideIn = 0;
  394. strideOut = 0;
  395. for (i = 0; i < LineCount; i++) {
  396. accum = (cmsUInt8Number*)in + strideIn;
  397. output = (cmsUInt8Number*)out + strideOut;
  398. for (j = 0; j < PixelsPerLine; j++) {
  399. accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
  400. if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
  401. memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
  402. }
  403. else {
  404. p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
  405. memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
  406. memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
  407. }
  408. output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
  409. }
  410. strideIn += Stride->BytesPerLineIn;
  411. strideOut += Stride->BytesPerLineOut;
  412. }
  413. }
  414. // All those nice features together
  415. static
  416. void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
  417. const void* in,
  418. void* out,
  419. cmsUInt32Number PixelsPerLine,
  420. cmsUInt32Number LineCount,
  421. const cmsStride* Stride)
  422. {
  423. cmsUInt8Number* accum;
  424. cmsUInt8Number* output;
  425. cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
  426. _cmsCACHE Cache;
  427. cmsUInt32Number i, j, strideIn, strideOut;
  428. _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
  429. // Empty buffers for quick memcmp
  430. memset(wIn, 0, sizeof(wIn));
  431. memset(wOut, 0, sizeof(wOut));
  432. // Get copy of zero cache
  433. memcpy(&Cache, &p->Cache, sizeof(Cache));
  434. strideIn = 0;
  435. strideOut = 0;
  436. for (i = 0; i < LineCount; i++) {
  437. accum = (cmsUInt8Number*)in + strideIn;
  438. output = (cmsUInt8Number*)out + strideOut;
  439. for (j = 0; j < PixelsPerLine; j++) {
  440. accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
  441. if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
  442. memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
  443. }
  444. else {
  445. TransformOnePixelWithGamutCheck(p, wIn, wOut);
  446. memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
  447. memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
  448. }
  449. output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
  450. }
  451. strideIn += Stride->BytesPerLineIn;
  452. strideOut += Stride->BytesPerLineOut;
  453. }
  454. }
  455. // Transform plug-ins ----------------------------------------------------------------------------------------------------
  456. // List of used-defined transform factories
  457. typedef struct _cmsTransformCollection_st {
  458. _cmsTransform2Factory Factory;
  459. cmsBool OldXform; // Factory returns xform function in the old style
  460. struct _cmsTransformCollection_st *Next;
  461. } _cmsTransformCollection;
  462. // The linked list head
  463. _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
  464. // Duplicates the zone of memory used by the plug-in in the new context
  465. static
  466. void DupPluginTransformList(struct _cmsContext_struct* ctx,
  467. const struct _cmsContext_struct* src)
  468. {
  469. _cmsTransformPluginChunkType newHead = { NULL };
  470. _cmsTransformCollection* entry;
  471. _cmsTransformCollection* Anterior = NULL;
  472. _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
  473. // Walk the list copying all nodes
  474. for (entry = head->TransformCollection;
  475. entry != NULL;
  476. entry = entry ->Next) {
  477. _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
  478. if (newEntry == NULL)
  479. return;
  480. // We want to keep the linked list order, so this is a little bit tricky
  481. newEntry -> Next = NULL;
  482. if (Anterior)
  483. Anterior -> Next = newEntry;
  484. Anterior = newEntry;
  485. if (newHead.TransformCollection == NULL)
  486. newHead.TransformCollection = newEntry;
  487. }
  488. ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
  489. }
  490. // Allocates memory for transform plugin factory
  491. void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx,
  492. const struct _cmsContext_struct* src)
  493. {
  494. if (src != NULL) {
  495. // Copy all linked list
  496. DupPluginTransformList(ctx, src);
  497. }
  498. else {
  499. static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
  500. ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
  501. }
  502. }
  503. // Adaptor for old versions of plug-in
  504. static
  505. void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo,
  506. const void* InputBuffer,
  507. void* OutputBuffer,
  508. cmsUInt32Number PixelsPerLine,
  509. cmsUInt32Number LineCount,
  510. const cmsStride* Stride)
  511. {
  512. cmsUInt32Number i, strideIn, strideOut;
  513. _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride);
  514. strideIn = 0;
  515. strideOut = 0;
  516. for (i = 0; i < LineCount; i++) {
  517. void *accum = (cmsUInt8Number*)InputBuffer + strideIn;
  518. void *output = (cmsUInt8Number*)OutputBuffer + strideOut;
  519. CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn);
  520. strideIn += Stride->BytesPerLineIn;
  521. strideOut += Stride->BytesPerLineOut;
  522. }
  523. }
  524. // Register new ways to transform
  525. cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
  526. {
  527. cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
  528. _cmsTransformCollection* fl;
  529. _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
  530. if (Data == NULL) {
  531. // Free the chain. Memory is safely freed at exit
  532. ctx->TransformCollection = NULL;
  533. return TRUE;
  534. }
  535. // Factory callback is required
  536. if (Plugin->factories.xform == NULL) return FALSE;
  537. fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
  538. if (fl == NULL) return FALSE;
  539. // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case
  540. if (Plugin->base.ExpectedVersion < 2080) {
  541. fl->OldXform = TRUE;
  542. }
  543. else
  544. fl->OldXform = FALSE;
  545. // Copy the parameters
  546. fl->Factory = Plugin->factories.xform;
  547. // Keep linked list
  548. fl ->Next = ctx->TransformCollection;
  549. ctx->TransformCollection = fl;
  550. // All is ok
  551. return TRUE;
  552. }
  553. void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
  554. {
  555. _cmsAssert(CMMcargo != NULL);
  556. CMMcargo ->UserData = ptr;
  557. CMMcargo ->FreeUserData = FreePrivateDataFn;
  558. }
  559. // returns the pointer defined by the plug-in to store private data
  560. void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
  561. {
  562. _cmsAssert(CMMcargo != NULL);
  563. return CMMcargo ->UserData;
  564. }
  565. // returns the current formatters
  566. void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
  567. {
  568. _cmsAssert(CMMcargo != NULL);
  569. if (FromInput) *FromInput = CMMcargo ->FromInput;
  570. if (ToOutput) *ToOutput = CMMcargo ->ToOutput;
  571. }
  572. void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
  573. {
  574. _cmsAssert(CMMcargo != NULL);
  575. if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
  576. if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat;
  577. }
  578. // returns original flags
  579. cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo)
  580. {
  581. _cmsAssert(CMMcargo != NULL);
  582. return CMMcargo->dwOriginalFlags;
  583. }
  584. // Returns the worker callback for parallelization plug-ins
  585. _cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo)
  586. {
  587. _cmsAssert(CMMcargo != NULL);
  588. return CMMcargo->Worker;
  589. }
  590. // This field holds maximum number of workers or -1 to auto
  591. cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo)
  592. {
  593. _cmsAssert(CMMcargo != NULL);
  594. return CMMcargo->MaxWorkers;
  595. }
  596. // This field is actually unused and reserved
  597. cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo)
  598. {
  599. _cmsAssert(CMMcargo != NULL);
  600. return CMMcargo->WorkerFlags;
  601. }
  602. // In the case there is a parallelization plug-in, let it to do its job
  603. static
  604. void ParalellizeIfSuitable(_cmsTRANSFORM* p)
  605. {
  606. _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
  607. _cmsAssert(p != NULL);
  608. if (ctx != NULL && ctx->SchedulerFn != NULL) {
  609. p->Worker = p->xform;
  610. p->xform = ctx->SchedulerFn;
  611. p->MaxWorkers = ctx->MaxWorkers;
  612. p->WorkerFlags = ctx->WorkerFlags;
  613. }
  614. }
  615. /**
  616. * An empty unroll to avoid a check with NULL on cmsDoTransform()
  617. */
  618. static
  619. cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info,
  620. CMSREGISTER cmsUInt16Number wIn[],
  621. CMSREGISTER cmsUInt8Number* accum,
  622. CMSREGISTER cmsUInt32Number Stride)
  623. {
  624. return accum;
  625. cmsUNUSED_PARAMETER(info);
  626. cmsUNUSED_PARAMETER(wIn);
  627. cmsUNUSED_PARAMETER(Stride);
  628. }
  629. static
  630. cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info,
  631. CMSREGISTER cmsUInt16Number wOut[],
  632. CMSREGISTER cmsUInt8Number* output,
  633. CMSREGISTER cmsUInt32Number Stride)
  634. {
  635. return output;
  636. cmsUNUSED_PARAMETER(info);
  637. cmsUNUSED_PARAMETER(wOut);
  638. cmsUNUSED_PARAMETER(Stride);
  639. }
  640. // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
  641. // for separated transforms. If this is the case,
  642. static
  643. _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
  644. cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
  645. {
  646. _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
  647. _cmsTransformCollection* Plugin;
  648. // Allocate needed memory
  649. _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
  650. if (!p) {
  651. cmsPipelineFree(lut);
  652. return NULL;
  653. }
  654. // Store the proposed pipeline
  655. p->Lut = lut;
  656. // Let's see if any plug-in want to do the transform by itself
  657. if (p->Lut != NULL) {
  658. if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE))
  659. {
  660. for (Plugin = ctx->TransformCollection;
  661. Plugin != NULL;
  662. Plugin = Plugin->Next) {
  663. if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
  664. // Last plugin in the declaration order takes control. We just keep
  665. // the original parameters as a logging.
  666. // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default
  667. // an optimized transform is not reusable. The plug-in can, however, change
  668. // the flags and make it suitable.
  669. p->ContextID = ContextID;
  670. p->InputFormat = *InputFormat;
  671. p->OutputFormat = *OutputFormat;
  672. p->dwOriginalFlags = *dwFlags;
  673. // Fill the formatters just in case the optimized routine is interested.
  674. // No error is thrown if the formatter doesn't exist. It is up to the optimization
  675. // factory to decide what to do in those cases.
  676. p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
  677. p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
  678. p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
  679. p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
  680. // Save the day? (Ignore the warning)
  681. if (Plugin->OldXform) {
  682. p->OldXform = (_cmsTransformFn)(void*)p->xform;
  683. p->xform = _cmsTransform2toTransformAdaptor;
  684. }
  685. ParalellizeIfSuitable(p);
  686. return p;
  687. }
  688. }
  689. }
  690. // Not suitable for the transform plug-in, let's check the pipeline plug-in
  691. _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
  692. }
  693. // Check whatever this is a true floating point transform
  694. if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) {
  695. // Get formatter function always return a valid union, but the contents of this union may be NULL.
  696. p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
  697. p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
  698. *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
  699. if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
  700. cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
  701. cmsDeleteTransform(p);
  702. return NULL;
  703. }
  704. if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
  705. p ->xform = NullFloatXFORM;
  706. }
  707. else {
  708. // Float transforms don't use cache, always are non-NULL
  709. p ->xform = FloatXFORM;
  710. }
  711. }
  712. else {
  713. // Formats are intended to be changed before use
  714. if (*InputFormat == 0 && *OutputFormat == 0) {
  715. p->FromInput = UnrollNothing;
  716. p->ToOutput = PackNothing;
  717. *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
  718. }
  719. else {
  720. cmsUInt32Number BytesPerPixelInput;
  721. p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
  722. p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
  723. if (p ->FromInput == NULL || p ->ToOutput == NULL) {
  724. cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
  725. cmsDeleteTransform(p);
  726. return NULL;
  727. }
  728. BytesPerPixelInput = T_BYTES(*InputFormat);
  729. if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
  730. *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
  731. }
  732. if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
  733. p ->xform = NullXFORM;
  734. }
  735. else {
  736. if (*dwFlags & cmsFLAGS_NOCACHE) {
  737. if (*dwFlags & cmsFLAGS_GAMUTCHECK)
  738. p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cache
  739. else
  740. p ->xform = PrecalculatedXFORM; // No cache, no gamut check
  741. }
  742. else {
  743. if (*dwFlags & cmsFLAGS_GAMUTCHECK)
  744. p ->xform = CachedXFORMGamutCheck; // Gamut check, cache
  745. else
  746. p ->xform = CachedXFORM; // No gamut check, cache
  747. }
  748. }
  749. }
  750. /**
  751. * Check consistency for alpha channel copy
  752. */
  753. if (*dwFlags & cmsFLAGS_COPY_ALPHA)
  754. {
  755. if (T_EXTRA(*InputFormat) != T_EXTRA(*OutputFormat))
  756. {
  757. cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Mismatched alpha channels");
  758. cmsDeleteTransform(p);
  759. return NULL;
  760. }
  761. }
  762. p ->InputFormat = *InputFormat;
  763. p ->OutputFormat = *OutputFormat;
  764. p ->dwOriginalFlags = *dwFlags;
  765. p ->ContextID = ContextID;
  766. p ->UserData = NULL;
  767. ParalellizeIfSuitable(p);
  768. return p;
  769. }
  770. static
  771. cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
  772. {
  773. cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
  774. cmsColorSpaceSignature PostColorSpace;
  775. cmsUInt32Number i;
  776. if (nProfiles == 0) return FALSE;
  777. if (hProfiles[0] == NULL) return FALSE;
  778. *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
  779. for (i=0; i < nProfiles; i++) {
  780. cmsProfileClassSignature cls;
  781. cmsHPROFILE hProfile = hProfiles[i];
  782. int lIsInput = (PostColorSpace != cmsSigXYZData) &&
  783. (PostColorSpace != cmsSigLabData);
  784. if (hProfile == NULL) return FALSE;
  785. cls = cmsGetDeviceClass(hProfile);
  786. if (cls == cmsSigNamedColorClass) {
  787. ColorSpaceIn = cmsSig1colorData;
  788. ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
  789. }
  790. else
  791. if (lIsInput || (cls == cmsSigLinkClass)) {
  792. ColorSpaceIn = cmsGetColorSpace(hProfile);
  793. ColorSpaceOut = cmsGetPCS(hProfile);
  794. }
  795. else
  796. {
  797. ColorSpaceIn = cmsGetPCS(hProfile);
  798. ColorSpaceOut = cmsGetColorSpace(hProfile);
  799. }
  800. if (i==0)
  801. *Input = ColorSpaceIn;
  802. PostColorSpace = ColorSpaceOut;
  803. }
  804. *Output = PostColorSpace;
  805. return TRUE;
  806. }
  807. // Check colorspace
  808. static
  809. cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
  810. {
  811. int Space1 = (int) T_COLORSPACE(dwFormat);
  812. int Space2 = _cmsLCMScolorSpace(Check);
  813. if (Space1 == PT_ANY) return TRUE;
  814. if (Space1 == Space2) return TRUE;
  815. if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
  816. if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE;
  817. return FALSE;
  818. }
  819. // ----------------------------------------------------------------------------------------------------------------
  820. // Jun-21-2000: Some profiles (those that comes with W2K) comes
  821. // with the media white (media black?) x 100. Add a sanity check
  822. static
  823. void NormalizeXYZ(cmsCIEXYZ* Dest)
  824. {
  825. while (Dest -> X > 2. &&
  826. Dest -> Y > 2. &&
  827. Dest -> Z > 2.) {
  828. Dest -> X /= 10.;
  829. Dest -> Y /= 10.;
  830. Dest -> Z /= 10.;
  831. }
  832. }
  833. static
  834. void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
  835. {
  836. if (src == NULL) {
  837. wtPt ->X = cmsD50X;
  838. wtPt ->Y = cmsD50Y;
  839. wtPt ->Z = cmsD50Z;
  840. }
  841. else {
  842. wtPt ->X = src->X;
  843. wtPt ->Y = src->Y;
  844. wtPt ->Z = src->Z;
  845. NormalizeXYZ(wtPt);
  846. }
  847. }
  848. // New to lcms 2.0 -- have all parameters available.
  849. cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
  850. cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
  851. cmsBool BPC[],
  852. cmsUInt32Number Intents[],
  853. cmsFloat64Number AdaptationStates[],
  854. cmsHPROFILE hGamutProfile,
  855. cmsUInt32Number nGamutPCSposition,
  856. cmsUInt32Number InputFormat,
  857. cmsUInt32Number OutputFormat,
  858. cmsUInt32Number dwFlags)
  859. {
  860. _cmsTRANSFORM* xform;
  861. cmsColorSpaceSignature EntryColorSpace;
  862. cmsColorSpaceSignature ExitColorSpace;
  863. cmsPipeline* Lut;
  864. cmsUInt32Number LastIntent = Intents[nProfiles-1];
  865. // If it is a fake transform
  866. if (dwFlags & cmsFLAGS_NULLTRANSFORM)
  867. {
  868. return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
  869. }
  870. // If gamut check is requested, make sure we have a gamut profile
  871. if (dwFlags & cmsFLAGS_GAMUTCHECK) {
  872. if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
  873. }
  874. // On floating point transforms, inhibit cache
  875. if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
  876. dwFlags |= cmsFLAGS_NOCACHE;
  877. // Mark entry/exit spaces
  878. if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
  879. cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
  880. return NULL;
  881. }
  882. // Check if proper colorspaces
  883. if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
  884. cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
  885. return NULL;
  886. }
  887. if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
  888. cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
  889. return NULL;
  890. }
  891. // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
  892. if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
  893. {
  894. cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
  895. if (gamma > 0 && gamma < 1.6)
  896. dwFlags |= cmsFLAGS_NOOPTIMIZE;
  897. }
  898. // Create a pipeline with all transformations
  899. Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
  900. if (Lut == NULL) {
  901. cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
  902. return NULL;
  903. }
  904. // Check channel count
  905. if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
  906. (cmsChannelsOfColorSpace(ExitColorSpace) != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
  907. cmsPipelineFree(Lut);
  908. cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
  909. return NULL;
  910. }
  911. // All seems ok
  912. xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
  913. if (xform == NULL) {
  914. return NULL;
  915. }
  916. // Keep values
  917. xform ->EntryColorSpace = EntryColorSpace;
  918. xform ->ExitColorSpace = ExitColorSpace;
  919. xform ->RenderingIntent = Intents[nProfiles-1];
  920. // Take white points
  921. SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
  922. SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
  923. // Create a gamut check LUT if requested
  924. if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
  925. xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
  926. BPC, Intents,
  927. AdaptationStates,
  928. nGamutPCSposition,
  929. hGamutProfile);
  930. // Try to read input and output colorant table
  931. if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
  932. // Input table can only come in this way.
  933. xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
  934. }
  935. // Output is a little bit more complex.
  936. if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
  937. // This tag may exist only on devicelink profiles.
  938. if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
  939. // It may be NULL if error
  940. xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
  941. }
  942. } else {
  943. if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
  944. xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
  945. }
  946. }
  947. // Store the sequence of profiles
  948. if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
  949. xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
  950. }
  951. else
  952. xform ->Sequence = NULL;
  953. // If this is a cached transform, init first value, which is zero (16 bits only)
  954. if (!(dwFlags & cmsFLAGS_NOCACHE)) {
  955. memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
  956. if (xform ->GamutCheck != NULL) {
  957. TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
  958. }
  959. else {
  960. xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
  961. }
  962. }
  963. return (cmsHTRANSFORM) xform;
  964. }
  965. // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
  966. cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
  967. cmsHPROFILE hProfiles[],
  968. cmsUInt32Number nProfiles,
  969. cmsUInt32Number InputFormat,
  970. cmsUInt32Number OutputFormat,
  971. cmsUInt32Number Intent,
  972. cmsUInt32Number dwFlags)
  973. {
  974. cmsUInt32Number i;
  975. cmsBool BPC[256];
  976. cmsUInt32Number Intents[256];
  977. cmsFloat64Number AdaptationStates[256];
  978. if (nProfiles <= 0 || nProfiles > 255) {
  979. cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
  980. return NULL;
  981. }
  982. for (i=0; i < nProfiles; i++) {
  983. BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
  984. Intents[i] = Intent;
  985. AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
  986. }
  987. return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
  988. }
  989. cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
  990. cmsUInt32Number nProfiles,
  991. cmsUInt32Number InputFormat,
  992. cmsUInt32Number OutputFormat,
  993. cmsUInt32Number Intent,
  994. cmsUInt32Number dwFlags)
  995. {
  996. if (nProfiles <= 0 || nProfiles > 255) {
  997. cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
  998. return NULL;
  999. }
  1000. return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
  1001. hProfiles,
  1002. nProfiles,
  1003. InputFormat,
  1004. OutputFormat,
  1005. Intent,
  1006. dwFlags);
  1007. }
  1008. cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
  1009. cmsHPROFILE Input,
  1010. cmsUInt32Number InputFormat,
  1011. cmsHPROFILE Output,
  1012. cmsUInt32Number OutputFormat,
  1013. cmsUInt32Number Intent,
  1014. cmsUInt32Number dwFlags)
  1015. {
  1016. cmsHPROFILE hArray[2];
  1017. hArray[0] = Input;
  1018. hArray[1] = Output;
  1019. return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
  1020. }
  1021. CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
  1022. cmsUInt32Number InputFormat,
  1023. cmsHPROFILE Output,
  1024. cmsUInt32Number OutputFormat,
  1025. cmsUInt32Number Intent,
  1026. cmsUInt32Number dwFlags)
  1027. {
  1028. return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
  1029. }
  1030. cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
  1031. cmsHPROFILE InputProfile,
  1032. cmsUInt32Number InputFormat,
  1033. cmsHPROFILE OutputProfile,
  1034. cmsUInt32Number OutputFormat,
  1035. cmsHPROFILE ProofingProfile,
  1036. cmsUInt32Number nIntent,
  1037. cmsUInt32Number ProofingIntent,
  1038. cmsUInt32Number dwFlags)
  1039. {
  1040. cmsHPROFILE hArray[4];
  1041. cmsUInt32Number Intents[4];
  1042. cmsBool BPC[4];
  1043. cmsFloat64Number Adaptation[4];
  1044. cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
  1045. hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile;
  1046. Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent;
  1047. BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0;
  1048. Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
  1049. if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
  1050. return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
  1051. return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
  1052. ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
  1053. }
  1054. cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
  1055. cmsUInt32Number InputFormat,
  1056. cmsHPROFILE OutputProfile,
  1057. cmsUInt32Number OutputFormat,
  1058. cmsHPROFILE ProofingProfile,
  1059. cmsUInt32Number nIntent,
  1060. cmsUInt32Number ProofingIntent,
  1061. cmsUInt32Number dwFlags)
  1062. {
  1063. return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
  1064. InputProfile,
  1065. InputFormat,
  1066. OutputProfile,
  1067. OutputFormat,
  1068. ProofingProfile,
  1069. nIntent,
  1070. ProofingIntent,
  1071. dwFlags);
  1072. }
  1073. // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
  1074. cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
  1075. {
  1076. _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
  1077. if (xform == NULL) return NULL;
  1078. return xform -> ContextID;
  1079. }
  1080. // Grab the input/output formats
  1081. cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
  1082. {
  1083. _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
  1084. if (xform == NULL) return 0;
  1085. return xform->InputFormat;
  1086. }
  1087. cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
  1088. {
  1089. _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
  1090. if (xform == NULL) return 0;
  1091. return xform->OutputFormat;
  1092. }
  1093. // For backwards compatibility
  1094. cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
  1095. cmsUInt32Number InputFormat,
  1096. cmsUInt32Number OutputFormat)
  1097. {
  1098. _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
  1099. cmsFormatter16 FromInput, ToOutput;
  1100. // We only can afford to change formatters if previous transform is at least 16 bits
  1101. if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
  1102. cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
  1103. return FALSE;
  1104. }
  1105. FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
  1106. ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
  1107. if (FromInput == NULL || ToOutput == NULL) {
  1108. cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
  1109. return FALSE;
  1110. }
  1111. xform ->InputFormat = InputFormat;
  1112. xform ->OutputFormat = OutputFormat;
  1113. xform ->FromInput = FromInput;
  1114. xform ->ToOutput = ToOutput;
  1115. return TRUE;
  1116. }