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 
8 #ifndef SkColorSpace_A2B_DEFINED
9 #define SkColorSpace_A2B_DEFINED
10 
11 #include "SkColorLookUpTable.h"
12 #include "SkColorSpace.h"
13 #include "SkGammas.h"
14 #include <vector>
15 
16 // An alternative SkColorSpace that represents all the color space data that
17 // is stored in an A2B0 ICC tag. This allows us to use alternative profile
18 // connection spaces (CIELAB instead of just CIEXYZ), use color-lookup-tables
19 // to do color space transformations not representable as TRC functions or
20 // matrix operations, as well as have multiple TRC functions. The CLUT also
21 // allows conversion between non-3-channel input color spaces ie CMYK(4) to
22 // a workable PCS (ie XYZ).
23 //
24 // AtoBType, lut8Type and lut16Type A2B0 tag types are supported. There are
25 // also MPET (multi-processing-elements) A2B0 tags in the standard which allow
26 // you to combine these 3 primitives (TRC, CLUT, matrix) in any order/quantity.
27 // MPET tags are currently unsupported by the MakeICC parser, could be supported
28 // here by the nature of the design.
29 class SkColorSpace_A2B : public SkColorSpace {
30 public:
onToXYZD50()31     const SkMatrix44* onToXYZD50() const override {
32         // the matrix specified in A2B0 profiles is not necessarily
33         // a to-XYZ matrix, as to-Lab is supported as well so returning
34         // that could be misleading. Additionally, B-curves are applied
35         // after the matrix is, but a toXYZD50 matrix is the last thing
36         // applied in order to get into the (XYZ) profile connection space.
37         return nullptr;
38     }
39 
onToXYZD50Hash()40     uint32_t onToXYZD50Hash() const override {
41         // See onToXYZD50()'s comment.
42         return 0;
43     }
44 
onFromXYZD50()45     const SkMatrix44* onFromXYZD50() const override {
46         // See onToXYZD50()'s comment. Also, A2B0 profiles are not supported
47         // as destination color spaces, so an inverse matrix is never wanted.
48         return nullptr;
49     }
50 
51     // There is no single gamma curve in an A2B0 profile
onGammaNamed()52     SkGammaNamed onGammaNamed() const override { return kNonStandard_SkGammaNamed; }
onGammaCloseToSRGB()53     bool onGammaCloseToSRGB() const override { return false; }
onGammaIsLinear()54     bool onGammaIsLinear() const override { return false; }
onIsNumericalTransferFn(SkColorSpaceTransferFn * coeffs)55     bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const override { return false; }
56 
onIsCMYK()57     bool onIsCMYK() const override { return SkColorSpace::kCMYK_Type == fICCType; }
58 
onProfileData()59     const SkData* onProfileData() const override { return fProfileData.get(); }
60 
makeLinearGamma()61     sk_sp<SkColorSpace> makeLinearGamma() const override {
62         // TODO: Analyze the extrema of our projection into XYZ and use suitable primaries?
63         // For now, just fall back to a default, because we don't have a good answer.
64         return SkColorSpace::MakeSRGBLinear();
65     }
66 
makeSRGBGamma()67     sk_sp<SkColorSpace> makeSRGBGamma() const override {
68         // See comment in makeLinearGamma
69         return SkColorSpace::MakeSRGB();
70     }
71 
72     class Element {
73     public:
Element(SkGammaNamed gammaNamed,int channelCount)74         Element(SkGammaNamed gammaNamed, int channelCount)
75             : fType(Type::kGammaNamed)
76             , fGammaNamed(gammaNamed)
77             , fMatrix(SkMatrix44::kUninitialized_Constructor)
78             , fInputChannels(channelCount)
79             , fOutputChannels(channelCount) {
80             SkASSERT(gammaNamed != kNonStandard_SkGammaNamed);
81         }
82 
Element(sk_sp<SkGammas> gammas)83         explicit Element(sk_sp<SkGammas> gammas)
84             : fType(Type::kGammas)
85             , fGammas(std::move(gammas))
86             , fMatrix(SkMatrix44::kUninitialized_Constructor)
87             , fInputChannels(fGammas->channels())
88             , fOutputChannels(fGammas->channels()) {
89             for (int i = 0; i < fGammas->channels(); ++i) {
90                 if (SkGammas::Type::kTable_Type == fGammas->type(i)) {
91                     SkASSERT(fGammas->data(i).fTable.fSize >= 2);
92                 }
93             }
94         }
95 
Element(sk_sp<SkColorLookUpTable> colorLUT)96         explicit Element(sk_sp<SkColorLookUpTable> colorLUT)
97             : fType(Type::kCLUT)
98             , fCLUT(std::move(colorLUT))
99             , fMatrix(SkMatrix44::kUninitialized_Constructor)
100             , fInputChannels(fCLUT->inputChannels())
101             , fOutputChannels(fCLUT->outputChannels())
102         {}
103 
Element(const SkMatrix44 & matrix)104         explicit Element(const SkMatrix44& matrix)
105             : fType(Type::kMatrix)
106             , fMatrix(matrix)
107             , fInputChannels(3)
108             , fOutputChannels(3)
109         {}
110 
111         enum class Type {
112             kGammaNamed,
113             kGammas,
114             kCLUT,
115             kMatrix
116         };
117 
type()118         Type type() const { return fType; }
119 
gammaNamed()120         SkGammaNamed gammaNamed() const {
121             SkASSERT(Type::kGammaNamed == fType);
122             return fGammaNamed;
123         }
124 
gammas()125         const SkGammas& gammas() const {
126             SkASSERT(Type::kGammas == fType);
127             return *fGammas;
128         }
129 
colorLUT()130         const SkColorLookUpTable& colorLUT() const {
131             SkASSERT(Type::kCLUT == fType);
132             return *fCLUT;
133         }
134 
matrix()135         const SkMatrix44& matrix() const {
136             SkASSERT(Type::kMatrix == fType);
137             return fMatrix;
138         }
139 
inputChannels()140         int inputChannels() const { return fInputChannels; }
141 
outputChannels()142         int outputChannels() const { return fOutputChannels; }
143 
144     private:
145         Type                      fType;
146         SkGammaNamed              fGammaNamed;
147         sk_sp<SkGammas>           fGammas;
148         sk_sp<SkColorLookUpTable> fCLUT;
149         SkMatrix44                fMatrix;
150         int                       fInputChannels;
151         int                       fOutputChannels;
152     };
element(int i)153     const Element& element(int i) const { return fElements[i]; }
154 
count()155     int count() const { return (int)fElements.size(); }
156 
157     // the intermediate profile connection space that this color space
158     // represents the transformation to
159     enum class PCS : uint8_t {
160         kLAB, // CIELAB
161         kXYZ  // CIEXYZ
162     };
163 
pcs()164     PCS pcs() const { return fPCS; }
165 
iccType()166     SkColorSpace::Type iccType() const { return fICCType; }
167 
168     SkColorSpace_A2B(SkColorSpace::Type iccType, std::vector<Element> elements, PCS pcs,
169                      sk_sp<SkData> profileData);
170 
171 private:
172     sk_sp<SkData>        fProfileData;
173 
174     SkColorSpace::Type   fICCType;
175     std::vector<Element> fElements;
176     PCS                  fPCS;
177 
178     friend class ColorSpaceXformTest;
179 };
180 
181 #endif
182