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_DEFINED
      9 #define SkColorSpace_DEFINED
     10 
     11 #include "SkMatrix44.h"
     12 #include "SkRefCnt.h"
     13 
     14 class SkData;
     15 
     16 enum SkGammaNamed {
     17     kLinear_SkGammaNamed,
     18     kSRGB_SkGammaNamed,
     19     k2Dot2Curve_SkGammaNamed,
     20     kNonStandard_SkGammaNamed,
     21 };
     22 
     23 /**
     24  *  Describes a color gamut with primaries and a white point.
     25  */
     26 struct SK_API SkColorSpacePrimaries {
     27     float fRX, fRY;
     28     float fGX, fGY;
     29     float fBX, fBY;
     30     float fWX, fWY;
     31 
     32     /**
     33      *  Convert primaries and a white point to a toXYZD50 matrix, the preferred color gamut
     34      *  representation of SkColorSpace.
     35      */
     36     bool toXYZD50(SkMatrix44* toXYZD50) const;
     37 };
     38 
     39 /**
     40  *  Contains the coefficients for a common transfer function equation, specified as
     41  *  a transformation from a curved space to linear.
     42  *
     43  *  LinearVal = C*InputVal + F        , for 0.0f <= InputVal <  D
     44  *  LinearVal = (A*InputVal + B)^G + E, for D    <= InputVal <= 1.0f
     45  *
     46  *  Function is undefined if InputVal is not in [ 0.0f, 1.0f ].
     47  *  Resulting LinearVals must be in [ 0.0f, 1.0f ].
     48  *  Function must be positive and increasing.
     49  */
     50 struct SK_API SkColorSpaceTransferFn {
     51     float fG;
     52     float fA;
     53     float fB;
     54     float fC;
     55     float fD;
     56     float fE;
     57     float fF;
     58 
     59     /**
     60      * Produces a new parametric transfer function equation that is the mathematical inverse of
     61      * this one.
     62      */
     63     SkColorSpaceTransferFn invert() const;
     64 
     65     /**
     66      * Transform a single float by this transfer function.
     67      * For negative inputs, returns sign(x) * f(abs(x)).
     68      */
     69     float operator()(float x) {
     70         SkScalar s = SkScalarSignAsScalar(x);
     71         x = sk_float_abs(x);
     72         if (x >= fD) {
     73             return s * (powf(fA * x + fB, fG) + fE);
     74         } else {
     75             return s * (fC * x + fF);
     76         }
     77     }
     78 };
     79 
     80 class SK_API SkColorSpace : public SkRefCnt {
     81 public:
     82     /**
     83      *  Create the sRGB color space.
     84      */
     85     static sk_sp<SkColorSpace> MakeSRGB();
     86 
     87     /**
     88      *  Colorspace with the sRGB primaries, but a linear (1.0) gamma. Commonly used for
     89      *  half-float surfaces, and high precision individual colors (gradient stops, etc...)
     90      */
     91     static sk_sp<SkColorSpace> MakeSRGBLinear();
     92 
     93     enum RenderTargetGamma : uint8_t {
     94         kLinear_RenderTargetGamma,
     95 
     96         /**
     97          *  Transfer function is the canonical sRGB curve, which has a short linear segment
     98          *  followed by a 2.4f exponential.
     99          */
    100         kSRGB_RenderTargetGamma,
    101     };
    102 
    103     enum Gamut {
    104         kSRGB_Gamut,
    105         kAdobeRGB_Gamut,
    106         kDCIP3_D65_Gamut,
    107         kRec2020_Gamut,
    108     };
    109 
    110     /**
    111      *  Create an SkColorSpace from a transfer function and a color gamut.
    112      *
    113      *  Transfer function can be specified as an enum or as the coefficients to an equation.
    114      *  Gamut can be specified as an enum or as the matrix transformation to XYZ D50.
    115      */
    116     static sk_sp<SkColorSpace> MakeRGB(RenderTargetGamma gamma, Gamut gamut);
    117     static sk_sp<SkColorSpace> MakeRGB(RenderTargetGamma gamma, const SkMatrix44& toXYZD50);
    118     static sk_sp<SkColorSpace> MakeRGB(const SkColorSpaceTransferFn& coeffs, Gamut gamut);
    119     static sk_sp<SkColorSpace> MakeRGB(const SkColorSpaceTransferFn& coeffs,
    120                                        const SkMatrix44& toXYZD50);
    121 
    122     static sk_sp<SkColorSpace> MakeRGB(SkGammaNamed gammaNamed, const SkMatrix44& toXYZD50);
    123 
    124     /**
    125      *  Create an SkColorSpace from an ICC profile.
    126      */
    127     static sk_sp<SkColorSpace> MakeICC(const void*, size_t);
    128 
    129     /**
    130      *  Types of colorspaces.
    131      */
    132     enum Type {
    133         kRGB_Type,
    134         kCMYK_Type,
    135         kGray_Type,
    136     };
    137     Type type() const;
    138 
    139     SkGammaNamed gammaNamed() const;
    140 
    141     /**
    142      *  Returns true if the color space gamma is near enough to be approximated as sRGB.
    143      *  This includes the canonical sRGB transfer function as well as a 2.2f exponential
    144      *  transfer function.
    145      */
    146     bool gammaCloseToSRGB() const;
    147 
    148     /**
    149      *  Returns true if the color space gamma is linear.
    150      */
    151     bool gammaIsLinear() const;
    152 
    153     /**
    154      *  If the transfer function can be represented as coefficients to the standard
    155      *  equation, returns true and sets |fn| to the proper values.
    156      *
    157      *  If not, returns false.
    158      */
    159     bool isNumericalTransferFn(SkColorSpaceTransferFn* fn) const;
    160 
    161     /**
    162      *  Returns true and sets |toXYZD50| if the color gamut can be described as a matrix.
    163      *  Returns false otherwise.
    164      */
    165     bool toXYZD50(SkMatrix44* toXYZD50) const;
    166 
    167     /**
    168      *  Describes color space gamut as a transformation to XYZ D50.
    169      *  Returns nullptr if color gamut cannot be described in terms of XYZ D50.
    170      */
    171     const SkMatrix44* toXYZD50() const;
    172 
    173     /**
    174      *  Describes color space gamut as a transformation from XYZ D50
    175      *  Returns nullptr if color gamut cannot be described in terms of XYZ D50.
    176      */
    177     const SkMatrix44* fromXYZD50() const;
    178 
    179     /**
    180      *  Returns a hash of the gamut transofmration to XYZ D50. Allows for fast equality checking
    181      *  of gamuts, at the (very small) risk of collision.
    182      *  Returns 0 if color gamut cannot be described in terms of XYZ D50.
    183      */
    184     uint32_t toXYZD50Hash() const;
    185 
    186     /**
    187      *  Returns a color space with the same gamut as this one, but with a linear gamma.
    188      *  For color spaces whose gamut can not be described in terms of XYZ D50, returns
    189      *  linear sRGB.
    190      */
    191     virtual sk_sp<SkColorSpace> makeLinearGamma() const = 0;
    192 
    193     /**
    194      *  Returns a color space with the same gamut as this one, with with the sRGB transfer
    195      *  function. For color spaces whose gamut can not be described in terms of XYZ D50, returns
    196      *  sRGB.
    197      */
    198     virtual sk_sp<SkColorSpace> makeSRGBGamma() const = 0;
    199 
    200     /**
    201      *  Returns a color space with the same transfer function as this one, but with the primary
    202      *  colors rotated. For any XYZ space, this produces a new color space that maps RGB to GBR
    203      *  (when applied to a source), and maps RGB to BRG (when applied to a destination). For other
    204      *  types of color spaces, returns nullptr.
    205      *
    206      *  This is used for testing, to construct color spaces that have severe and testable behavior.
    207      */
    208     virtual sk_sp<SkColorSpace> makeColorSpin() const { return nullptr; }
    209 
    210     /**
    211      *  Returns true if the color space is sRGB.
    212      *  Returns false otherwise.
    213      *
    214      *  This allows a little bit of tolerance, given that we might see small numerical error
    215      *  in some cases: converting ICC fixed point to float, converting white point to D50,
    216      *  rounding decisions on transfer function and matrix.
    217      *
    218      *  This does not consider a 2.2f exponential transfer function to be sRGB.  While these
    219      *  functions are similar (and it is sometimes useful to consider them together), this
    220      *  function checks for logical equality.
    221      */
    222     bool isSRGB() const;
    223 
    224     /**
    225      *  Returns nullptr on failure.  Fails when we fallback to serializing ICC data and
    226      *  the data is too large to serialize.
    227      */
    228     sk_sp<SkData> serialize() const;
    229 
    230     /**
    231      *  If |memory| is nullptr, returns the size required to serialize.
    232      *  Otherwise, serializes into |memory| and returns the size.
    233      */
    234     size_t writeToMemory(void* memory) const;
    235 
    236     static sk_sp<SkColorSpace> Deserialize(const void* data, size_t length);
    237 
    238     /**
    239      *  If both are null, we return true.  If one is null and the other is not, we return false.
    240      *  If both are non-null, we do a deeper compare.
    241      */
    242     static bool Equals(const SkColorSpace* src, const SkColorSpace* dst);
    243 
    244 private:
    245     virtual const SkMatrix44* onToXYZD50() const = 0;
    246     virtual uint32_t onToXYZD50Hash() const = 0;
    247     virtual const SkMatrix44* onFromXYZD50() const = 0;
    248 
    249     virtual SkGammaNamed onGammaNamed() const = 0;
    250     virtual bool onGammaCloseToSRGB() const = 0;
    251     virtual bool onGammaIsLinear() const = 0;
    252     virtual bool onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const = 0;
    253     virtual bool onIsCMYK() const { return false; }
    254 
    255     virtual const SkData* onProfileData() const { return nullptr; }
    256 
    257     using INHERITED = SkRefCnt;
    258 };
    259 
    260 enum class SkTransferFunctionBehavior {
    261     /**
    262      *  Converts to a linear space before premultiplying, unpremultiplying, or blending.
    263      */
    264     kRespect,
    265 
    266     /**
    267      *  Premultiplies, unpremultiplies, and blends ignoring the transfer function.  Pixels are
    268      *  treated as if they are linear, regardless of their transfer function encoding.
    269      */
    270     kIgnore,
    271 };
    272 
    273 #endif
    274