aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkColorSpace_A2B.h
blob: 48e8a700e608e4361e53d9dbc4b08be271abd618 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkColorSpace_A2B_DEFINED
#define SkColorSpace_A2B_DEFINED

#include "SkColorLookUpTable.h"
#include "SkColorSpace.h"
#include "SkGammas.h"
#include <vector>

// An alternative SkColorSpace that represents all the color space data that
// is stored in an A2B0 ICC tag. This allows us to use alternative profile
// connection spaces (CIELAB instead of just CIEXYZ), use color-lookup-tables
// to do color space transformations not representable as TRC functions or
// matrix operations, as well as have multiple TRC functions. The CLUT also
// allows conversion between non-3-channel input color spaces ie CMYK(4) to
// a workable PCS (ie XYZ).
//
// AtoBType, lut8Type and lut16Type A2B0 tag types are supported. There are
// also MPET (multi-processing-elements) A2B0 tags in the standard which allow
// you to combine these 3 primitives (TRC, CLUT, matrix) in any order/quantity.
// MPET tags are currently unsupported by the MakeICC parser, could be supported
// here by the nature of the design.
class SkColorSpace_A2B : public SkColorSpace {
public:
    const SkMatrix44* onToXYZD50() const override {
        // the matrix specified in A2B0 profiles is not necessarily
        // a to-XYZ matrix, as to-Lab is supported as well so returning
        // that could be misleading. Additionally, B-curves are applied
        // after the matrix is, but a toXYZD50 matrix is the last thing
        // applied in order to get into the (XYZ) profile connection space.
        return nullptr;
    }

    uint32_t onToXYZD50Hash() const override {
        // See onToXYZD50()'s comment.
        return 0;
    }

    const SkMatrix44* onFromXYZD50() const override {
        // See onToXYZD50()'s comment. Also, A2B0 profiles are not supported
        // as destination color spaces, so an inverse matrix is never wanted.
        return nullptr;
    }

    // There is no single gamma curve in an A2B0 profile
    SkGammaNamed onGammaNamed() const override { return kNonStandard_SkGammaNamed; }
    bool onGammaCloseToSRGB() const override { return false; }
    bool onGammaIsLinear() const override { return false; }
    bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const override { return false; }

    bool onIsCMYK() const override { return SkColorSpace::kCMYK_Type == fICCType; }

    const SkData* onProfileData() const override { return fProfileData.get(); }

    sk_sp<SkColorSpace> makeLinearGamma() const override {
        // TODO: Analyze the extrema of our projection into XYZ and use suitable primaries?
        // For now, just fall back to a default, because we don't have a good answer.
        return SkColorSpace::MakeSRGBLinear();
    }

    sk_sp<SkColorSpace> makeSRGBGamma() const override {
        // See comment in makeLinearGamma
        return SkColorSpace::MakeSRGB();
    }

    class Element {
    public:
        Element(SkGammaNamed gammaNamed, int channelCount)
            : fType(Type::kGammaNamed)
            , fGammaNamed(gammaNamed)
            , fMatrix(SkMatrix44::kUninitialized_Constructor)
            , fInputChannels(channelCount)
            , fOutputChannels(channelCount) {
            SkASSERT(gammaNamed != kNonStandard_SkGammaNamed);
        }

        explicit Element(sk_sp<SkGammas> gammas)
            : fType(Type::kGammas)
            , fGammas(std::move(gammas))
            , fMatrix(SkMatrix44::kUninitialized_Constructor)
            , fInputChannels(fGammas->channels())
            , fOutputChannels(fGammas->channels()) {
            for (int i = 0; i < fGammas->channels(); ++i) {
                if (SkGammas::Type::kTable_Type == fGammas->type(i)) {
                    SkASSERT(fGammas->data(i).fTable.fSize >= 2);
                }
            }
        }

        explicit Element(sk_sp<SkColorLookUpTable> colorLUT)
            : fType(Type::kCLUT)
            , fCLUT(std::move(colorLUT))
            , fMatrix(SkMatrix44::kUninitialized_Constructor)
            , fInputChannels(fCLUT->inputChannels())
            , fOutputChannels(fCLUT->outputChannels())
        {}

        explicit Element(const SkMatrix44& matrix)
            : fType(Type::kMatrix)
            , fMatrix(matrix)
            , fInputChannels(3)
            , fOutputChannels(3)
        {}

        enum class Type {
            kGammaNamed,
            kGammas,
            kCLUT,
            kMatrix
        };

        Type type() const { return fType; }

        SkGammaNamed gammaNamed() const {
            SkASSERT(Type::kGammaNamed == fType);
            return fGammaNamed;
        }

        const SkGammas& gammas() const {
            SkASSERT(Type::kGammas == fType);
            return *fGammas;
        }

        const SkColorLookUpTable& colorLUT() const {
            SkASSERT(Type::kCLUT == fType);
            return *fCLUT;
        }

        const SkMatrix44& matrix() const {
            SkASSERT(Type::kMatrix == fType);
            return fMatrix;
        }

        int inputChannels() const { return fInputChannels; }

        int outputChannels() const { return fOutputChannels; }

    private:
        Type                      fType;
        SkGammaNamed              fGammaNamed;
        sk_sp<SkGammas>           fGammas;
        sk_sp<SkColorLookUpTable> fCLUT;
        SkMatrix44                fMatrix;
        int                       fInputChannels;
        int                       fOutputChannels;
    };
    const Element& element(int i) const { return fElements[i]; }

    int count() const { return (int)fElements.size(); }

    // the intermediate profile connection space that this color space
    // represents the transformation to
    enum class PCS : uint8_t {
        kLAB, // CIELAB
        kXYZ  // CIEXYZ
    };

    PCS pcs() const { return fPCS; }

    SkColorSpace::Type iccType() const { return fICCType; }

    SkColorSpace_A2B(SkColorSpace::Type iccType, std::vector<Element> elements, PCS pcs,
                     sk_sp<SkData> profileData);

private:
    sk_sp<SkData>        fProfileData;

    SkColorSpace::Type   fICCType;
    std::vector<Element> fElements;
    PCS                  fPCS;

    friend class ColorSpaceXformTest;
};

#endif