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_XYZ.h" 9#include "SkColorSpacePriv.h" 10#include "SkColorSpaceXform_Base.h" 11#include "SkOpts.h" 12 13SkColorSpace_XYZ::SkColorSpace_XYZ(SkGammaNamed gammaNamed, const SkMatrix44& toXYZD50) 14 : fProfileData(nullptr) 15 , fGammaNamed(gammaNamed) 16 , fGammas(nullptr) 17 , fToXYZD50(toXYZD50) 18 , fToXYZD50Hash(SkOpts::hash_fn(toXYZD50.values(), 16 * sizeof(SkMScalar), 0)) 19 , fFromXYZD50(SkMatrix44::kUninitialized_Constructor) 20{} 21 22SkColorSpace_XYZ::SkColorSpace_XYZ(SkGammaNamed gammaNamed, sk_sp<SkGammas> gammas, 23 const SkMatrix44& toXYZD50, sk_sp<SkData> profileData) 24 : fProfileData(std::move(profileData)) 25 , fGammaNamed(gammaNamed) 26 , fGammas(std::move(gammas)) 27 , fToXYZD50(toXYZD50) 28 , fToXYZD50Hash(SkOpts::hash_fn(toXYZD50.values(), 16 * sizeof(SkMScalar), 0)) 29 , fFromXYZD50(SkMatrix44::kUninitialized_Constructor) { 30 SkASSERT(!fGammas || 3 == fGammas->channels()); 31 if (fGammas) { 32 for (int i = 0; i < fGammas->channels(); ++i) { 33 if (SkGammas::Type::kTable_Type == fGammas->type(i)) { 34 SkASSERT(fGammas->data(i).fTable.fSize >= 2); 35 } 36 } 37 } 38} 39 40const SkMatrix44* SkColorSpace_XYZ::onFromXYZD50() const { 41 fFromXYZOnce([this] { 42 if (!fToXYZD50.invert(&fFromXYZD50)) { 43 // If a client gives us a dst gamut with a transform that we can't invert, we will 44 // simply give them back a transform to sRGB gamut. 45 SkDEBUGFAIL("Non-invertible XYZ matrix, defaulting to sRGB"); 46 SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor); 47 srgbToxyzD50.set3x3RowMajorf(gSRGB_toXYZD50); 48 srgbToxyzD50.invert(&fFromXYZD50); 49 } 50 }); 51 return &fFromXYZD50; 52} 53 54bool SkColorSpace_XYZ::onGammaCloseToSRGB() const { 55 return kSRGB_SkGammaNamed == fGammaNamed || k2Dot2Curve_SkGammaNamed == fGammaNamed; 56} 57 58bool SkColorSpace_XYZ::onGammaIsLinear() const { 59 return kLinear_SkGammaNamed == fGammaNamed; 60} 61 62bool SkColorSpace_XYZ::onIsNumericalTransferFn(SkColorSpaceTransferFn* coeffs) const { 63 if (named_to_parametric(coeffs, fGammaNamed)) { 64 return true; 65 } 66 67 SkASSERT(fGammas); 68 if (!fGammas->allChannelsSame()) { 69 return false; 70 } 71 72 if (fGammas->isValue(0)) { 73 value_to_parametric(coeffs, fGammas->data(0).fValue); 74 return true; 75 } 76 77 if (fGammas->isParametric(0)) { 78 *coeffs = fGammas->params(0); 79 return true; 80 } 81 82 return false; 83} 84 85sk_sp<SkColorSpace> SkColorSpace_XYZ::makeLinearGamma() const { 86 if (this->gammaIsLinear()) { 87 return sk_ref_sp(const_cast<SkColorSpace_XYZ*>(this)); 88 } 89 return SkColorSpace::MakeRGB(kLinear_SkGammaNamed, fToXYZD50); 90} 91 92sk_sp<SkColorSpace> SkColorSpace_XYZ::makeSRGBGamma() const { 93 if (this->gammaCloseToSRGB()) { 94 return sk_ref_sp(const_cast<SkColorSpace_XYZ*>(this)); 95 } 96 return SkColorSpace::MakeRGB(kSRGB_SkGammaNamed, fToXYZD50); 97} 98 99sk_sp<SkColorSpace> SkColorSpace_XYZ::makeColorSpin() const { 100 SkMatrix44 spin(SkMatrix44::kUninitialized_Constructor); 101 spin.set3x3(0, 1, 0, 0, 0, 1, 1, 0, 0); 102 spin.postConcat(fToXYZD50); 103 return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(fGammaNamed, fGammas, spin, fProfileData)); 104} 105 106void SkColorSpace_XYZ::toDstGammaTables(const uint8_t* tables[3], sk_sp<SkData>* storage, 107 int numTables) const { 108 fToDstGammaOnce([this, numTables] { 109 const bool gammasAreMatching = numTables <= 1; 110 fDstStorage = 111 SkData::MakeUninitialized(numTables * SkColorSpaceXform_Base::kDstGammaTableSize); 112 SkColorSpaceXform_Base::BuildDstGammaTables(fToDstGammaTables, 113 (uint8_t*) fDstStorage->writable_data(), this, 114 gammasAreMatching); 115 }); 116 117 *storage = fDstStorage; 118 tables[0] = fToDstGammaTables[0]; 119 tables[1] = fToDstGammaTables[1]; 120 tables[2] = fToDstGammaTables[2]; 121} 122