| 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 |