msarett | bb9f774 | 2016-05-17 09:31:20 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
msarett | 8cc2091 | 2016-05-23 09:29:29 -0700 | [diff] [blame] | 8 | #ifndef SkColorSpace_Base_DEFINED |
| 9 | #define SkColorSpace_Base_DEFINED |
| 10 | |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 11 | #include "SkColorLookUpTable.h" |
msarett | 8cc2091 | 2016-05-23 09:29:29 -0700 | [diff] [blame] | 12 | #include "SkColorSpace.h" |
msarett | ab926f0 | 2016-05-25 08:53:40 -0700 | [diff] [blame] | 13 | #include "SkData.h" |
brianosman | 971cd49 | 2016-09-08 10:10:11 -0700 | [diff] [blame] | 14 | #include "SkOnce.h" |
msarett | e077e06 | 2016-05-24 10:16:53 -0700 | [diff] [blame] | 15 | #include "SkTemplates.h" |
msarett | bb9f774 | 2016-05-17 09:31:20 -0700 | [diff] [blame] | 16 | |
msarett | 600c737 | 2016-09-07 12:03:53 -0700 | [diff] [blame] | 17 | enum SkGammaNamed : uint8_t { |
| 18 | kLinear_SkGammaNamed, |
| 19 | kSRGB_SkGammaNamed, |
| 20 | k2Dot2Curve_SkGammaNamed, |
| 21 | kNonStandard_SkGammaNamed, |
| 22 | }; |
| 23 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 24 | struct SkGammas : SkRefCnt { |
msarett | 2aec3ff | 2016-07-20 12:58:52 -0700 | [diff] [blame] | 25 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 26 | // There are four possible representations for gamma curves. kNone_Type is used |
| 27 | // as a placeholder until the struct is initialized. It is not a valid value. |
| 28 | enum class Type : uint8_t { |
| 29 | kNone_Type, |
| 30 | kNamed_Type, |
| 31 | kValue_Type, |
| 32 | kTable_Type, |
| 33 | kParam_Type, |
| 34 | }; |
msarett | 2aec3ff | 2016-07-20 12:58:52 -0700 | [diff] [blame] | 35 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 36 | // Contains information for a gamma table. |
| 37 | struct Table { |
| 38 | size_t fOffset; |
| 39 | int fSize; |
msarett | 959ccc1 | 2016-07-20 15:10:02 -0700 | [diff] [blame] | 40 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 41 | const float* table(const SkGammas* base) const { |
| 42 | return SkTAddOffset<const float>(base, sizeof(SkGammas) + fOffset); |
| 43 | } |
| 44 | }; |
msarett | 959ccc1 | 2016-07-20 15:10:02 -0700 | [diff] [blame] | 45 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 46 | // Contains the actual gamma curve information. Should be interpreted |
| 47 | // based on the type of the gamma curve. |
| 48 | union Data { |
| 49 | Data() |
| 50 | : fTable{ 0, 0 } |
| 51 | {} |
msarett | 959ccc1 | 2016-07-20 15:10:02 -0700 | [diff] [blame] | 52 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 53 | inline bool operator==(const Data& that) const { |
| 54 | return this->fTable.fOffset == that.fTable.fOffset && |
| 55 | this->fTable.fSize == that.fTable.fSize; |
msarett | 456bf30 | 2016-07-20 16:14:16 -0700 | [diff] [blame] | 56 | } |
| 57 | |
Ravi Mistry | 113d05f | 2016-12-17 01:31:03 +0000 | [diff] [blame] | 58 | inline bool operator!=(const Data& that) const { |
| 59 | return !(*this == that); |
| 60 | } |
| 61 | |
msarett | 600c737 | 2016-09-07 12:03:53 -0700 | [diff] [blame] | 62 | SkGammaNamed fNamed; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 63 | float fValue; |
| 64 | Table fTable; |
| 65 | size_t fParamOffset; |
| 66 | |
Matt Sarett | df44fc5 | 2016-10-11 16:57:50 -0400 | [diff] [blame] | 67 | const SkColorSpaceTransferFn& params(const SkGammas* base) const { |
| 68 | return *SkTAddOffset<const SkColorSpaceTransferFn>( |
| 69 | base, sizeof(SkGammas) + fParamOffset); |
msarett | 456bf30 | 2016-07-20 16:14:16 -0700 | [diff] [blame] | 70 | } |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 71 | }; |
msarett | 456bf30 | 2016-07-20 16:14:16 -0700 | [diff] [blame] | 72 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 73 | bool isNamed(int i) const { |
msarett | a714bc3 | 2016-07-29 08:58:33 -0700 | [diff] [blame] | 74 | return Type::kNamed_Type == this->type(i); |
msarett | 456bf30 | 2016-07-20 16:14:16 -0700 | [diff] [blame] | 75 | } |
| 76 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 77 | bool isValue(int i) const { |
msarett | a714bc3 | 2016-07-29 08:58:33 -0700 | [diff] [blame] | 78 | return Type::kValue_Type == this->type(i); |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 79 | } |
msarett | 456bf30 | 2016-07-20 16:14:16 -0700 | [diff] [blame] | 80 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 81 | bool isTable(int i) const { |
msarett | a714bc3 | 2016-07-29 08:58:33 -0700 | [diff] [blame] | 82 | return Type::kTable_Type == this->type(i); |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 83 | } |
| 84 | |
| 85 | bool isParametric(int i) const { |
msarett | a714bc3 | 2016-07-29 08:58:33 -0700 | [diff] [blame] | 86 | return Type::kParam_Type == this->type(i); |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | const Data& data(int i) const { |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 90 | SkASSERT(i >= 0 && i < fChannels); |
| 91 | return fData[i]; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | const float* table(int i) const { |
| 95 | SkASSERT(isTable(i)); |
msarett | a714bc3 | 2016-07-29 08:58:33 -0700 | [diff] [blame] | 96 | return this->data(i).fTable.table(this); |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 97 | } |
| 98 | |
raftias | a97a60c | 2016-12-16 17:24:30 -0500 | [diff] [blame] | 99 | int tableSize(int i) const { |
| 100 | SkASSERT(isTable(i)); |
| 101 | return this->data(i).fTable.fSize; |
| 102 | } |
| 103 | |
Matt Sarett | df44fc5 | 2016-10-11 16:57:50 -0400 | [diff] [blame] | 104 | const SkColorSpaceTransferFn& params(int i) const { |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 105 | SkASSERT(isParametric(i)); |
msarett | a714bc3 | 2016-07-29 08:58:33 -0700 | [diff] [blame] | 106 | return this->data(i).params(this); |
| 107 | } |
| 108 | |
| 109 | Type type(int i) const { |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 110 | SkASSERT(i >= 0 && i < fChannels); |
| 111 | return fType[i]; |
| 112 | } |
Matt Sarett | 1fa986c | 2017-05-01 17:42:20 -0400 | [diff] [blame] | 113 | |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 114 | uint8_t channels() const { return fChannels; } |
| 115 | |
| 116 | SkGammas(uint8_t channels) |
| 117 | : fChannels(channels) { |
| 118 | SkASSERT(channels <= kMaxColorChannels); |
| 119 | for (uint8_t i = 0; i < kMaxColorChannels; ++i) { |
| 120 | fType[i] = Type::kNone_Type; |
msarett | a714bc3 | 2016-07-29 08:58:33 -0700 | [diff] [blame] | 121 | } |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 122 | } |
| 123 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 124 | // These fields should only be modified when initializing the struct. |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 125 | uint8_t fChannels; |
| 126 | Data fData[kMaxColorChannels]; |
| 127 | Type fType[kMaxColorChannels]; |
msarett | 456bf30 | 2016-07-20 16:14:16 -0700 | [diff] [blame] | 128 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 129 | // Objects of this type are sometimes created in a custom fashion using |
| 130 | // sk_malloc_throw and therefore must be sk_freed. We overload new to |
| 131 | // also call sk_malloc_throw so that memory can be unconditionally released |
| 132 | // using sk_free in an overloaded delete. Overloading regular new means we |
| 133 | // must also overload placement new. |
| 134 | void* operator new(size_t size) { return sk_malloc_throw(size); } |
| 135 | void* operator new(size_t, void* p) { return p; } |
| 136 | void operator delete(void* p) { sk_free(p); } |
msarett | bb9f774 | 2016-05-17 09:31:20 -0700 | [diff] [blame] | 137 | }; |
| 138 | |
msarett | 8cc2091 | 2016-05-23 09:29:29 -0700 | [diff] [blame] | 139 | class SkColorSpace_Base : public SkColorSpace { |
| 140 | public: |
Matt Sarett | f489886 | 2016-10-16 10:20:41 -0400 | [diff] [blame] | 141 | |
raftias | 7c602de | 2016-10-13 10:45:44 -0700 | [diff] [blame] | 142 | /** |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 143 | * Describes color space gamut as a transformation to XYZ D50. |
| 144 | * Returns nullptr if color gamut cannot be described in terms of XYZ D50. |
raftias | 7c602de | 2016-10-13 10:45:44 -0700 | [diff] [blame] | 145 | */ |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 146 | virtual const SkMatrix44* toXYZD50() const = 0; |
| 147 | |
| 148 | /** |
Brian Osman | bbf251b | 2016-10-19 14:56:07 -0400 | [diff] [blame] | 149 | * Returns a hash of the gamut transofmration to XYZ D50. Allows for fast equality checking |
| 150 | * of gamuts, at the (very small) risk of collision. |
| 151 | * Returns 0 if color gamut cannot be described in terms of XYZ D50. |
| 152 | */ |
| 153 | virtual uint32_t toXYZD50Hash() const = 0; |
| 154 | |
| 155 | /** |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 156 | * Describes color space gamut as a transformation from XYZ D50 |
| 157 | * Returns nullptr if color gamut cannot be described in terms of XYZ D50. |
| 158 | */ |
| 159 | virtual const SkMatrix44* fromXYZD50() const = 0; |
Ravi Mistry | 113d05f | 2016-12-17 01:31:03 +0000 | [diff] [blame] | 160 | |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 161 | virtual bool onGammaCloseToSRGB() const = 0; |
Ravi Mistry | 113d05f | 2016-12-17 01:31:03 +0000 | [diff] [blame] | 162 | |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 163 | virtual bool onGammaIsLinear() const = 0; |
Ravi Mistry | 113d05f | 2016-12-17 01:31:03 +0000 | [diff] [blame] | 164 | |
| 165 | virtual bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const = 0; |
| 166 | |
Matt Sarett | 7f15b68 | 2017-02-24 17:22:09 -0500 | [diff] [blame] | 167 | virtual bool onIsCMYK() const { return false; } |
| 168 | |
Brian Osman | 12313f0 | 2016-11-07 09:42:37 -0500 | [diff] [blame] | 169 | /** |
| 170 | * Returns a color space with the same gamut as this one, but with a linear gamma. |
| 171 | * For color spaces whose gamut can not be described in terms of XYZ D50, returns |
| 172 | * linear sRGB. |
| 173 | */ |
Mike Klein | b1e6cfd | 2017-06-12 15:20:33 -0400 | [diff] [blame] | 174 | virtual sk_sp<SkColorSpace> makeLinearGamma() const = 0; |
Brian Osman | 12313f0 | 2016-11-07 09:42:37 -0500 | [diff] [blame] | 175 | |
| 176 | /** |
| 177 | * Returns a color space with the same gamut as this one, with with the sRGB transfer |
| 178 | * function. For color spaces whose gamut can not be described in terms of XYZ D50, returns |
| 179 | * sRGB. |
| 180 | */ |
Mike Klein | b1e6cfd | 2017-06-12 15:20:33 -0400 | [diff] [blame] | 181 | virtual sk_sp<SkColorSpace> makeSRGBGamma() const = 0; |
Brian Osman | 12313f0 | 2016-11-07 09:42:37 -0500 | [diff] [blame] | 182 | |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 183 | enum class Type : uint8_t { |
| 184 | kXYZ, |
| 185 | kA2B |
| 186 | }; |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 187 | |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 188 | virtual Type type() const = 0; |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 189 | |
Matt Sarett | 523116d | 2017-01-12 18:36:38 -0500 | [diff] [blame] | 190 | typedef uint8_t ICCTypeFlag; |
| 191 | static constexpr ICCTypeFlag kRGB_ICCTypeFlag = 1 << 0; |
| 192 | static constexpr ICCTypeFlag kCMYK_ICCTypeFlag = 1 << 1; |
| 193 | static constexpr ICCTypeFlag kGray_ICCTypeFlag = 1 << 2; |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 194 | |
Matt Sarett | 523116d | 2017-01-12 18:36:38 -0500 | [diff] [blame] | 195 | static sk_sp<SkColorSpace> MakeICC(const void* input, size_t len, ICCTypeFlag type); |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 196 | |
Matt Sarett | f388093 | 2017-03-24 10:06:03 -0400 | [diff] [blame] | 197 | static sk_sp<SkColorSpace> MakeRGB(SkGammaNamed gammaNamed, const SkMatrix44& toXYZD50); |
Matt Sarett | 595599f | 2016-12-15 13:05:53 -0500 | [diff] [blame] | 198 | |
Matt Sarett | 77a7a1b | 2017-02-07 13:56:11 -0500 | [diff] [blame] | 199 | enum Named : uint8_t { |
| 200 | kSRGB_Named, |
| 201 | kAdobeRGB_Named, |
| 202 | kSRGBLinear_Named, |
Brian Osman | 3f40598 | 2017-02-15 08:51:19 -0500 | [diff] [blame] | 203 | kSRGB_NonLinearBlending_Named, |
Matt Sarett | 77a7a1b | 2017-02-07 13:56:11 -0500 | [diff] [blame] | 204 | }; |
| 205 | |
| 206 | static sk_sp<SkColorSpace> MakeNamed(Named); |
| 207 | |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 208 | protected: |
Matt Sarett | f388093 | 2017-03-24 10:06:03 -0400 | [diff] [blame] | 209 | SkColorSpace_Base(sk_sp<SkData> profileData); |
brianosman | 971cd49 | 2016-09-08 10:10:11 -0700 | [diff] [blame] | 210 | |
msarett | c213f0d | 2016-08-01 14:23:32 -0700 | [diff] [blame] | 211 | private: |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 212 | sk_sp<SkData> fProfileData; |
brianosman | 971cd49 | 2016-09-08 10:10:11 -0700 | [diff] [blame] | 213 | |
msarett | 8cc2091 | 2016-05-23 09:29:29 -0700 | [diff] [blame] | 214 | friend class SkColorSpace; |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 215 | friend class SkColorSpace_XYZ; |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 216 | friend class ColorSpaceXformTest; |
msarett | c213f0d | 2016-08-01 14:23:32 -0700 | [diff] [blame] | 217 | friend class ColorSpaceTest; |
msarett | 8cc2091 | 2016-05-23 09:29:29 -0700 | [diff] [blame] | 218 | typedef SkColorSpace INHERITED; |
| 219 | }; |
| 220 | |
| 221 | static inline SkColorSpace_Base* as_CSB(SkColorSpace* colorSpace) { |
| 222 | return static_cast<SkColorSpace_Base*>(colorSpace); |
| 223 | } |
| 224 | |
msarett | ab926f0 | 2016-05-25 08:53:40 -0700 | [diff] [blame] | 225 | static inline const SkColorSpace_Base* as_CSB(const SkColorSpace* colorSpace) { |
| 226 | return static_cast<const SkColorSpace_Base*>(colorSpace); |
| 227 | } |
| 228 | |
msarett | 888dc16 | 2016-05-23 10:21:17 -0700 | [diff] [blame] | 229 | static inline SkColorSpace_Base* as_CSB(const sk_sp<SkColorSpace>& colorSpace) { |
| 230 | return static_cast<SkColorSpace_Base*>(colorSpace.get()); |
| 231 | } |
| 232 | |
msarett | bb9f774 | 2016-05-17 09:31:20 -0700 | [diff] [blame] | 233 | #endif |