LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkColorSpace.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 14 298 4.7 %
Date: 2017-07-14 16:53:18 Functions: 4 25 16.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "SkColorSpace.h"
       9             : #include "SkColorSpace_Base.h"
      10             : #include "SkColorSpace_XYZ.h"
      11             : #include "SkColorSpacePriv.h"
      12             : #include "SkOnce.h"
      13             : #include "SkPoint3.h"
      14             : 
      15           0 : bool SkColorSpacePrimaries::toXYZD50(SkMatrix44* toXYZ_D50) const {
      16           0 :     if (!is_zero_to_one(fRX) || !is_zero_to_one(fRY) ||
      17           0 :         !is_zero_to_one(fGX) || !is_zero_to_one(fGY) ||
      18           0 :         !is_zero_to_one(fBX) || !is_zero_to_one(fBY) ||
      19           0 :         !is_zero_to_one(fWX) || !is_zero_to_one(fWY))
      20             :     {
      21           0 :         return false;
      22             :     }
      23             : 
      24             :     // First, we need to convert xy values (primaries) to XYZ.
      25             :     SkMatrix primaries;
      26           0 :     primaries.setAll(             fRX,              fGX,              fBX,
      27           0 :                                   fRY,              fGY,              fBY,
      28           0 :                      1.0f - fRX - fRY, 1.0f - fGX - fGY, 1.0f - fBX - fBY);
      29             :     SkMatrix primariesInv;
      30           0 :     if (!primaries.invert(&primariesInv)) {
      31           0 :         return false;
      32             :     }
      33             : 
      34             :     // Assumes that Y is 1.0f.
      35           0 :     SkVector3 wXYZ = SkVector3::Make(fWX / fWY, 1.0f, (1.0f - fWX - fWY) / fWY);
      36             :     SkVector3 XYZ;
      37           0 :     XYZ.fX = primariesInv[0] * wXYZ.fX + primariesInv[1] * wXYZ.fY + primariesInv[2] * wXYZ.fZ;
      38           0 :     XYZ.fY = primariesInv[3] * wXYZ.fX + primariesInv[4] * wXYZ.fY + primariesInv[5] * wXYZ.fZ;
      39           0 :     XYZ.fZ = primariesInv[6] * wXYZ.fX + primariesInv[7] * wXYZ.fY + primariesInv[8] * wXYZ.fZ;
      40             :     SkMatrix toXYZ;
      41           0 :     toXYZ.setAll(XYZ.fX,   0.0f,   0.0f,
      42             :                    0.0f, XYZ.fY,   0.0f,
      43           0 :                    0.0f,   0.0f, XYZ.fZ);
      44           0 :     toXYZ.postConcat(primaries);
      45             : 
      46             :     // Now convert toXYZ matrix to toXYZD50.
      47           0 :     SkVector3 wXYZD50 = SkVector3::Make(0.96422f, 1.0f, 0.82521f);
      48             : 
      49             :     // Calculate the chromatic adaptation matrix.  We will use the Bradford method, thus
      50             :     // the matrices below.  The Bradford method is used by Adobe and is widely considered
      51             :     // to be the best.
      52             :     SkMatrix mA, mAInv;
      53             :     mA.setAll(+0.8951f, +0.2664f, -0.1614f,
      54             :               -0.7502f, +1.7135f, +0.0367f,
      55           0 :               +0.0389f, -0.0685f, +1.0296f);
      56             :     mAInv.setAll(+0.9869929f, -0.1470543f, +0.1599627f,
      57             :                  +0.4323053f, +0.5183603f, +0.0492912f,
      58           0 :                  -0.0085287f, +0.0400428f, +0.9684867f);
      59             : 
      60             :     SkVector3 srcCone;
      61           0 :     srcCone.fX = mA[0] * wXYZ.fX + mA[1] * wXYZ.fY + mA[2] * wXYZ.fZ;
      62           0 :     srcCone.fY = mA[3] * wXYZ.fX + mA[4] * wXYZ.fY + mA[5] * wXYZ.fZ;
      63           0 :     srcCone.fZ = mA[6] * wXYZ.fX + mA[7] * wXYZ.fY + mA[8] * wXYZ.fZ;
      64             :     SkVector3 dstCone;
      65           0 :     dstCone.fX = mA[0] * wXYZD50.fX + mA[1] * wXYZD50.fY + mA[2] * wXYZD50.fZ;
      66           0 :     dstCone.fY = mA[3] * wXYZD50.fX + mA[4] * wXYZD50.fY + mA[5] * wXYZD50.fZ;
      67           0 :     dstCone.fZ = mA[6] * wXYZD50.fX + mA[7] * wXYZD50.fY + mA[8] * wXYZD50.fZ;
      68             : 
      69             :     SkMatrix DXToD50;
      70           0 :     DXToD50.setIdentity();
      71           0 :     DXToD50[0] = dstCone.fX / srcCone.fX;
      72           0 :     DXToD50[4] = dstCone.fY / srcCone.fY;
      73           0 :     DXToD50[8] = dstCone.fZ / srcCone.fZ;
      74           0 :     DXToD50.postConcat(mAInv);
      75           0 :     DXToD50.preConcat(mA);
      76             : 
      77           0 :     toXYZ.postConcat(DXToD50);
      78           0 :     toXYZ_D50->set3x3(toXYZ[0], toXYZ[3], toXYZ[6],
      79           0 :                       toXYZ[1], toXYZ[4], toXYZ[7],
      80           0 :                       toXYZ[2], toXYZ[5], toXYZ[8]);
      81           0 :     return true;
      82             : }
      83             : 
      84             : ///////////////////////////////////////////////////////////////////////////////////////////////////
      85             : 
      86           1 : SkColorSpace_Base::SkColorSpace_Base(sk_sp<SkData> profileData)
      87           1 :     : fProfileData(std::move(profileData))
      88           1 : {}
      89             : 
      90             : /**
      91             :  *  Checks if our toXYZ matrix is a close match to a known color gamut.
      92             :  *
      93             :  *  @param toXYZD50 transformation matrix deduced from profile data
      94             :  *  @param standard 3x3 canonical transformation matrix
      95             :  */
      96           0 : static bool xyz_almost_equal(const SkMatrix44& toXYZD50, const float* standard) {
      97           0 :     return color_space_almost_equal(toXYZD50.getFloat(0, 0), standard[0]) &&
      98           0 :            color_space_almost_equal(toXYZD50.getFloat(0, 1), standard[1]) &&
      99           0 :            color_space_almost_equal(toXYZD50.getFloat(0, 2), standard[2]) &&
     100           0 :            color_space_almost_equal(toXYZD50.getFloat(1, 0), standard[3]) &&
     101           0 :            color_space_almost_equal(toXYZD50.getFloat(1, 1), standard[4]) &&
     102           0 :            color_space_almost_equal(toXYZD50.getFloat(1, 2), standard[5]) &&
     103           0 :            color_space_almost_equal(toXYZD50.getFloat(2, 0), standard[6]) &&
     104           0 :            color_space_almost_equal(toXYZD50.getFloat(2, 1), standard[7]) &&
     105           0 :            color_space_almost_equal(toXYZD50.getFloat(2, 2), standard[8]) &&
     106           0 :            color_space_almost_equal(toXYZD50.getFloat(0, 3), 0.0f) &&
     107           0 :            color_space_almost_equal(toXYZD50.getFloat(1, 3), 0.0f) &&
     108           0 :            color_space_almost_equal(toXYZD50.getFloat(2, 3), 0.0f) &&
     109           0 :            color_space_almost_equal(toXYZD50.getFloat(3, 0), 0.0f) &&
     110           0 :            color_space_almost_equal(toXYZD50.getFloat(3, 1), 0.0f) &&
     111           0 :            color_space_almost_equal(toXYZD50.getFloat(3, 2), 0.0f) &&
     112           0 :            color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f);
     113             : }
     114             : 
     115           0 : sk_sp<SkColorSpace> SkColorSpace_Base::MakeRGB(SkGammaNamed gammaNamed, const SkMatrix44& toXYZD50)
     116             : {
     117           0 :     switch (gammaNamed) {
     118             :         case kSRGB_SkGammaNamed:
     119           0 :             if (xyz_almost_equal(toXYZD50, gSRGB_toXYZD50)) {
     120           0 :                 return SkColorSpace_Base::MakeNamed(kSRGB_Named);
     121             :             }
     122           0 :             break;
     123             :         case k2Dot2Curve_SkGammaNamed:
     124           0 :             if (xyz_almost_equal(toXYZD50, gAdobeRGB_toXYZD50)) {
     125           0 :                 return SkColorSpace_Base::MakeNamed(kAdobeRGB_Named);
     126             :             }
     127           0 :             break;
     128             :         case kLinear_SkGammaNamed:
     129           0 :             if (xyz_almost_equal(toXYZD50, gSRGB_toXYZD50)) {
     130           0 :                 return SkColorSpace_Base::MakeNamed(kSRGBLinear_Named);
     131             :             }
     132           0 :             break;
     133             :         case kNonStandard_SkGammaNamed:
     134             :             // This is not allowed.
     135           0 :             return nullptr;
     136             :         default:
     137           0 :             break;
     138             :     }
     139             : 
     140           0 :     return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed, toXYZD50));
     141             : }
     142             : 
     143           0 : sk_sp<SkColorSpace> SkColorSpace::MakeRGB(RenderTargetGamma gamma, const SkMatrix44& toXYZD50) {
     144           0 :     switch (gamma) {
     145             :         case kLinear_RenderTargetGamma:
     146           0 :             return SkColorSpace_Base::MakeRGB(kLinear_SkGammaNamed, toXYZD50);
     147             :         case kSRGB_RenderTargetGamma:
     148           0 :             return SkColorSpace_Base::MakeRGB(kSRGB_SkGammaNamed, toXYZD50);
     149             :         default:
     150           0 :             return nullptr;
     151             :     }
     152             : }
     153             : 
     154           0 : sk_sp<SkColorSpace> SkColorSpace::MakeRGB(const SkColorSpaceTransferFn& coeffs,
     155             :                                           const SkMatrix44& toXYZD50) {
     156           0 :     if (!is_valid_transfer_fn(coeffs)) {
     157           0 :         return nullptr;
     158             :     }
     159             : 
     160           0 :     if (is_almost_srgb(coeffs)) {
     161           0 :         return SkColorSpace::MakeRGB(kSRGB_RenderTargetGamma, toXYZD50);
     162             :     }
     163             : 
     164           0 :     if (is_almost_2dot2(coeffs)) {
     165           0 :         return SkColorSpace_Base::MakeRGB(k2Dot2Curve_SkGammaNamed, toXYZD50);
     166             :     }
     167             : 
     168           0 :     if (is_almost_linear(coeffs)) {
     169           0 :         return SkColorSpace_Base::MakeRGB(kLinear_SkGammaNamed, toXYZD50);
     170             :     }
     171             : 
     172           0 :     void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn));
     173           0 :     sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
     174           0 :     SkColorSpaceTransferFn* fn = SkTAddOffset<SkColorSpaceTransferFn>(memory, sizeof(SkGammas));
     175           0 :     *fn = coeffs;
     176           0 :     SkGammas::Data data;
     177           0 :     data.fParamOffset = 0;
     178           0 :     for (int channel = 0; channel < 3; ++channel) {
     179           0 :         gammas->fType[channel] = SkGammas::Type::kParam_Type;
     180           0 :         gammas->fData[channel] = data;
     181             :     }
     182             :     return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed,
     183           0 :                                                     std::move(gammas), toXYZD50, nullptr));
     184             : }
     185             : 
     186           0 : sk_sp<SkColorSpace> SkColorSpace::MakeRGB(RenderTargetGamma gamma, Gamut gamut) {
     187           0 :     SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
     188           0 :     to_xyz_d50(&toXYZD50, gamut);
     189           0 :     return SkColorSpace::MakeRGB(gamma, toXYZD50);
     190             : }
     191             : 
     192           0 : sk_sp<SkColorSpace> SkColorSpace::MakeRGB(const SkColorSpaceTransferFn& coeffs, Gamut gamut) {
     193           0 :     SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
     194           0 :     to_xyz_d50(&toXYZD50, gamut);
     195           0 :     return SkColorSpace::MakeRGB(coeffs, toXYZD50);
     196             : }
     197             : 
     198             : static SkColorSpace* gAdobeRGB;
     199             : static SkColorSpace* gSRGB;
     200             : static SkColorSpace* gSRGBLinear;
     201             : 
     202          25 : sk_sp<SkColorSpace> SkColorSpace_Base::MakeNamed(Named named) {
     203             :     static SkOnce sRGBOnce;
     204             :     static SkOnce adobeRGBOnce;
     205             :     static SkOnce sRGBLinearOnce;
     206             : 
     207          25 :     switch (named) {
     208             :         case kSRGB_Named: {
     209           0 :             sRGBOnce([] {
     210           0 :                 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
     211           0 :                 srgbToxyzD50.set3x3RowMajorf(gSRGB_toXYZD50);
     212             : 
     213             :                 // Force the mutable type mask to be computed.  This avoids races.
     214           0 :                 (void)srgbToxyzD50.getType();
     215           0 :                 gSRGB = new SkColorSpace_XYZ(kSRGB_SkGammaNamed, srgbToxyzD50);
     216           0 :             });
     217           0 :             return sk_ref_sp<SkColorSpace>(gSRGB);
     218             :         }
     219             :         case kAdobeRGB_Named: {
     220           0 :             adobeRGBOnce([] {
     221           0 :                 SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Constructor);
     222           0 :                 adobergbToxyzD50.set3x3RowMajorf(gAdobeRGB_toXYZD50);
     223             : 
     224             :                 // Force the mutable type mask to be computed.  This avoids races.
     225           0 :                 (void)adobergbToxyzD50.getType();
     226           0 :                 gAdobeRGB = new SkColorSpace_XYZ(k2Dot2Curve_SkGammaNamed, adobergbToxyzD50);
     227           0 :             });
     228           0 :             return sk_ref_sp<SkColorSpace>(gAdobeRGB);
     229             :         }
     230             :         case kSRGBLinear_Named: {
     231          26 :             sRGBLinearOnce([] {
     232           1 :                 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
     233           1 :                 srgbToxyzD50.set3x3RowMajorf(gSRGB_toXYZD50);
     234             : 
     235             :                 // Force the mutable type mask to be computed.  This avoids races.
     236           1 :                 (void)srgbToxyzD50.getType();
     237           1 :                 gSRGBLinear = new SkColorSpace_XYZ(kLinear_SkGammaNamed, srgbToxyzD50);
     238          26 :             });
     239          25 :             return sk_ref_sp<SkColorSpace>(gSRGBLinear);
     240             :         }
     241             :         default:
     242           0 :             break;
     243             :     }
     244           0 :     return nullptr;
     245             : }
     246             : 
     247           0 : sk_sp<SkColorSpace> SkColorSpace::MakeSRGB() {
     248           0 :     return SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kSRGB_Named);
     249             : }
     250             : 
     251          25 : sk_sp<SkColorSpace> SkColorSpace::MakeSRGBLinear() {
     252          25 :     return SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kSRGBLinear_Named);
     253             : }
     254             : 
     255             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     256             : 
     257           0 : bool SkColorSpace::gammaCloseToSRGB() const {
     258           0 :     return as_CSB(this)->onGammaCloseToSRGB();
     259             : }
     260             : 
     261           0 : bool SkColorSpace::gammaIsLinear() const {
     262           0 :     return as_CSB(this)->onGammaIsLinear();
     263             : }
     264             : 
     265           0 : bool SkColorSpace::isNumericalTransferFn(SkColorSpaceTransferFn* fn) const {
     266           0 :     return as_CSB(this)->onIsNumericalTransferFn(fn);
     267             : }
     268             : 
     269           0 : bool SkColorSpace::toXYZD50(SkMatrix44* toXYZD50) const {
     270           0 :     const SkMatrix44* matrix = as_CSB(this)->toXYZD50();
     271           0 :     if (matrix) {
     272           0 :         *toXYZD50 = *matrix;
     273           0 :         return true;
     274             :     }
     275             : 
     276           0 :     return false;
     277             : }
     278             : 
     279           0 : bool SkColorSpace::isSRGB() const {
     280           0 :     return gSRGB == this;
     281             : }
     282             : 
     283             : ///////////////////////////////////////////////////////////////////////////////////////////////////
     284             : 
     285             : enum Version {
     286             :     k0_Version, // Initial version, header + flags for matrix and profile
     287             : };
     288             : 
     289             : struct ColorSpaceHeader {
     290             :     /**
     291             :      *  It is only valid to set zero or one flags.
     292             :      *  Setting multiple flags is invalid.
     293             :      */
     294             : 
     295             :     /**
     296             :      *  If kMatrix_Flag is set, we will write 12 floats after the header.
     297             :      */
     298             :     static constexpr uint8_t kMatrix_Flag     = 1 << 0;
     299             : 
     300             :     /**
     301             :      *  If kICC_Flag is set, we will write an ICC profile after the header.
     302             :      *  The ICC profile will be written as a uint32 size, followed immediately
     303             :      *  by the data (padded to 4 bytes).
     304             :      */
     305             :     static constexpr uint8_t kICC_Flag        = 1 << 1;
     306             : 
     307             :     /**
     308             :      *  If kTransferFn_Flag is set, we will write 19 floats after the header.
     309             :      *  The first seven represent the transfer fn, and the next twelve are the
     310             :      *  matrix.
     311             :      */
     312             :     static constexpr uint8_t kTransferFn_Flag = 1 << 3;
     313             : 
     314           0 :     static ColorSpaceHeader Pack(Version version, uint8_t named, uint8_t gammaNamed, uint8_t flags)
     315             :     {
     316             :         ColorSpaceHeader header;
     317             : 
     318           0 :         SkASSERT(k0_Version == version);
     319           0 :         header.fVersion = (uint8_t) version;
     320             : 
     321           0 :         SkASSERT(named <= SkColorSpace_Base::kSRGBLinear_Named);
     322           0 :         header.fNamed = (uint8_t) named;
     323             : 
     324           0 :         SkASSERT(gammaNamed <= kNonStandard_SkGammaNamed);
     325           0 :         header.fGammaNamed = (uint8_t) gammaNamed;
     326             : 
     327           0 :         SkASSERT(flags <= kTransferFn_Flag);
     328           0 :         header.fFlags = flags;
     329           0 :         return header;
     330             :     }
     331             : 
     332             :     uint8_t fVersion;            // Always zero
     333             :     uint8_t fNamed;              // Must be a SkColorSpace::Named
     334             :     uint8_t fGammaNamed;         // Must be a SkGammaNamed
     335             :     uint8_t fFlags;
     336             : };
     337             : 
     338           0 : size_t SkColorSpace::writeToMemory(void* memory) const {
     339             :     // Start by trying the serialization fast path.  If we haven't saved ICC profile data,
     340             :     // we must have a profile that we can serialize easily.
     341           0 :     if (!as_CSB(this)->fProfileData) {
     342             :         // Profile data is mandatory for A2B0 color spaces.
     343           0 :         SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(this)->type());
     344           0 :         const SkColorSpace_XYZ* thisXYZ = static_cast<const SkColorSpace_XYZ*>(this);
     345             :         // If we have a named profile, only write the enum.
     346           0 :         const SkGammaNamed gammaNamed = thisXYZ->gammaNamed();
     347           0 :         if (this == gSRGB) {
     348           0 :             if (memory) {
     349             :                 *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
     350           0 :                         k0_Version, SkColorSpace_Base::kSRGB_Named, gammaNamed, 0);
     351             :             }
     352           0 :             return sizeof(ColorSpaceHeader);
     353           0 :         } else if (this == gAdobeRGB) {
     354           0 :             if (memory) {
     355             :                 *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
     356           0 :                         k0_Version, SkColorSpace_Base::kAdobeRGB_Named, gammaNamed, 0);
     357             :             }
     358           0 :             return sizeof(ColorSpaceHeader);
     359           0 :         } else if (this == gSRGBLinear) {
     360           0 :             if (memory) {
     361             :                 *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(
     362           0 :                         k0_Version, SkColorSpace_Base::kSRGBLinear_Named, gammaNamed, 0);
     363             :             }
     364           0 :             return sizeof(ColorSpaceHeader);
     365             :         }
     366             : 
     367             :         // If we have a named gamma, write the enum and the matrix.
     368           0 :         switch (gammaNamed) {
     369             :             case kSRGB_SkGammaNamed:
     370             :             case k2Dot2Curve_SkGammaNamed:
     371             :             case kLinear_SkGammaNamed: {
     372           0 :                 if (memory) {
     373             :                     *((ColorSpaceHeader*) memory) =
     374             :                             ColorSpaceHeader::Pack(k0_Version, 0, gammaNamed,
     375           0 :                                                    ColorSpaceHeader::kMatrix_Flag);
     376           0 :                     memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
     377           0 :                     thisXYZ->toXYZD50()->as3x4RowMajorf((float*) memory);
     378             :                 }
     379           0 :                 return sizeof(ColorSpaceHeader) + 12 * sizeof(float);
     380             :             }
     381             :             default: {
     382           0 :                 const SkGammas* gammas = thisXYZ->gammas();
     383           0 :                 SkASSERT(gammas);
     384           0 :                 SkASSERT(gammas->isParametric(0));
     385           0 :                 SkASSERT(gammas->isParametric(1));
     386           0 :                 SkASSERT(gammas->isParametric(2));
     387           0 :                 SkASSERT(gammas->data(0) == gammas->data(1));
     388           0 :                 SkASSERT(gammas->data(0) == gammas->data(2));
     389             : 
     390           0 :                 if (memory) {
     391             :                     *((ColorSpaceHeader*) memory) =
     392           0 :                             ColorSpaceHeader::Pack(k0_Version, 0, thisXYZ->fGammaNamed,
     393           0 :                                                    ColorSpaceHeader::kTransferFn_Flag);
     394           0 :                     memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
     395             : 
     396           0 :                     *(((float*) memory) + 0) = gammas->params(0).fA;
     397           0 :                     *(((float*) memory) + 1) = gammas->params(0).fB;
     398           0 :                     *(((float*) memory) + 2) = gammas->params(0).fC;
     399           0 :                     *(((float*) memory) + 3) = gammas->params(0).fD;
     400           0 :                     *(((float*) memory) + 4) = gammas->params(0).fE;
     401           0 :                     *(((float*) memory) + 5) = gammas->params(0).fF;
     402           0 :                     *(((float*) memory) + 6) = gammas->params(0).fG;
     403           0 :                     memory = SkTAddOffset<void>(memory, 7 * sizeof(float));
     404             : 
     405           0 :                     thisXYZ->fToXYZD50.as3x4RowMajorf((float*) memory);
     406             :                 }
     407             : 
     408           0 :                 return sizeof(ColorSpaceHeader) + 19 * sizeof(float);
     409             :             }
     410             :         }
     411             :     }
     412             : 
     413             :     // Otherwise, serialize the ICC data.
     414           0 :     size_t profileSize = as_CSB(this)->fProfileData->size();
     415           0 :     if (SkAlign4(profileSize) != (uint32_t) SkAlign4(profileSize)) {
     416           0 :         return 0;
     417             :     }
     418             : 
     419           0 :     if (memory) {
     420             :         *((ColorSpaceHeader*) memory) = ColorSpaceHeader::Pack(k0_Version, 0,
     421             :                                                                kNonStandard_SkGammaNamed,
     422           0 :                                                                ColorSpaceHeader::kICC_Flag);
     423           0 :         memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
     424             : 
     425           0 :         *((uint32_t*) memory) = (uint32_t) SkAlign4(profileSize);
     426           0 :         memory = SkTAddOffset<void>(memory, sizeof(uint32_t));
     427             : 
     428           0 :         memcpy(memory, as_CSB(this)->fProfileData->data(), profileSize);
     429           0 :         memset(SkTAddOffset<void>(memory, profileSize), 0, SkAlign4(profileSize) - profileSize);
     430             :     }
     431           0 :     return sizeof(ColorSpaceHeader) + sizeof(uint32_t) + SkAlign4(profileSize);
     432             : }
     433             : 
     434           0 : sk_sp<SkData> SkColorSpace::serialize() const {
     435           0 :     size_t size = this->writeToMemory(nullptr);
     436           0 :     if (0 == size) {
     437           0 :         return nullptr;
     438             :     }
     439             : 
     440           0 :     sk_sp<SkData> data = SkData::MakeUninitialized(size);
     441           0 :     this->writeToMemory(data->writable_data());
     442           0 :     return data;
     443             : }
     444             : 
     445           0 : sk_sp<SkColorSpace> SkColorSpace::Deserialize(const void* data, size_t length) {
     446           0 :     if (length < sizeof(ColorSpaceHeader)) {
     447           0 :         return nullptr;
     448             :     }
     449             : 
     450           0 :     ColorSpaceHeader header = *((const ColorSpaceHeader*) data);
     451           0 :     data = SkTAddOffset<const void>(data, sizeof(ColorSpaceHeader));
     452           0 :     length -= sizeof(ColorSpaceHeader);
     453           0 :     if (0 == header.fFlags) {
     454           0 :         return SkColorSpace_Base::MakeNamed((SkColorSpace_Base::Named) header.fNamed);
     455             :     }
     456             : 
     457           0 :     switch ((SkGammaNamed) header.fGammaNamed) {
     458             :         case kSRGB_SkGammaNamed:
     459             :         case k2Dot2Curve_SkGammaNamed:
     460             :         case kLinear_SkGammaNamed: {
     461           0 :             if (ColorSpaceHeader::kMatrix_Flag != header.fFlags || length < 12 * sizeof(float)) {
     462           0 :                 return nullptr;
     463             :             }
     464             : 
     465           0 :             SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
     466           0 :             toXYZ.set3x4RowMajorf((const float*) data);
     467           0 :             return SkColorSpace_Base::MakeRGB((SkGammaNamed) header.fGammaNamed, toXYZ);
     468             :         }
     469             :         default:
     470           0 :             break;
     471             :     }
     472             : 
     473           0 :     switch (header.fFlags) {
     474             :         case ColorSpaceHeader::kICC_Flag: {
     475           0 :             if (length < sizeof(uint32_t)) {
     476           0 :                 return nullptr;
     477             :             }
     478             : 
     479           0 :             uint32_t profileSize = *((uint32_t*) data);
     480           0 :             data = SkTAddOffset<const void>(data, sizeof(uint32_t));
     481           0 :             length -= sizeof(uint32_t);
     482           0 :             if (length < profileSize) {
     483           0 :                 return nullptr;
     484             :             }
     485             : 
     486           0 :             return MakeICC(data, profileSize);
     487             :         }
     488             :         case ColorSpaceHeader::kTransferFn_Flag: {
     489           0 :             if (length < 19 * sizeof(float)) {
     490           0 :                 return nullptr;
     491             :             }
     492             : 
     493             :             SkColorSpaceTransferFn transferFn;
     494           0 :             transferFn.fA = *(((const float*) data) + 0);
     495           0 :             transferFn.fB = *(((const float*) data) + 1);
     496           0 :             transferFn.fC = *(((const float*) data) + 2);
     497           0 :             transferFn.fD = *(((const float*) data) + 3);
     498           0 :             transferFn.fE = *(((const float*) data) + 4);
     499           0 :             transferFn.fF = *(((const float*) data) + 5);
     500           0 :             transferFn.fG = *(((const float*) data) + 6);
     501           0 :             data = SkTAddOffset<const void>(data, 7 * sizeof(float));
     502             : 
     503           0 :             SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
     504           0 :             toXYZ.set3x4RowMajorf((const float*) data);
     505           0 :             return SkColorSpace::MakeRGB(transferFn, toXYZ);
     506             :         }
     507             :         default:
     508           0 :             return nullptr;
     509             :     }
     510             : }
     511             : 
     512           0 : bool SkColorSpace::Equals(const SkColorSpace* src, const SkColorSpace* dst) {
     513           0 :     if (src == dst) {
     514           0 :         return true;
     515             :     }
     516             : 
     517           0 :     if (!src || !dst) {
     518           0 :         return false;
     519             :     }
     520             : 
     521           0 :     SkData* srcData = as_CSB(src)->fProfileData.get();
     522           0 :     SkData* dstData = as_CSB(dst)->fProfileData.get();
     523           0 :     if (srcData || dstData) {
     524           0 :         if (srcData && dstData) {
     525           0 :             return srcData->size() == dstData->size() &&
     526           0 :                    0 == memcmp(srcData->data(), dstData->data(), srcData->size());
     527             :         }
     528             : 
     529           0 :         return false;
     530             :     }
     531             : 
     532             :     // profiles are mandatory for A2B0 color spaces
     533           0 :     SkASSERT(as_CSB(src)->type() == SkColorSpace_Base::Type::kXYZ);
     534           0 :     const SkColorSpace_XYZ* srcXYZ = static_cast<const SkColorSpace_XYZ*>(src);
     535           0 :     const SkColorSpace_XYZ* dstXYZ = static_cast<const SkColorSpace_XYZ*>(dst);
     536             : 
     537           0 :     if (srcXYZ->gammaNamed() != dstXYZ->gammaNamed()) {
     538           0 :         return false;
     539             :     }
     540             : 
     541           0 :     switch (srcXYZ->gammaNamed()) {
     542             :         case kSRGB_SkGammaNamed:
     543             :         case k2Dot2Curve_SkGammaNamed:
     544             :         case kLinear_SkGammaNamed:
     545           0 :             if (srcXYZ->toXYZD50Hash() == dstXYZ->toXYZD50Hash()) {
     546           0 :                 SkASSERT(*srcXYZ->toXYZD50() == *dstXYZ->toXYZD50() && "Hash collision");
     547           0 :                 return true;
     548             :             }
     549           0 :             return false;
     550             :         default:
     551             :             // It is unlikely that we will reach this case.
     552           0 :             sk_sp<SkData> serializedSrcData = src->serialize();
     553           0 :             sk_sp<SkData> serializedDstData = dst->serialize();
     554           0 :             return serializedSrcData->size() == serializedDstData->size() &&
     555           0 :                    0 == memcmp(serializedSrcData->data(), serializedDstData->data(),
     556           0 :                                serializedSrcData->size());
     557             :     }
     558             : }
     559             : 
     560           0 : SkColorSpaceTransferFn SkColorSpaceTransferFn::invert() const {
     561             :     // Original equation is:       y = (ax + b)^g + e   for x >= d
     562             :     //                             y = cx + f           otherwise
     563             :     //
     564             :     // so 1st inverse is:          (y - e)^(1/g) = ax + b
     565             :     //                             x = ((y - e)^(1/g) - b) / a
     566             :     //
     567             :     // which can be re-written as: x = (1/a)(y - e)^(1/g) - b/a
     568             :     //                             x = ((1/a)^g)^(1/g) * (y - e)^(1/g) - b/a
     569             :     //                             x = ([(1/a)^g]y + [-((1/a)^g)e]) ^ [1/g] + [-b/a]
     570             :     //
     571             :     // and 2nd inverse is:         x = (y - f) / c
     572             :     // which can be re-written as: x = [1/c]y + [-f/c]
     573             :     //
     574             :     // and now both can be expressed in terms of the same parametric form as the
     575             :     // original - parameters are enclosed in square brackets.
     576           0 :     SkColorSpaceTransferFn inv = { 0, 0, 0, 0, 0, 0, 0 };
     577             : 
     578             :     // find inverse for linear segment (if possible)
     579           0 :     if (!transfer_fn_almost_equal(0.f, fC)) {
     580           0 :         inv.fC = 1.f / fC;
     581           0 :         inv.fF = -fF / fC;
     582             :     } else {
     583             :         // otherwise assume it should be 0 as it is the lower segment
     584             :         // as y = f is a constant function
     585             :     }
     586             : 
     587             :     // find inverse for the other segment (if possible)
     588           0 :     if (transfer_fn_almost_equal(0.f, fA) || transfer_fn_almost_equal(0.f, fG)) {
     589             :         // otherwise assume it should be 1 as it is the top segment
     590             :         // as you can't invert the constant functions y = b^g + c, or y = 1 + c
     591           0 :         inv.fG = 1.f;
     592           0 :         inv.fE = 1.f;
     593             :     } else {
     594           0 :         inv.fG = 1.f / fG;
     595           0 :         inv.fA = powf(1.f / fA, fG);
     596           0 :         inv.fB = -inv.fA * fE;
     597           0 :         inv.fE = -fB / fA;
     598             :     }
     599           0 :     inv.fD = fC * fD + fF;
     600             : 
     601           0 :     return inv;
     602             : }

Generated by: LCOV version 1.13