OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkColorSpace.h" | 8 #include "SkColorSpace.h" |
| 9 #include "SkColorSpace_A2B.h" |
9 #include "SkColorSpace_Base.h" | 10 #include "SkColorSpace_Base.h" |
| 11 #include "SkColorSpace_XYZ.h" |
10 #include "SkColorSpacePriv.h" | 12 #include "SkColorSpacePriv.h" |
11 #include "SkEndian.h" | 13 #include "SkEndian.h" |
12 #include "SkFixed.h" | 14 #include "SkFixed.h" |
13 #include "SkTemplates.h" | 15 #include "SkTemplates.h" |
14 | 16 |
15 #define return_if_false(pred, msg) \ | 17 #define return_if_false(pred, msg) \ |
16 do { \ | 18 do { \ |
17 if (!(pred)) { \ | 19 if (!(pred)) { \ |
18 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ | 20 SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ |
19 return false; \ | 21 return false; \ |
(...skipping 25 matching lines...) Expand all Loading... |
45 | 47 |
46 // Contains a signature (4), offset (4), and size (4). | 48 // Contains a signature (4), offset (4), and size (4). |
47 static constexpr size_t kICCTagTableEntrySize = 12; | 49 static constexpr size_t kICCTagTableEntrySize = 12; |
48 | 50 |
49 static constexpr uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B',
' '); | 51 static constexpr uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B',
' '); |
50 static constexpr uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't',
'r'); | 52 static constexpr uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't',
'r'); |
51 static constexpr uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n',
'r'); | 53 static constexpr uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n',
'r'); |
52 static constexpr uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't',
'r'); | 54 static constexpr uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't',
'r'); |
53 static constexpr uint32_t kColorSpace_Profile = SkSetFourByteTag('s', 'p', 'a',
'c'); | 55 static constexpr uint32_t kColorSpace_Profile = SkSetFourByteTag('s', 'p', 'a',
'c'); |
54 static constexpr uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z',
' '); | 56 static constexpr uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z',
' '); |
| 57 static constexpr uint32_t kLAB_PCSSpace = SkSetFourByteTag('L', 'a', 'b',
' '); |
55 static constexpr uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's',
'p'); | 58 static constexpr uint32_t kACSP_Signature = SkSetFourByteTag('a', 'c', 's',
'p'); |
56 | 59 |
57 struct ICCProfileHeader { | 60 struct ICCProfileHeader { |
58 uint32_t fSize; | 61 uint32_t fSize; |
59 | 62 |
60 // No reason to care about the preferred color management module (ex: Adobe,
Apple, etc.). | 63 // No reason to care about the preferred color management module (ex: Adobe,
Apple, etc.). |
61 // We're always going to use this one. | 64 // We're always going to use this one. |
62 uint32_t fCMMType_ignored; | 65 uint32_t fCMMType_ignored; |
63 | 66 |
64 uint32_t fVersion; | 67 uint32_t fVersion; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 fProfileClass == kOutput_Profile || | 126 fProfileClass == kOutput_Profile || |
124 fProfileClass == kColorSpace_Profile, | 127 fProfileClass == kColorSpace_Profile, |
125 "Unsupported profile"); | 128 "Unsupported profile"); |
126 | 129 |
127 // TODO (msarett): | 130 // TODO (msarett): |
128 // All the profiles we've tested so far use RGB as the input color space
. | 131 // All the profiles we've tested so far use RGB as the input color space
. |
129 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color
space"); | 132 return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color
space"); |
130 | 133 |
131 // TODO (msarett): | 134 // TODO (msarett): |
132 // All the profiles we've tested so far use XYZ as the profile connectio
n space. | 135 // All the profiles we've tested so far use XYZ as the profile connectio
n space. |
133 return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); | 136 return_if_false(fPCS == kXYZ_PCSSpace || fPCS == kLAB_PCSSpace, "Unsuppo
rted PCS space"); |
134 | 137 |
135 return_if_false(fSignature == kACSP_Signature, "Bad signature"); | 138 return_if_false(fSignature == kACSP_Signature, "Bad signature"); |
136 | 139 |
137 // TODO (msarett): | 140 // TODO (msarett): |
138 // Should we treat different rendering intents differently? | 141 // Should we treat different rendering intents differently? |
139 // Valid rendering intents include kPerceptual (0), kRelative (1), | 142 // Valid rendering intents include kPerceptual (0), kRelative (1), |
140 // kSaturation (2), and kAbsolute (3). | 143 // kSaturation (2), and kAbsolute (3). |
141 if (fRenderingIntent > 3) { | 144 if (fRenderingIntent > 3) { |
142 // Warn rather than fail here. Occasionally, we see perfectly | 145 // Warn rather than fail here. Occasionally, we see perfectly |
143 // normal profiles with wacky rendering intents. | 146 // normal profiles with wacky rendering intents. |
(...skipping 517 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
661 | 664 |
662 return true; | 665 return true; |
663 } | 666 } |
664 | 667 |
665 static bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { | 668 static bool load_matrix(SkMatrix44* toXYZ, const uint8_t* src, size_t len) { |
666 if (len < 48) { | 669 if (len < 48) { |
667 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); | 670 SkColorSpacePrintf("Matrix tag is too small (%d bytes).", len); |
668 return false; | 671 return false; |
669 } | 672 } |
670 | 673 |
671 // For this matrix to behave like our "to XYZ D50" matrices, it needs to be
scaled. | |
672 constexpr float scale = 65535.0 / 32768.0; | |
673 float array[16]; | 674 float array[16]; |
674 array[ 0] = scale * SkFixedToFloat(read_big_endian_i32(src)); | 675 array[ 0] = SkFixedToFloat(read_big_endian_i32(src)); |
675 array[ 1] = scale * SkFixedToFloat(read_big_endian_i32(src + 4)); | 676 array[ 1] = SkFixedToFloat(read_big_endian_i32(src + 4)); |
676 array[ 2] = scale * SkFixedToFloat(read_big_endian_i32(src + 8)); | 677 array[ 2] = SkFixedToFloat(read_big_endian_i32(src + 8)); |
677 array[ 3] = scale * SkFixedToFloat(read_big_endian_i32(src + 36)); // transl
ate R | 678 array[ 3] = SkFixedToFloat(read_big_endian_i32(src + 36)); // translate R |
678 array[ 4] = scale * SkFixedToFloat(read_big_endian_i32(src + 12)); | 679 array[ 4] = SkFixedToFloat(read_big_endian_i32(src + 12)); |
679 array[ 5] = scale * SkFixedToFloat(read_big_endian_i32(src + 16)); | 680 array[ 5] = SkFixedToFloat(read_big_endian_i32(src + 16)); |
680 array[ 6] = scale * SkFixedToFloat(read_big_endian_i32(src + 20)); | 681 array[ 6] = SkFixedToFloat(read_big_endian_i32(src + 20)); |
681 array[ 7] = scale * SkFixedToFloat(read_big_endian_i32(src + 40)); // transl
ate G | 682 array[ 7] = SkFixedToFloat(read_big_endian_i32(src + 40)); // translate G |
682 array[ 8] = scale * SkFixedToFloat(read_big_endian_i32(src + 24)); | 683 array[ 8] = SkFixedToFloat(read_big_endian_i32(src + 24)); |
683 array[ 9] = scale * SkFixedToFloat(read_big_endian_i32(src + 28)); | 684 array[ 9] = SkFixedToFloat(read_big_endian_i32(src + 28)); |
684 array[10] = scale * SkFixedToFloat(read_big_endian_i32(src + 32)); | 685 array[10] = SkFixedToFloat(read_big_endian_i32(src + 32)); |
685 array[11] = scale * SkFixedToFloat(read_big_endian_i32(src + 44)); // transl
ate B | 686 array[11] = SkFixedToFloat(read_big_endian_i32(src + 44)); // translate B |
686 array[12] = 0.0f; | 687 array[12] = 0.0f; |
687 array[13] = 0.0f; | 688 array[13] = 0.0f; |
688 array[14] = 0.0f; | 689 array[14] = 0.0f; |
689 array[15] = 1.0f; | 690 array[15] = 1.0f; |
690 toXYZ->setRowMajorf(array); | 691 toXYZ->setRowMajorf(array); |
691 return true; | 692 return true; |
692 } | 693 } |
693 | 694 |
694 static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) { | 695 static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) { |
695 if (gammas->isNamed(0) && gammas->isNamed(1) && gammas->isNamed(2) && | 696 if (gammas->isNamed(0) && gammas->isNamed(1) && gammas->isNamed(2) && |
696 gammas->fRedData.fNamed == gammas->fGreenData.fNamed && | 697 gammas->fRedData.fNamed == gammas->fGreenData.fNamed && |
697 gammas->fRedData.fNamed == gammas->fBlueData.fNamed) | 698 gammas->fRedData.fNamed == gammas->fBlueData.fNamed) |
698 { | 699 { |
699 return gammas->fRedData.fNamed; | 700 return gammas->fRedData.fNamed; |
700 } | 701 } |
701 | 702 |
702 return kNonStandard_SkGammaNamed; | 703 return kNonStandard_SkGammaNamed; |
703 } | 704 } |
704 | 705 |
| 706 /** |
| 707 * Parse and load an entire stored curve. Handles invalid gammas as well. |
| 708 * |
| 709 * There's nothing to do for the simple cases, but for table gammas we need to
actually |
| 710 * read the table into heap memory. And for parametric gammas, we need to copy
over the |
| 711 * parameter values. |
| 712 * |
| 713 * @param gammaNamed Out-variable. The named gamma curve. |
| 714 * @param gammas Out-variable. The stored gamma curve information. Can be n
ull if |
| 715 * gammaNamed is a named curve |
| 716 * @param rTagPtr Pointer to start of the gamma tag. |
| 717 * @param taglen The size in bytes of the tag |
| 718 * |
| 719 * @return false on failure, true on success |
| 720 */ |
| 721 static bool parse_and_load_gamma(SkGammaNamed* gammaNamed, sk_sp<SkGammas>* gamm
as, |
| 722 const uint8_t* rTagPtr, size_t tagLen) |
| 723 { |
| 724 SkGammas::Data rData; |
| 725 SkColorSpaceTransferFn rParams; |
705 | 726 |
706 static bool load_a2b0(sk_sp<SkColorLookUpTable>* colorLUT, SkGammaNamed* gammaNa
med, | 727 // On an invalid first gamma, tagBytes remains set as zero. This causes the
two |
707 sk_sp<SkGammas>* gammas, SkMatrix44* toXYZ, const uint8_t*
src, size_t len) { | 728 // subsequent to be treated as identical (which is what we want). |
| 729 size_t tagBytes = 0; |
| 730 SkGammas::Type rType = parse_gamma(&rData, &rParams, &tagBytes, rTagPtr, tag
Len); |
| 731 handle_invalid_gamma(&rType, &rData); |
| 732 size_t alignedTagBytes = SkAlign4(tagBytes); |
| 733 |
| 734 if ((3 * alignedTagBytes <= tagLen) && |
| 735 !memcmp(rTagPtr, rTagPtr + 1 * alignedTagBytes, tagBytes) && |
| 736 !memcmp(rTagPtr, rTagPtr + 2 * alignedTagBytes, tagBytes)) |
| 737 { |
| 738 if (SkGammas::Type::kNamed_Type == rType) { |
| 739 *gammaNamed = rData.fNamed; |
| 740 } else { |
| 741 size_t allocSize = sizeof(SkGammas); |
| 742 return_if_false(safe_add(allocSize, gamma_alloc_size(rType, rData),
&allocSize), |
| 743 "SkGammas struct is too large to allocate"); |
| 744 void* memory = sk_malloc_throw(allocSize); |
| 745 *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
| 746 load_gammas(memory, 0, rType, &rData, rParams, rTagPtr); |
| 747 |
| 748 (*gammas)->fRedType = rType; |
| 749 (*gammas)->fGreenType = rType; |
| 750 (*gammas)->fBlueType = rType; |
| 751 |
| 752 (*gammas)->fRedData = rData; |
| 753 (*gammas)->fGreenData = rData; |
| 754 (*gammas)->fBlueData = rData; |
| 755 } |
| 756 } else { |
| 757 const uint8_t* gTagPtr = rTagPtr + alignedTagBytes; |
| 758 tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; |
| 759 SkGammas::Data gData; |
| 760 SkColorSpaceTransferFn gParams; |
| 761 tagBytes = 0; |
| 762 SkGammas::Type gType = parse_gamma(&gData, &gParams, &tagBytes, gTagPtr, |
| 763 tagLen); |
| 764 handle_invalid_gamma(&gType, &gData); |
| 765 |
| 766 alignedTagBytes = SkAlign4(tagBytes); |
| 767 const uint8_t* bTagPtr = gTagPtr + alignedTagBytes; |
| 768 tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; |
| 769 SkGammas::Data bData; |
| 770 SkColorSpaceTransferFn bParams; |
| 771 SkGammas::Type bType = parse_gamma(&bData, &bParams, &tagBytes, bTagPtr, |
| 772 tagLen); |
| 773 handle_invalid_gamma(&bType, &bData); |
| 774 |
| 775 size_t allocSize = sizeof(SkGammas); |
| 776 return_if_false(safe_add(allocSize, gamma_alloc_size(rType, rData), &all
ocSize), |
| 777 "SkGammas struct is too large to allocate"); |
| 778 return_if_false(safe_add(allocSize, gamma_alloc_size(gType, gData), &all
ocSize), |
| 779 "SkGammas struct is too large to allocate"); |
| 780 return_if_false(safe_add(allocSize, gamma_alloc_size(bType, bData), &all
ocSize), |
| 781 "SkGammas struct is too large to allocate"); |
| 782 void* memory = sk_malloc_throw(allocSize); |
| 783 *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
| 784 |
| 785 uint32_t offset = 0; |
| 786 (*gammas)->fRedType = rType; |
| 787 offset += load_gammas(memory, offset, rType, &rData, rParams, rTagPtr); |
| 788 |
| 789 (*gammas)->fGreenType = gType; |
| 790 offset += load_gammas(memory, offset, gType, &gData, gParams, gTagPtr); |
| 791 |
| 792 (*gammas)->fBlueType = bType; |
| 793 load_gammas(memory, offset, bType, &bData, bParams, bTagPtr); |
| 794 |
| 795 (*gammas)->fRedData = rData; |
| 796 (*gammas)->fGreenData = gData; |
| 797 (*gammas)->fBlueData = bData; |
| 798 } |
| 799 |
| 800 if (kNonStandard_SkGammaNamed == *gammaNamed) { |
| 801 *gammaNamed = is_named(*gammas); |
| 802 if (kNonStandard_SkGammaNamed != *gammaNamed) { |
| 803 // No need to keep the gammas struct, the enum is enough. |
| 804 *gammas = nullptr; |
| 805 } |
| 806 } |
| 807 return true; |
| 808 } |
| 809 |
| 810 static bool load_a2b0(sk_sp<SkColorLookUpTable>* colorLUT, |
| 811 SkGammaNamed* aCurveNamed, sk_sp<SkGammas>* aCurve, |
| 812 SkGammaNamed* mCurveNamed, sk_sp<SkGammas>* mCurve, |
| 813 SkGammaNamed* bCurveNamed, sk_sp<SkGammas>* bCurve, |
| 814 SkMatrix44* matrix, const uint8_t* src, size_t len) { |
708 if (len < 32) { | 815 if (len < 32) { |
709 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); | 816 SkColorSpacePrintf("A to B tag is too small (%d bytes).", len); |
710 return false; | 817 return false; |
711 } | 818 } |
712 | 819 |
713 uint32_t type = read_big_endian_u32(src); | 820 uint32_t type = read_big_endian_u32(src); |
714 if (kTAG_AtoBType != type) { | 821 if (kTAG_AtoBType != type) { |
715 // FIXME (msarett): Need to support lut8Type and lut16Type. | 822 // FIXME (msarett): Need to support lut8Type and lut16Type. |
716 SkColorSpacePrintf("Unsupported A to B tag type.\n"); | 823 SkColorSpacePrintf("Unsupported A to B tag type.\n"); |
717 return false; | 824 return false; |
718 } | 825 } |
719 | 826 |
720 // Read the number of channels. The four bytes that we skipped are reserved
and | 827 // Read the number of channels. The four bytes that we skipped are reserved
and |
721 // must be zero. | 828 // must be zero. |
722 uint8_t inputChannels = src[8]; | 829 uint8_t inputChannels = src[8]; |
723 uint8_t outputChannels = src[9]; | 830 uint8_t outputChannels = src[9]; |
724 if (3 != inputChannels || SkColorLookUpTable::kOutputChannels != outputChann
els) { | 831 if (3 != inputChannels || SkColorLookUpTable::kOutputChannels != outputChann
els) { |
725 // We only handle (supposedly) RGB inputs and RGB outputs. The numbers
of input | 832 // We only handle (supposedly) RGB inputs and RGB outputs. The numbers
of input |
726 // channels and output channels both must be 3. | 833 // channels and output channels both must be 3. |
727 // TODO (msarett): | 834 // TODO (msarett): |
728 // Support different numbers of input channels. Ex: CMYK (4). | 835 // Support different numbers of input channels. Ex: CMYK (4). |
729 SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag
.\n"); | 836 SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag
.\n"); |
730 return false; | 837 return false; |
731 } | 838 } |
732 | 839 |
733 // Read the offsets of each element in the A to B tag. With the exception o
f A curves and | |
734 // B curves (which we do not yet support), we will handle these elements in
the order in | |
735 // which they should be applied (rather than the order in which they occur i
n the tag). | |
736 // If the offset is non-zero it indicates that the element is present. | 840 // If the offset is non-zero it indicates that the element is present. |
737 uint32_t offsetToACurves = read_big_endian_i32(src + 28); | 841 uint32_t offsetToACurves = read_big_endian_i32(src + 28); |
738 uint32_t offsetToBCurves = read_big_endian_i32(src + 12); | 842 if (0 != offsetToACurves && offsetToACurves < len) { |
739 if ((0 != offsetToACurves) || (0 != offsetToBCurves)) { | 843 const size_t tagLen = len - offsetToACurves; |
740 // FIXME (msarett): Handle A and B curves. | 844 if (!parse_and_load_gamma(aCurveNamed, aCurve, src + offsetToACurves, ta
gLen)) { |
741 // Note that the A curve is technically required in order to have a colo
r LUT. | 845 return false; |
742 // However, all the A curves I have seen so far have are just placeholde
rs that | 846 } |
743 // don't actually transform the data. | |
744 SkColorSpacePrintf("Ignoring A and/or B curve. Output may be wrong.\n")
; | |
745 } | 847 } |
746 | 848 |
747 uint32_t offsetToColorLUT = read_big_endian_i32(src + 24); | 849 uint32_t offsetToColorLUT = read_big_endian_i32(src + 24); |
748 if (0 != offsetToColorLUT && offsetToColorLUT < len) { | 850 if (0 != offsetToColorLUT && offsetToColorLUT < len) { |
749 if (!load_color_lut(colorLUT, inputChannels, src + offsetToColorLUT, | 851 if (!load_color_lut(colorLUT, inputChannels, src + offsetToColorLUT, |
750 len - offsetToColorLUT)) { | 852 len - offsetToColorLUT)) { |
751 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); | 853 SkColorSpacePrintf("Failed to read color LUT from A to B tag.\n"); |
| 854 return false; |
752 } | 855 } |
753 } | 856 } |
754 | 857 |
755 uint32_t offsetToMCurves = read_big_endian_i32(src + 20); | 858 uint32_t offsetToMCurves = read_big_endian_i32(src + 20); |
756 if (0 != offsetToMCurves && offsetToMCurves < len) { | 859 if (0 != offsetToMCurves && offsetToMCurves < len) { |
757 const uint8_t* rTagPtr = src + offsetToMCurves; | 860 const size_t tagLen = len - offsetToMCurves; |
758 size_t tagLen = len - offsetToMCurves; | 861 if (!parse_and_load_gamma(mCurveNamed, mCurve, src + offsetToMCurves, ta
gLen)) { |
759 | 862 return false; |
760 SkGammas::Data rData; | |
761 SkColorSpaceTransferFn rParams; | |
762 | |
763 // On an invalid first gamma, tagBytes remains set as zero. This causes
the two | |
764 // subsequent to be treated as identical (which is what we want). | |
765 size_t tagBytes = 0; | |
766 SkGammas::Type rType = parse_gamma(&rData, &rParams, &tagBytes, rTagPtr,
tagLen); | |
767 handle_invalid_gamma(&rType, &rData); | |
768 size_t alignedTagBytes = SkAlign4(tagBytes); | |
769 | |
770 if ((3 * alignedTagBytes <= tagLen) && | |
771 !memcmp(rTagPtr, rTagPtr + 1 * alignedTagBytes, tagBytes) && | |
772 !memcmp(rTagPtr, rTagPtr + 2 * alignedTagBytes, tagBytes)) | |
773 { | |
774 if (SkGammas::Type::kNamed_Type == rType) { | |
775 *gammaNamed = rData.fNamed; | |
776 } else { | |
777 size_t allocSize = sizeof(SkGammas); | |
778 return_if_false(safe_add(allocSize, gamma_alloc_size(rType, rDat
a), &allocSize), | |
779 "SkGammas struct is too large to allocate"); | |
780 void* memory = sk_malloc_throw(allocSize); | |
781 *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); | |
782 load_gammas(memory, 0, rType, &rData, rParams, rTagPtr); | |
783 | |
784 (*gammas)->fRedType = rType; | |
785 (*gammas)->fGreenType = rType; | |
786 (*gammas)->fBlueType = rType; | |
787 | |
788 (*gammas)->fRedData = rData; | |
789 (*gammas)->fGreenData = rData; | |
790 (*gammas)->fBlueData = rData; | |
791 } | |
792 } else { | |
793 const uint8_t* gTagPtr = rTagPtr + alignedTagBytes; | |
794 tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; | |
795 SkGammas::Data gData; | |
796 SkColorSpaceTransferFn gParams; | |
797 tagBytes = 0; | |
798 SkGammas::Type gType = parse_gamma(&gData, &gParams, &tagBytes, gTag
Ptr, | |
799 tagLen); | |
800 handle_invalid_gamma(&gType, &gData); | |
801 | |
802 alignedTagBytes = SkAlign4(tagBytes); | |
803 const uint8_t* bTagPtr = gTagPtr + alignedTagBytes; | |
804 tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0; | |
805 SkGammas::Data bData; | |
806 SkColorSpaceTransferFn bParams; | |
807 SkGammas::Type bType = parse_gamma(&bData, &bParams, &tagBytes, bTag
Ptr, | |
808 tagLen); | |
809 handle_invalid_gamma(&bType, &bData); | |
810 | |
811 size_t allocSize = sizeof(SkGammas); | |
812 return_if_false(safe_add(allocSize, gamma_alloc_size(rType, rData),
&allocSize), | |
813 "SkGammas struct is too large to allocate"); | |
814 return_if_false(safe_add(allocSize, gamma_alloc_size(gType, gData),
&allocSize), | |
815 "SkGammas struct is too large to allocate"); | |
816 return_if_false(safe_add(allocSize, gamma_alloc_size(bType, bData),
&allocSize), | |
817 "SkGammas struct is too large to allocate"); | |
818 void* memory = sk_malloc_throw(allocSize); | |
819 *gammas = sk_sp<SkGammas>(new (memory) SkGammas()); | |
820 | |
821 uint32_t offset = 0; | |
822 (*gammas)->fRedType = rType; | |
823 offset += load_gammas(memory, offset, rType, &rData, rParams, rTagPt
r); | |
824 | |
825 (*gammas)->fGreenType = gType; | |
826 offset += load_gammas(memory, offset, gType, &gData, gParams, gTagPt
r); | |
827 | |
828 (*gammas)->fBlueType = bType; | |
829 load_gammas(memory, offset, bType, &bData, bParams, bTagPtr); | |
830 | |
831 (*gammas)->fRedData = rData; | |
832 (*gammas)->fGreenData = gData; | |
833 (*gammas)->fBlueData = bData; | |
834 } | |
835 } else { | |
836 // Guess sRGB if the chunk is missing a transfer function. | |
837 *gammaNamed = kSRGB_SkGammaNamed; | |
838 } | |
839 | |
840 if (kNonStandard_SkGammaNamed == *gammaNamed) { | |
841 *gammaNamed = is_named(*gammas); | |
842 if (kNonStandard_SkGammaNamed != *gammaNamed) { | |
843 // No need to keep the gammas struct, the enum is enough. | |
844 *gammas = nullptr; | |
845 } | 863 } |
846 } | 864 } |
847 | 865 |
848 uint32_t offsetToMatrix = read_big_endian_i32(src + 16); | 866 uint32_t offsetToMatrix = read_big_endian_i32(src + 16); |
849 if (0 != offsetToMatrix && offsetToMatrix < len) { | 867 if (0 != offsetToMatrix && offsetToMatrix < len) { |
850 if (!load_matrix(toXYZ, src + offsetToMatrix, len - offsetToMatrix)) { | 868 if (!load_matrix(matrix, src + offsetToMatrix, len - offsetToMatrix)) { |
851 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); | 869 SkColorSpacePrintf("Failed to read matrix from A to B tag.\n"); |
852 toXYZ->setIdentity(); | 870 matrix->setIdentity(); |
| 871 } |
| 872 } |
| 873 |
| 874 uint32_t offsetToBCurves = read_big_endian_i32(src + 12); |
| 875 if (0 != offsetToBCurves && offsetToBCurves < len) { |
| 876 const size_t tagLen = len - offsetToBCurves; |
| 877 if (!parse_and_load_gamma(bCurveNamed, bCurve, src + offsetToBCurves, ta
gLen)) { |
| 878 return false; |
853 } | 879 } |
854 } | 880 } |
855 | 881 |
856 return true; | 882 return true; |
857 } | 883 } |
858 | 884 |
859 static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) { | 885 static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) { |
860 if (!a || !b) { | 886 if (!a || !b) { |
861 return a == b; | 887 return a == b; |
862 } | 888 } |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
920 } | 946 } |
921 } | 947 } |
922 | 948 |
923 switch (header.fInputColorSpace) { | 949 switch (header.fInputColorSpace) { |
924 case kRGB_ColorSpace: { | 950 case kRGB_ColorSpace: { |
925 // Recognize the rXYZ, gXYZ, and bXYZ tags. | 951 // Recognize the rXYZ, gXYZ, and bXYZ tags. |
926 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); | 952 const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); |
927 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); | 953 const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); |
928 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); | 954 const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); |
929 if (r && g && b) { | 955 if (r && g && b) { |
| 956 // Lab PCS means the profile is required to be an n-component LU
T-based |
| 957 // profile, so 3-component matrix-based profiles can only have a
n XYZ PCS |
| 958 if (kXYZ_PCSSpace != header.fPCS) { |
| 959 return_null("Unsupported PCS space"); |
| 960 } |
930 float toXYZ[9]; | 961 float toXYZ[9]; |
931 if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) || | 962 if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) || |
932 !load_xyz(&toXYZ[3], g->addr(base), g->fLength) || | 963 !load_xyz(&toXYZ[3], g->addr(base), g->fLength) || |
933 !load_xyz(&toXYZ[6], b->addr(base), b->fLength)) | 964 !load_xyz(&toXYZ[6], b->addr(base), b->fLength)) |
934 { | 965 { |
935 return_null("Need valid rgb tags for XYZ space"); | 966 return_null("Need valid rgb tags for XYZ space"); |
936 } | 967 } |
937 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); | 968 SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor); |
938 mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2], | 969 mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2], |
939 toXYZ[3], toXYZ[4], toXYZ[5], | 970 toXYZ[3], toXYZ[4], toXYZ[5], |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1037 } | 1068 } |
1038 } else { | 1069 } else { |
1039 // Guess sRGB if the profile is missing transfer functions. | 1070 // Guess sRGB if the profile is missing transfer functions. |
1040 gammaNamed = kSRGB_SkGammaNamed; | 1071 gammaNamed = kSRGB_SkGammaNamed; |
1041 } | 1072 } |
1042 | 1073 |
1043 if (kNonStandard_SkGammaNamed == gammaNamed) { | 1074 if (kNonStandard_SkGammaNamed == gammaNamed) { |
1044 // It's possible that we'll initially detect non-matching ga
mmas, only for | 1075 // It's possible that we'll initially detect non-matching ga
mmas, only for |
1045 // them to evaluate to the same named gamma curve. | 1076 // them to evaluate to the same named gamma curve. |
1046 gammaNamed = is_named(gammas); | 1077 gammaNamed = is_named(gammas); |
1047 if (kNonStandard_SkGammaNamed == gammaNamed) { | 1078 } |
1048 return sk_sp<SkColorSpace>(new SkColorSpace_Base(nullptr
, gammaNamed, | 1079 |
1049 std::mo
ve(gammas), mat, | 1080 if (kNonStandard_SkGammaNamed == gammaNamed) { |
1050 std::mo
ve(data))); | 1081 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed, |
1051 } | 1082 std::move(ga
mmas), |
| 1083 mat, std::mo
ve(data))); |
1052 } | 1084 } |
1053 | 1085 |
1054 return SkColorSpace_Base::NewRGB(gammaNamed, mat); | 1086 return SkColorSpace_Base::NewRGB(gammaNamed, mat); |
1055 } | 1087 } |
1056 | 1088 |
1057 // Recognize color profile specified by A2B0 tag. | 1089 // Recognize color profile specified by A2B0 tag. |
1058 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); | 1090 const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0); |
1059 if (a2b0) { | 1091 if (a2b0) { |
1060 SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed; | 1092 // default to Linear transforms for when the curves are not |
1061 sk_sp<SkGammas> gammas = nullptr; | 1093 // in the profile (which is legal behavior for a profile) |
| 1094 SkGammaNamed aCurveNamed = kLinear_SkGammaNamed; |
| 1095 SkGammaNamed mCurveNamed = kLinear_SkGammaNamed; |
| 1096 SkGammaNamed bCurveNamed = kLinear_SkGammaNamed; |
| 1097 sk_sp<SkGammas> aCurve = nullptr; |
| 1098 sk_sp<SkGammas> mCurve = nullptr; |
| 1099 sk_sp<SkGammas> bCurve = nullptr; |
1062 sk_sp<SkColorLookUpTable> colorLUT = nullptr; | 1100 sk_sp<SkColorLookUpTable> colorLUT = nullptr; |
1063 SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor); | 1101 SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor); |
1064 if (!load_a2b0(&colorLUT, &gammaNamed, &gammas, &toXYZ, a2b0->ad
dr(base), | 1102 if (!load_a2b0(&colorLUT, &aCurveNamed, &aCurve, &mCurveNamed, &
mCurve, |
1065 a2b0->fLength)) { | 1103 &bCurveNamed, &bCurve, &matrix, a2b0->addr(base),
a2b0->fLength)) { |
1066 return_null("Failed to parse A2B0 tag"); | 1104 return_null("Failed to parse A2B0 tag"); |
1067 } | 1105 } |
1068 | 1106 |
1069 if (colorLUT || kNonStandard_SkGammaNamed == gammaNamed) { | 1107 SkColorSpace_A2B::PCS pcs = SkColorSpace_A2B::PCS::kLAB; |
1070 return sk_sp<SkColorSpace>(new SkColorSpace_Base(std::move(c
olorLUT), | 1108 if (header.fPCS == kXYZ_PCSSpace) { |
1071 gammaNamed,
std::move(gammas), | 1109 pcs = SkColorSpace_A2B::PCS::kXYZ; |
1072 toXYZ, std:
:move(data))); | |
1073 } | 1110 } |
1074 | 1111 |
1075 return SkColorSpace_Base::NewRGB(gammaNamed, toXYZ); | 1112 return sk_sp<SkColorSpace>(new SkColorSpace_A2B(aCurveNamed, std
::move(aCurve), |
| 1113 std::move(colorL
UT), |
| 1114 mCurveNamed, std
::move(mCurve), |
| 1115 matrix, |
| 1116 bCurveNamed, std
::move(bCurve), |
| 1117 pcs, std::move(d
ata))); |
1076 } | 1118 } |
1077 } | 1119 } |
1078 default: | 1120 default: |
1079 break; | 1121 break; |
1080 } | 1122 } |
1081 | 1123 |
1082 return_null("ICC profile contains unsupported colorspace"); | 1124 return_null("ICC profile contains unsupported colorspace"); |
1083 } | 1125 } |
1084 | 1126 |
1085 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | 1127 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1210 // Pad tag with zero. | 1252 // Pad tag with zero. |
1211 ptr16[1] = 0; | 1253 ptr16[1] = 0; |
1212 } | 1254 } |
1213 | 1255 |
1214 sk_sp<SkData> SkColorSpace_Base::writeToICC() const { | 1256 sk_sp<SkData> SkColorSpace_Base::writeToICC() const { |
1215 // Return if this object was created from a profile, or if we have already s
erialized | 1257 // Return if this object was created from a profile, or if we have already s
erialized |
1216 // the profile. | 1258 // the profile. |
1217 if (fProfileData) { | 1259 if (fProfileData) { |
1218 return fProfileData; | 1260 return fProfileData; |
1219 } | 1261 } |
| 1262 // Profile Data is be mandatory for A2B0 Color Spaces |
| 1263 SkASSERT(type() == Type::kXYZ); |
1220 | 1264 |
1221 // The client may create an SkColorSpace using an SkMatrix44, but currently
we only | 1265 // The client may create an SkColorSpace using an SkMatrix44, but currently
we only |
1222 // support writing profiles with 3x3 matrices. | 1266 // support writing profiles with 3x3 matrices. |
1223 // TODO (msarett): Fix this! | 1267 // TODO (msarett): Fix this! |
1224 if (0.0f != fToXYZD50.getFloat(3, 0) || 0.0f != fToXYZD50.getFloat(3, 1) || | 1268 const SkColorSpace_XYZ* thisXYZ = static_cast<const SkColorSpace_XYZ*>(this)
; |
1225 0.0f != fToXYZD50.getFloat(3, 2) || 0.0f != fToXYZD50.getFloat(0, 3) || | 1269 const SkMatrix44& toXYZD50 = *thisXYZ->toXYZD50(); |
1226 0.0f != fToXYZD50.getFloat(1, 3) || 0.0f != fToXYZD50.getFloat(2, 3)) | 1270 if (0.0f != toXYZD50.getFloat(3, 0) || 0.0f != toXYZD50.getFloat(3, 1) || |
| 1271 0.0f != toXYZD50.getFloat(3, 2) || 0.0f != toXYZD50.getFloat(0, 3) || |
| 1272 0.0f != toXYZD50.getFloat(1, 3) || 0.0f != toXYZD50.getFloat(2, 3)) |
1227 { | 1273 { |
1228 return nullptr; | 1274 return nullptr; |
1229 } | 1275 } |
1230 | 1276 |
1231 SkAutoMalloc profile(kICCProfileSize); | 1277 SkAutoMalloc profile(kICCProfileSize); |
1232 uint8_t* ptr = (uint8_t*) profile.get(); | 1278 uint8_t* ptr = (uint8_t*) profile.get(); |
1233 | 1279 |
1234 // Write profile header | 1280 // Write profile header |
1235 memcpy(ptr, gICCHeader, sizeof(gICCHeader)); | 1281 memcpy(ptr, gICCHeader, sizeof(gICCHeader)); |
1236 ptr += sizeof(gICCHeader); | 1282 ptr += sizeof(gICCHeader); |
1237 | 1283 |
1238 // Write tag table | 1284 // Write tag table |
1239 memcpy(ptr, gICCTagTable, sizeof(gICCTagTable)); | 1285 memcpy(ptr, gICCTagTable, sizeof(gICCTagTable)); |
1240 ptr += sizeof(gICCTagTable); | 1286 ptr += sizeof(gICCTagTable); |
1241 | 1287 |
1242 // Write profile description tag | 1288 // Write profile description tag |
1243 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); | 1289 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); |
1244 ptr += sizeof(gEmptyTextTag); | 1290 ptr += sizeof(gEmptyTextTag); |
1245 | 1291 |
1246 // Write XYZ tags | 1292 // Write XYZ tags |
1247 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 0); | 1293 write_xyz_tag((uint32_t*) ptr, toXYZD50, 0); |
1248 ptr += kTAG_XYZ_Bytes; | 1294 ptr += kTAG_XYZ_Bytes; |
1249 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 1); | 1295 write_xyz_tag((uint32_t*) ptr, toXYZD50, 1); |
1250 ptr += kTAG_XYZ_Bytes; | 1296 ptr += kTAG_XYZ_Bytes; |
1251 write_xyz_tag((uint32_t*) ptr, fToXYZD50, 2); | 1297 write_xyz_tag((uint32_t*) ptr, toXYZD50, 2); |
1252 ptr += kTAG_XYZ_Bytes; | 1298 ptr += kTAG_XYZ_Bytes; |
1253 | 1299 |
1254 // Write TRC tags | 1300 // Write TRC tags |
1255 SkGammaNamed gammaNamed = this->gammaNamed(); | 1301 SkGammaNamed gammaNamed = thisXYZ->gammaNamed(); |
1256 if (kNonStandard_SkGammaNamed == gammaNamed) { | 1302 if (kNonStandard_SkGammaNamed == gammaNamed) { |
1257 // FIXME (msarett): | 1303 // FIXME (msarett): |
1258 // Write the correct gamma representation rather than 2.2f. | 1304 // Write the correct gamma representation rather than 2.2f. |
1259 write_trc_tag((uint32_t*) ptr, 2.2f); | 1305 write_trc_tag((uint32_t*) ptr, 2.2f); |
1260 ptr += SkAlign4(kTAG_TRC_Bytes); | 1306 ptr += SkAlign4(kTAG_TRC_Bytes); |
1261 write_trc_tag((uint32_t*) ptr, 2.2f); | 1307 write_trc_tag((uint32_t*) ptr, 2.2f); |
1262 ptr += SkAlign4(kTAG_TRC_Bytes); | 1308 ptr += SkAlign4(kTAG_TRC_Bytes); |
1263 write_trc_tag((uint32_t*) ptr, 2.2f); | 1309 write_trc_tag((uint32_t*) ptr, 2.2f); |
1264 ptr += SkAlign4(kTAG_TRC_Bytes); | 1310 ptr += SkAlign4(kTAG_TRC_Bytes); |
1265 } else { | 1311 } else { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1302 ptr32[4] = SkEndian_SwapBE32(0x000116cc); | 1348 ptr32[4] = SkEndian_SwapBE32(0x000116cc); |
1303 ptr += kTAG_XYZ_Bytes; | 1349 ptr += kTAG_XYZ_Bytes; |
1304 | 1350 |
1305 // Write copyright tag | 1351 // Write copyright tag |
1306 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); | 1352 memcpy(ptr, gEmptyTextTag, sizeof(gEmptyTextTag)); |
1307 | 1353 |
1308 // TODO (msarett): Should we try to hold onto the data so we can return imme
diately if | 1354 // TODO (msarett): Should we try to hold onto the data so we can return imme
diately if |
1309 // the client calls again? | 1355 // the client calls again? |
1310 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize); | 1356 return SkData::MakeFromMalloc(profile.release(), kICCProfileSize); |
1311 } | 1357 } |
OLD | NEW |