Home | History | Annotate | Download | only in core
      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:
     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 
     40     uint32_t onToXYZD50Hash() const override {
     41         // See onToXYZD50()'s comment.
     42         return 0;
     43     }
     44 
     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
     52     SkGammaNamed onGammaNamed() const override { return kNonStandard_SkGammaNamed; }
     53     bool onGammaCloseToSRGB() const override { return false; }
     54     bool onGammaIsLinear() const override { return false; }
     55     bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const override { return false; }
     56 
     57     bool onIsCMYK() const override { return SkColorSpace::kCMYK_Type == fICCType; }
     58 
     59     const SkData* onProfileData() const override { return fProfileData.get(); }
     60 
     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 
     67     sk_sp<SkColorSpace> makeSRGBGamma() const override {
     68         // See comment in makeLinearGamma
     69         return SkColorSpace::MakeSRGB();
     70     }
     71 
     72     class Element {
     73     public:
     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 
     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 
     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 
    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 
    118         Type type() const { return fType; }
    119 
    120         SkGammaNamed gammaNamed() const {
    121             SkASSERT(Type::kGammaNamed == fType);
    122             return fGammaNamed;
    123         }
    124 
    125         const SkGammas& gammas() const {
    126             SkASSERT(Type::kGammas == fType);
    127             return *fGammas;
    128         }
    129 
    130         const SkColorLookUpTable& colorLUT() const {
    131             SkASSERT(Type::kCLUT == fType);
    132             return *fCLUT;
    133         }
    134 
    135         const SkMatrix44& matrix() const {
    136             SkASSERT(Type::kMatrix == fType);
    137             return fMatrix;
    138         }
    139 
    140         int inputChannels() const { return fInputChannels; }
    141 
    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     };
    153     const Element& element(int i) const { return fElements[i]; }
    154 
    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 
    164     PCS pcs() const { return fPCS; }
    165 
    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