13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 // This file is available under and governed by the GNU General Public
26 // License version 2 only, as published by the Free Software Foundation.
27 // However, the following notice accompanied the original version of this
28 // file:
29 //
30 //---------------------------------------------------------------------------------
31 //
32 // Little Color Management System
33 // Copyright (c) 1998-2012 Marti Maria Saguer
34 //
35 // Permission is hereby granted, free of charge, to any person obtaining
36 // a copy of this software and associated documentation files (the "Software"),
37 // to deal in the Software without restriction, including without limitation
38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
39 // and/or sell copies of the Software, and to permit persons to whom the Software
40 // is furnished to do so, subject to the following conditions:
41 //
42 // The above copyright notice and this permission notice shall be included in
43 // all copies or substantial portions of the Software.
44 //
45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 //
53 //---------------------------------------------------------------------------------
140 if (cmsGetEncodedICCversion(hProfile) < 0x4000000) {
141
142 if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) {
143
144 cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag);
145
146 if (White == NULL) {
147
148 _cmsMAT3identity(Dest);
149 return TRUE;
150 }
151
152 return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ());
153 }
154 }
155
156 return TRUE;
157 }
158
159
160 // Auxiliar, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper
161 static
162 cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile)
163 {
164 cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue;
165
166 _cmsAssert(r != NULL);
167
168 PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag);
169 PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag);
170 PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag);
171
172 if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL)
173 return FALSE;
174
175 _cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X);
176 _cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y);
177 _cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z);
178
179 return TRUE;
180 }
326 }
327
328 return Lut;
329
330 Error:
331 cmsPipelineFree(Lut);
332 return NULL;
333 }
334
335
336 // Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc
337 // is adjusted here in order to create a LUT that takes care of all those details.
338 // We add intent = -1 as a way to read matrix shaper always, no matter of other LUT
339 cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
340 {
341 cmsTagTypeSignature OriginalType;
342 cmsTagSignature tag16;
343 cmsTagSignature tagFloat;
344 cmsContext ContextID = cmsGetProfileContextID(hProfile);
345
346 // On named color, take the appropiate tag
347 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
348
349 cmsPipeline* Lut;
350 cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
351
352 if (nc == NULL) return NULL;
353
354 Lut = cmsPipelineAlloc(ContextID, 0, 0);
355 if (Lut == NULL) {
356 cmsFreeNamedColorList(nc);
357 return NULL;
358 }
359
360 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) ||
361 !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) {
362 cmsPipelineFree(Lut);
363 return NULL;
364 }
365 return Lut;
366 }
367
368 // This is an attempt to reuse this funtion to retrieve the matrix-shaper as pipeline no
369 // matter other LUT are present and have precedence. Intent = -1 means just this.
370 if (Intent != -1) {
371
372 tag16 = Device2PCS16[Intent];
373 tagFloat = Device2PCSFloat[Intent];
374
375 if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
376
377 // Floating point LUT are always V4, but the encoding range is no
378 // longer 0..1.0, so we need to add an stage depending on the color space
379 return _cmsReadFloatInputTag(hProfile, tagFloat);
380 }
381
382 // Revert to perceptual if no tag is found
383 if (!cmsIsTag(hProfile, tag16)) {
384 tag16 = Device2PCS16[0];
385 }
386
387 if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
388
389 // Check profile version and LUT type. Do the necessary adjustments if needed
390
406 if (cmsGetColorSpace(hProfile) == cmsSigLabData &&
407 !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
408 goto Error;
409
410 // Add a matrix for conversion V2 to V4 Lab PCS
411 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
412 goto Error;
413
414 return Lut;
415 Error:
416 cmsPipelineFree(Lut);
417 return NULL;
418 }
419 }
420
421 // Lut was not found, try to create a matrix-shaper
422
423 // Check if this is a grayscale profile.
424 if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
425
426 // if so, build appropiate conversion tables.
427 // The tables are the PCS iluminant, scaled across GrayTRC
428 return BuildGrayInputMatrixPipeline(hProfile);
429 }
430
431 // Not gray, create a normal matrix-shaper
432 return BuildRGBInputMatrixShaper(hProfile);
433 }
434
435 // ---------------------------------------------------------------------------------------------------------------
436
437 // Gray output pipeline.
438 // XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be
439 // given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve.
440 // The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well.
441
442 static
443 cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile)
444 {
445 cmsToneCurve *GrayTRC, *RevGrayTRC;
446 cmsPipeline* Lut;
561
562 CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR;
563 _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params);
564 }
565 }
566 }
567
568
569 // Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
570 static
571 cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
572 {
573 cmsContext ContextID = cmsGetProfileContextID(hProfile);
574 cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
575 cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
576 cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile);
577
578 if (Lut == NULL) return NULL;
579
580 // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding,
581 // and since the formatter has already accomodated to 0..1.0, we should undo this change
582 if ( PCS == cmsSigLabData)
583 {
584 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
585 goto Error;
586 }
587 else
588 if (PCS == cmsSigXYZData)
589 {
590 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
591 goto Error;
592 }
593
594 // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline
595 if ( dataSpace == cmsSigLabData)
596 {
597 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
598 goto Error;
599 }
600 else if (dataSpace == cmsSigXYZData)
601 {
602 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
603 goto Error;
604 }
605
606 return Lut;
607
608 Error:
609 cmsPipelineFree(Lut);
610 return NULL;
611 }
612
613 // Create an output MPE LUT from agiven profile. Version mismatches are handled here
614 cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
615 {
616 cmsTagTypeSignature OriginalType;
617 cmsTagSignature tag16;
618 cmsTagSignature tagFloat;
619 cmsContext ContextID = cmsGetProfileContextID(hProfile);
620
621
622 if (Intent != -1) {
623
624 tag16 = PCS2Device16[Intent];
625 tagFloat = PCS2DeviceFloat[Intent];
626
627 if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
628
629 // Floating point LUT are always V4
630 return _cmsReadFloatOutputTag(hProfile, tagFloat);
631 }
632
633 // Revert to perceptual if no tag is found
634 if (!cmsIsTag(hProfile, tag16)) {
635 tag16 = PCS2Device16[0];
636 }
637
638 if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
639
640 // Check profile version and LUT type. Do the necessary adjustments if needed
641
642 // First read the tag
663 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
664 goto Error;
665
666 // If the output is Lab, add also a conversion at the end
667 if (cmsGetColorSpace(hProfile) == cmsSigLabData)
668 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
669 goto Error;
670
671 return Lut;
672 Error:
673 cmsPipelineFree(Lut);
674 return NULL;
675 }
676 }
677
678 // Lut not found, try to create a matrix-shaper
679
680 // Check if this is a grayscale profile.
681 if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
682
683 // if so, build appropiate conversion tables.
684 // The tables are the PCS iluminant, scaled across GrayTRC
685 return BuildGrayOutputPipeline(hProfile);
686 }
687
688 // Not gray, create a normal matrix-shaper, which only operates in XYZ space
689 return BuildRGBOutputMatrixShaper(hProfile);
690 }
691
692 // ---------------------------------------------------------------------------------------------------------------
693
694 // Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded
695 static
696 cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
697 {
698 cmsContext ContextID = cmsGetProfileContextID(hProfile);
699 cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
700 cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
701 cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
702
703 if (Lut == NULL) return NULL;
721 }
722 else
723 if (PCS == cmsSigXYZData)
724 {
725 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
726 goto Error;
727 }
728
729 return Lut;
730 Error:
731 cmsPipelineFree(Lut);
732 return NULL;
733 }
734
735 // This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
736 // tag name here may default to AToB0
737 cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
738 {
739 cmsPipeline* Lut;
740 cmsTagTypeSignature OriginalType;
741 cmsTagSignature tag16 = Device2PCS16[Intent];
742 cmsTagSignature tagFloat = Device2PCSFloat[Intent];
743 cmsContext ContextID = cmsGetProfileContextID(hProfile);
744
745
746 // On named color, take the appropiate tag
747 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
748
749 cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
750
751 if (nc == NULL) return NULL;
752
753 Lut = cmsPipelineAlloc(ContextID, 0, 0);
754 if (Lut == NULL)
755 goto Error;
756
757 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE)))
758 goto Error;
759
760 if (cmsGetColorSpace(hProfile) == cmsSigLabData)
761 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
762 goto Error;
763
764 return Lut;
765 Error:
766 cmsPipelineFree(Lut);
767 cmsFreeNamedColorList(nc);
768 return NULL;
769 }
770
771 if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
772
773 // Floating point LUT are always V
774 return _cmsReadFloatDevicelinkTag(hProfile, tagFloat);
775 }
776
777 tagFloat = Device2PCSFloat[0];
778 if (cmsIsTag(hProfile, tagFloat)) {
779
780 return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
781 }
782
783 if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
784
785 tag16 = Device2PCS16[0];
786 if (!cmsIsTag(hProfile, tag16)) return NULL;
787 }
788
789 // Check profile version and LUT type. Do the necessary adjustments if needed
790
791 // Read the tag
792 Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16);
793 if (Lut == NULL) return NULL;
794
795 // The profile owns the Lut, so we need to copy it
796 Lut = cmsPipelineDup(Lut);
797 if (Lut == NULL) return NULL;
798
799 // Now it is time for a controversial stuff. I found that for 3D LUTS using
800 // Lab used as indexer space, trilinear interpolation should be used
801 if (cmsGetPCS(hProfile) == cmsSigLabData)
802 ChangeInterpolationToTrilinear(Lut);
803
804 // After reading it, we have info about the original type
805 OriginalType = _cmsGetTagTrueType(hProfile, tag16);
806
807 // We need to adjust data for Lab16 on output
808 if (OriginalType != cmsSigLut16Type) return Lut;
809
810 // Here it is possible to get Lab on both sides
811
812 if (cmsGetColorSpace(hProfile) == cmsSigLabData) {
813 if(!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
814 goto Error2;
815 }
816
817 if (cmsGetPCS(hProfile) == cmsSigLabData) {
818 if(!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
819 goto Error2;
820 }
821
822 return Lut;
823
824 Error2:
825 cmsPipelineFree(Lut);
826 return NULL;
827 }
828
829 // ---------------------------------------------------------------------------------------------------------------
830
831 // Returns TRUE if the profile is implemented as matrix-shaper
832 cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile)
833 {
834 switch (cmsGetColorSpace(hProfile)) {
835
836 case cmsSigGrayData:
837
838 return cmsIsTag(hProfile, cmsSigGrayTRCTag);
933 NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description);
934 }
935 }
936 return NewSeq;
937 }
938
939 // Dump the contents of profile sequence in both tags (if v4 available)
940 cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq)
941 {
942 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE;
943
944 if (cmsGetEncodedICCversion(hProfile) >= 0x4000000) {
945
946 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE;
947 }
948
949 return TRUE;
950 }
951
952
953 // Auxiliar, read and duplicate a MLU if found.
954 static
955 cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig)
956 {
957 cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig);
958 if (mlu == NULL) return NULL;
959
960 return cmsMLUdup(mlu);
961 }
962
963 // Create a sequence description out of an array of profiles
964 cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[])
965 {
966 cmsUInt32Number i;
967 cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles);
968
969 if (seq == NULL) return NULL;
970
971 for (i=0; i < nProfiles; i++) {
972
973 cmsPSEQDESC* ps = &seq ->seq[i];
|
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 // This file is available under and governed by the GNU General Public
26 // License version 2 only, as published by the Free Software Foundation.
27 // However, the following notice accompanied the original version of this
28 // file:
29 //
30 //---------------------------------------------------------------------------------
31 //
32 // Little Color Management System
33 // Copyright (c) 1998-2016 Marti Maria Saguer
34 //
35 // Permission is hereby granted, free of charge, to any person obtaining
36 // a copy of this software and associated documentation files (the "Software"),
37 // to deal in the Software without restriction, including without limitation
38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
39 // and/or sell copies of the Software, and to permit persons to whom the Software
40 // is furnished to do so, subject to the following conditions:
41 //
42 // The above copyright notice and this permission notice shall be included in
43 // all copies or substantial portions of the Software.
44 //
45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
46 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
47 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
48 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
49 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
50 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
51 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 //
53 //---------------------------------------------------------------------------------
140 if (cmsGetEncodedICCversion(hProfile) < 0x4000000) {
141
142 if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) {
143
144 cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag);
145
146 if (White == NULL) {
147
148 _cmsMAT3identity(Dest);
149 return TRUE;
150 }
151
152 return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ());
153 }
154 }
155
156 return TRUE;
157 }
158
159
160 // Auxiliary, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper
161 static
162 cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile)
163 {
164 cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue;
165
166 _cmsAssert(r != NULL);
167
168 PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag);
169 PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag);
170 PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag);
171
172 if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL)
173 return FALSE;
174
175 _cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X);
176 _cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y);
177 _cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z);
178
179 return TRUE;
180 }
326 }
327
328 return Lut;
329
330 Error:
331 cmsPipelineFree(Lut);
332 return NULL;
333 }
334
335
336 // Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc
337 // is adjusted here in order to create a LUT that takes care of all those details.
338 // We add intent = -1 as a way to read matrix shaper always, no matter of other LUT
339 cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent)
340 {
341 cmsTagTypeSignature OriginalType;
342 cmsTagSignature tag16;
343 cmsTagSignature tagFloat;
344 cmsContext ContextID = cmsGetProfileContextID(hProfile);
345
346 // On named color, take the appropriate tag
347 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
348
349 cmsPipeline* Lut;
350 cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag);
351
352 if (nc == NULL) return NULL;
353
354 Lut = cmsPipelineAlloc(ContextID, 0, 0);
355 if (Lut == NULL) {
356 cmsFreeNamedColorList(nc);
357 return NULL;
358 }
359
360 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) ||
361 !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) {
362 cmsPipelineFree(Lut);
363 return NULL;
364 }
365 return Lut;
366 }
367
368 // This is an attempt to reuse this function to retrieve the matrix-shaper as pipeline no
369 // matter other LUT are present and have precedence. Intent = -1 means just this.
370 if (Intent >= INTENT_PERCEPTUAL && Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {
371
372 tag16 = Device2PCS16[Intent];
373 tagFloat = Device2PCSFloat[Intent];
374
375 if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
376
377 // Floating point LUT are always V4, but the encoding range is no
378 // longer 0..1.0, so we need to add an stage depending on the color space
379 return _cmsReadFloatInputTag(hProfile, tagFloat);
380 }
381
382 // Revert to perceptual if no tag is found
383 if (!cmsIsTag(hProfile, tag16)) {
384 tag16 = Device2PCS16[0];
385 }
386
387 if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
388
389 // Check profile version and LUT type. Do the necessary adjustments if needed
390
406 if (cmsGetColorSpace(hProfile) == cmsSigLabData &&
407 !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
408 goto Error;
409
410 // Add a matrix for conversion V2 to V4 Lab PCS
411 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
412 goto Error;
413
414 return Lut;
415 Error:
416 cmsPipelineFree(Lut);
417 return NULL;
418 }
419 }
420
421 // Lut was not found, try to create a matrix-shaper
422
423 // Check if this is a grayscale profile.
424 if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
425
426 // if so, build appropriate conversion tables.
427 // The tables are the PCS iluminant, scaled across GrayTRC
428 return BuildGrayInputMatrixPipeline(hProfile);
429 }
430
431 // Not gray, create a normal matrix-shaper
432 return BuildRGBInputMatrixShaper(hProfile);
433 }
434
435 // ---------------------------------------------------------------------------------------------------------------
436
437 // Gray output pipeline.
438 // XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be
439 // given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve.
440 // The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well.
441
442 static
443 cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile)
444 {
445 cmsToneCurve *GrayTRC, *RevGrayTRC;
446 cmsPipeline* Lut;
561
562 CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR;
563 _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params);
564 }
565 }
566 }
567
568
569 // Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
570 static
571 cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
572 {
573 cmsContext ContextID = cmsGetProfileContextID(hProfile);
574 cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
575 cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
576 cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile);
577
578 if (Lut == NULL) return NULL;
579
580 // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding,
581 // and since the formatter has already accommodated to 0..1.0, we should undo this change
582 if ( PCS == cmsSigLabData)
583 {
584 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID)))
585 goto Error;
586 }
587 else
588 if (PCS == cmsSigXYZData)
589 {
590 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID)))
591 goto Error;
592 }
593
594 // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline
595 if ( dataSpace == cmsSigLabData)
596 {
597 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID)))
598 goto Error;
599 }
600 else if (dataSpace == cmsSigXYZData)
601 {
602 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
603 goto Error;
604 }
605
606 return Lut;
607
608 Error:
609 cmsPipelineFree(Lut);
610 return NULL;
611 }
612
613 // Create an output MPE LUT from agiven profile. Version mismatches are handled here
614 cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent)
615 {
616 cmsTagTypeSignature OriginalType;
617 cmsTagSignature tag16;
618 cmsTagSignature tagFloat;
619 cmsContext ContextID = cmsGetProfileContextID(hProfile);
620
621
622 if (Intent >= INTENT_PERCEPTUAL && Intent <= INTENT_ABSOLUTE_COLORIMETRIC) {
623
624 tag16 = PCS2Device16[Intent];
625 tagFloat = PCS2DeviceFloat[Intent];
626
627 if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
628
629 // Floating point LUT are always V4
630 return _cmsReadFloatOutputTag(hProfile, tagFloat);
631 }
632
633 // Revert to perceptual if no tag is found
634 if (!cmsIsTag(hProfile, tag16)) {
635 tag16 = PCS2Device16[0];
636 }
637
638 if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
639
640 // Check profile version and LUT type. Do the necessary adjustments if needed
641
642 // First read the tag
663 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
664 goto Error;
665
666 // If the output is Lab, add also a conversion at the end
667 if (cmsGetColorSpace(hProfile) == cmsSigLabData)
668 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
669 goto Error;
670
671 return Lut;
672 Error:
673 cmsPipelineFree(Lut);
674 return NULL;
675 }
676 }
677
678 // Lut not found, try to create a matrix-shaper
679
680 // Check if this is a grayscale profile.
681 if (cmsGetColorSpace(hProfile) == cmsSigGrayData) {
682
683 // if so, build appropriate conversion tables.
684 // The tables are the PCS iluminant, scaled across GrayTRC
685 return BuildGrayOutputPipeline(hProfile);
686 }
687
688 // Not gray, create a normal matrix-shaper, which only operates in XYZ space
689 return BuildRGBOutputMatrixShaper(hProfile);
690 }
691
692 // ---------------------------------------------------------------------------------------------------------------
693
694 // Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded
695 static
696 cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
697 {
698 cmsContext ContextID = cmsGetProfileContextID(hProfile);
699 cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat));
700 cmsColorSpaceSignature PCS = cmsGetPCS(hProfile);
701 cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile);
702
703 if (Lut == NULL) return NULL;
721 }
722 else
723 if (PCS == cmsSigXYZData)
724 {
725 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID)))
726 goto Error;
727 }
728
729 return Lut;
730 Error:
731 cmsPipelineFree(Lut);
732 return NULL;
733 }
734
735 // This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The
736 // tag name here may default to AToB0
737 cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent)
738 {
739 cmsPipeline* Lut;
740 cmsTagTypeSignature OriginalType;
741 cmsTagSignature tag16;
742 cmsTagSignature tagFloat;
743 cmsContext ContextID = cmsGetProfileContextID(hProfile);
744
745
746 if (Intent < INTENT_PERCEPTUAL || Intent > INTENT_ABSOLUTE_COLORIMETRIC)
747 return NULL;
748
749 tag16 = Device2PCS16[Intent];
750 tagFloat = Device2PCSFloat[Intent];
751
752 // On named color, take the appropriate tag
753 if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
754
755 cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*)cmsReadTag(hProfile, cmsSigNamedColor2Tag);
756
757 if (nc == NULL) return NULL;
758
759 Lut = cmsPipelineAlloc(ContextID, 0, 0);
760 if (Lut == NULL)
761 goto Error;
762
763 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE)))
764 goto Error;
765
766 if (cmsGetColorSpace(hProfile) == cmsSigLabData)
767 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
768 goto Error;
769
770 return Lut;
771 Error:
772 cmsPipelineFree(Lut);
773 cmsFreeNamedColorList(nc);
774 return NULL;
775 }
776
777
778 if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence
779
780 // Floating point LUT are always V
781 return _cmsReadFloatDevicelinkTag(hProfile, tagFloat);
782 }
783
784 tagFloat = Device2PCSFloat[0];
785 if (cmsIsTag(hProfile, tagFloat)) {
786
787 return cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat));
788 }
789
790 if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table?
791
792 tag16 = Device2PCS16[0];
793 if (!cmsIsTag(hProfile, tag16)) return NULL;
794 }
795
796 // Check profile version and LUT type. Do the necessary adjustments if needed
797
798 // Read the tag
799 Lut = (cmsPipeline*)cmsReadTag(hProfile, tag16);
800 if (Lut == NULL) return NULL;
801
802 // The profile owns the Lut, so we need to copy it
803 Lut = cmsPipelineDup(Lut);
804 if (Lut == NULL) return NULL;
805
806 // Now it is time for a controversial stuff. I found that for 3D LUTS using
807 // Lab used as indexer space, trilinear interpolation should be used
808 if (cmsGetPCS(hProfile) == cmsSigLabData)
809 ChangeInterpolationToTrilinear(Lut);
810
811 // After reading it, we have info about the original type
812 OriginalType = _cmsGetTagTrueType(hProfile, tag16);
813
814 // We need to adjust data for Lab16 on output
815 if (OriginalType != cmsSigLut16Type) return Lut;
816
817 // Here it is possible to get Lab on both sides
818
819 if (cmsGetColorSpace(hProfile) == cmsSigLabData) {
820 if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID)))
821 goto Error2;
822 }
823
824 if (cmsGetPCS(hProfile) == cmsSigLabData) {
825 if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID)))
826 goto Error2;
827 }
828
829 return Lut;
830
831 Error2:
832 cmsPipelineFree(Lut);
833 return NULL;
834 }
835
836 // ---------------------------------------------------------------------------------------------------------------
837
838 // Returns TRUE if the profile is implemented as matrix-shaper
839 cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile)
840 {
841 switch (cmsGetColorSpace(hProfile)) {
842
843 case cmsSigGrayData:
844
845 return cmsIsTag(hProfile, cmsSigGrayTRCTag);
940 NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description);
941 }
942 }
943 return NewSeq;
944 }
945
946 // Dump the contents of profile sequence in both tags (if v4 available)
947 cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq)
948 {
949 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE;
950
951 if (cmsGetEncodedICCversion(hProfile) >= 0x4000000) {
952
953 if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE;
954 }
955
956 return TRUE;
957 }
958
959
960 // Auxiliary, read and duplicate a MLU if found.
961 static
962 cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig)
963 {
964 cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig);
965 if (mlu == NULL) return NULL;
966
967 return cmsMLUdup(mlu);
968 }
969
970 // Create a sequence description out of an array of profiles
971 cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[])
972 {
973 cmsUInt32Number i;
974 cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles);
975
976 if (seq == NULL) return NULL;
977
978 for (i=0; i < nProfiles; i++) {
979
980 cmsPSEQDESC* ps = &seq ->seq[i];
|