12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ui/gfx/color_space.h"
- #include <iomanip>
- #include <limits>
- #include <map>
- #include <sstream>
- #include "base/atomic_sequence_num.h"
- #include "base/lazy_instance.h"
- #include "base/logging.h"
- #include "base/notreached.h"
- #include "base/synchronization/lock.h"
- #include "third_party/skia/include/core/SkColorSpace.h"
- #include "third_party/skia/include/core/SkData.h"
- #include "third_party/skia/include/core/SkICC.h"
- #include "third_party/skia/include/core/SkImageInfo.h"
- #include "third_party/skia/include/core/SkM44.h"
- #include "ui/gfx/display_color_spaces.h"
- #include "ui/gfx/icc_profile.h"
- #include "ui/gfx/skia_color_space_util.h"
- namespace gfx {
- namespace {
- static bool IsAlmostZero(float value) {
- return std::abs(value) < std::numeric_limits<float>::epsilon();
- }
- static bool FloatsEqualWithinTolerance(const float* a,
- const float* b,
- int n,
- float tol) {
- for (int i = 0; i < n; ++i) {
- if (std::abs(a[i] - b[i]) > tol) {
- return false;
- }
- }
- return true;
- }
- skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
- // Note that SkColorSpace doesn't have the notion of an unspecified SDR white
- // level.
- if (sdr_white_level == 0.f)
- sdr_white_level = ColorSpace::kDefaultSDRWhiteLevel;
- // The generic PQ transfer function produces normalized luminance values i.e.
- // the range 0-1 represents 0-10000 nits for the reference display, but we
- // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly.
- const double w = 10000. / sdr_white_level;
- // Distribute scaling factor W by scaling A and B with X ^ (1/F):
- // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F
- // See https://crbug.com/1058580#c32 for discussion.
- skcms_TransferFunction fn = SkNamedTransferFn::kPQ;
- const double ws = pow(w, 1. / fn.f);
- fn.a = ws * fn.a;
- fn.b = ws * fn.b;
- return fn;
- }
- skcms_TransferFunction GetHLGSkTransferFunction(float sdr_white_level) {
- // Note that SkColorSpace doesn't have the notion of an unspecified SDR white
- // level.
- if (sdr_white_level == 0.f)
- sdr_white_level = ColorSpace::kDefaultSDRWhiteLevel;
- // The kHLG constant will evaluate to values in the range [0, 12].
- skcms_TransferFunction fn = SkNamedTransferFn::kHLG;
- // The value of k is equal to kHLG evaluated at 0.75 (3.77) , divided by kHLG
- // evaluated at 1 (12), multiplied by 203 nits. This value is selected such
- // that a signal of 0.75 will map to the same value that a PQ signal for 203
- // nits will map to.
- constexpr float k = 63.84549817071231f;
- fn.f = k / sdr_white_level - 1;
- return fn;
- }
- bool PrimaryIdContainsSRGB(ColorSpace::PrimaryID id) {
- DCHECK(id != ColorSpace::PrimaryID::INVALID &&
- id != ColorSpace::PrimaryID::CUSTOM);
- switch (id) {
- case ColorSpace::PrimaryID::BT709:
- case ColorSpace::PrimaryID::BT2020:
- case ColorSpace::PrimaryID::SMPTEST428_1:
- case ColorSpace::PrimaryID::SMPTEST431_2:
- case ColorSpace::PrimaryID::P3:
- case ColorSpace::PrimaryID::XYZ_D50:
- case ColorSpace::PrimaryID::ADOBE_RGB:
- case ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN:
- return true;
- default:
- return false;
- }
- }
- } // namespace
- // static
- constexpr float ColorSpace::kDefaultSDRWhiteLevel;
- ColorSpace::ColorSpace(PrimaryID primaries,
- TransferID transfer,
- MatrixID matrix,
- RangeID range,
- const skcms_Matrix3x3* custom_primary_matrix,
- const skcms_TransferFunction* custom_transfer_fn,
- bool is_hdr)
- : primaries_(primaries),
- transfer_(transfer),
- matrix_(matrix),
- range_(range) {
- if (custom_primary_matrix) {
- DCHECK_EQ(PrimaryID::CUSTOM, primaries_);
- SetCustomPrimaries(*custom_primary_matrix);
- }
- if (custom_transfer_fn)
- SetCustomTransferFunction(*custom_transfer_fn, is_hdr);
- }
- ColorSpace::ColorSpace(const SkColorSpace& sk_color_space, bool is_hdr)
- : ColorSpace(PrimaryID::INVALID,
- TransferID::INVALID,
- MatrixID::RGB,
- RangeID::FULL) {
- skcms_TransferFunction fn;
- if (sk_color_space.isNumericalTransferFn(&fn)) {
- transfer_ = TransferID::CUSTOM;
- SetCustomTransferFunction(fn, is_hdr);
- } else if (skcms_TransferFunction_isHLGish(&fn)) {
- transfer_ = TransferID::HLG;
- } else if (skcms_TransferFunction_isPQish(&fn)) {
- transfer_ = TransferID::PQ;
- } else {
- // Construct an invalid result: Unable to extract necessary parameters
- return;
- }
- skcms_Matrix3x3 to_XYZD50;
- if (!sk_color_space.toXYZD50(&to_XYZD50)) {
- // Construct an invalid result: Unable to extract necessary parameters
- return;
- }
- SetCustomPrimaries(to_XYZD50);
- }
- bool ColorSpace::IsValid() const {
- return primaries_ != PrimaryID::INVALID && transfer_ != TransferID::INVALID &&
- matrix_ != MatrixID::INVALID && range_ != RangeID::INVALID;
- }
- // static
- ColorSpace ColorSpace::CreatePiecewiseHDR(
- PrimaryID primaries,
- float sdr_joint,
- float hdr_level,
- const skcms_Matrix3x3* custom_primary_matrix) {
- // If |sdr_joint| is 1, then this is just sRGB (and so |hdr_level| must be 1).
- // An |sdr_joint| higher than 1 breaks.
- DCHECK_LE(sdr_joint, 1.f);
- if (sdr_joint == 1.f)
- DCHECK_EQ(hdr_level, 1.f);
- // An |hdr_level| of 1 has no HDR. An |hdr_level| less than 1 breaks.
- DCHECK_GE(hdr_level, 1.f);
- ColorSpace result(primaries, TransferID::PIECEWISE_HDR, MatrixID::RGB,
- RangeID::FULL, custom_primary_matrix, nullptr);
- result.transfer_params_[0] = sdr_joint;
- result.transfer_params_[1] = hdr_level;
- return result;
- }
- // static
- ColorSpace ColorSpace::CreateCustom(const skcms_Matrix3x3& to_XYZD50,
- const skcms_TransferFunction& fn) {
- ColorSpace result(ColorSpace::PrimaryID::CUSTOM,
- ColorSpace::TransferID::CUSTOM, ColorSpace::MatrixID::RGB,
- ColorSpace::RangeID::FULL, &to_XYZD50, &fn);
- return result;
- }
- // static
- ColorSpace ColorSpace::CreateCustom(const skcms_Matrix3x3& to_XYZD50,
- TransferID transfer) {
- ColorSpace result(ColorSpace::PrimaryID::CUSTOM, transfer,
- ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL,
- &to_XYZD50, nullptr);
- return result;
- }
- void ColorSpace::SetCustomPrimaries(const skcms_Matrix3x3& to_XYZD50) {
- const PrimaryID kIDsToCheck[] = {
- PrimaryID::BT709,
- PrimaryID::BT470M,
- PrimaryID::BT470BG,
- PrimaryID::SMPTE170M,
- PrimaryID::SMPTE240M,
- PrimaryID::FILM,
- PrimaryID::BT2020,
- PrimaryID::SMPTEST428_1,
- PrimaryID::SMPTEST431_2,
- PrimaryID::P3,
- PrimaryID::XYZ_D50,
- PrimaryID::ADOBE_RGB,
- PrimaryID::APPLE_GENERIC_RGB,
- PrimaryID::WIDE_GAMUT_COLOR_SPIN,
- };
- for (PrimaryID id : kIDsToCheck) {
- skcms_Matrix3x3 matrix;
- GetPrimaryMatrix(id, &matrix);
- if (FloatsEqualWithinTolerance(&to_XYZD50.vals[0][0], &matrix.vals[0][0], 9,
- 0.001f)) {
- primaries_ = id;
- return;
- }
- }
- memcpy(custom_primary_matrix_, &to_XYZD50, 9 * sizeof(float));
- primaries_ = PrimaryID::CUSTOM;
- }
- void ColorSpace::SetCustomTransferFunction(const skcms_TransferFunction& fn,
- bool is_hdr) {
- DCHECK(transfer_ == TransferID::CUSTOM ||
- transfer_ == TransferID::CUSTOM_HDR);
- // These are all TransferIDs that will return a transfer function from
- // GetTransferFunction. When multiple ids map to the same function, this list
- // prioritizes the most common name (eg SRGB). This applies only to
- // SDR transfer functions.
- if (transfer_ == TransferID::CUSTOM) {
- const TransferID kIDsToCheck[] = {
- TransferID::SRGB, TransferID::LINEAR,
- TransferID::GAMMA18, TransferID::GAMMA22,
- TransferID::GAMMA24, TransferID::GAMMA28,
- TransferID::SMPTE240M, TransferID::BT709_APPLE,
- TransferID::SMPTEST428_1,
- };
- const TransferID kIDsToCheckNoHDR[] = {
- TransferID::SRGB,
- TransferID::LINEAR,
- };
- const TransferID kIDsToCheckHDR[] = {
- TransferID::SRGB_HDR,
- TransferID::LINEAR_HDR,
- };
- auto check_transfer_fn = [this, &fn](TransferID id) {
- skcms_TransferFunction id_fn;
- GetTransferFunction(id, &id_fn);
- if (!FloatsEqualWithinTolerance(&fn.g, &id_fn.g, 7, 0.001f))
- return false;
- transfer_ = id;
- return true;
- };
- for (TransferID id : is_hdr ? kIDsToCheckHDR : kIDsToCheckNoHDR) {
- if (check_transfer_fn(id))
- return;
- }
- for (TransferID id : kIDsToCheck) {
- if (check_transfer_fn(id))
- return;
- }
- }
- transfer_params_[0] = fn.a;
- transfer_params_[1] = fn.b;
- transfer_params_[2] = fn.c;
- transfer_params_[3] = fn.d;
- transfer_params_[4] = fn.e;
- transfer_params_[5] = fn.f;
- transfer_params_[6] = fn.g;
- }
- // static
- size_t ColorSpace::TransferParamCount(TransferID transfer) {
- switch (transfer) {
- case TransferID::CUSTOM:
- return 7;
- case TransferID::CUSTOM_HDR:
- return 7;
- case TransferID::PIECEWISE_HDR:
- return 2;
- default:
- return 0;
- }
- }
- bool ColorSpace::operator==(const ColorSpace& other) const {
- if (primaries_ != other.primaries_ || transfer_ != other.transfer_ ||
- matrix_ != other.matrix_ || range_ != other.range_) {
- return false;
- }
- if (primaries_ == PrimaryID::CUSTOM) {
- if (memcmp(custom_primary_matrix_, other.custom_primary_matrix_,
- sizeof(custom_primary_matrix_))) {
- return false;
- }
- }
- if (size_t param_count = TransferParamCount(transfer_)) {
- if (memcmp(transfer_params_, other.transfer_params_,
- param_count * sizeof(float))) {
- return false;
- }
- }
- return true;
- }
- bool ColorSpace::IsWide() const {
- // These HDR transfer functions are always wide
- if (transfer_ == TransferID::SRGB_HDR ||
- transfer_ == TransferID::LINEAR_HDR ||
- transfer_ == TransferID::CUSTOM_HDR)
- return true;
- if (primaries_ == PrimaryID::BT2020 ||
- primaries_ == PrimaryID::SMPTEST431_2 || primaries_ == PrimaryID::P3 ||
- primaries_ == PrimaryID::ADOBE_RGB ||
- primaries_ == PrimaryID::WIDE_GAMUT_COLOR_SPIN ||
- // TODO(cblume/ccameron): Compute if the custom primaries actually are
- // wide. For now, assume so.
- primaries_ == PrimaryID::CUSTOM)
- return true;
- return false;
- }
- bool ColorSpace::IsHDR() const {
- return transfer_ == TransferID::PQ || transfer_ == TransferID::HLG ||
- transfer_ == TransferID::LINEAR_HDR ||
- transfer_ == TransferID::SRGB_HDR ||
- transfer_ == TransferID::CUSTOM_HDR ||
- transfer_ == TransferID::PIECEWISE_HDR ||
- transfer_ == TransferID::SCRGB_LINEAR_80_NITS;
- }
- bool ColorSpace::IsToneMappedByDefault() const {
- switch (transfer_) {
- case TransferID::PQ:
- case TransferID::HLG:
- return true;
- default:
- return false;
- }
- }
- bool ColorSpace::IsAffectedBySDRWhiteLevel() const {
- switch (transfer_) {
- case TransferID::PQ:
- case TransferID::HLG:
- case TransferID::SCRGB_LINEAR_80_NITS:
- return true;
- default:
- return false;
- }
- }
- bool ColorSpace::FullRangeEncodedValues() const {
- return transfer_ == TransferID::LINEAR_HDR ||
- transfer_ == TransferID::SRGB_HDR ||
- transfer_ == TransferID::CUSTOM_HDR ||
- transfer_ == TransferID::PIECEWISE_HDR ||
- transfer_ == TransferID::SCRGB_LINEAR_80_NITS ||
- transfer_ == TransferID::BT1361_ECG ||
- transfer_ == TransferID::IEC61966_2_4;
- }
- bool ColorSpace::operator!=(const ColorSpace& other) const {
- return !(*this == other);
- }
- bool ColorSpace::operator<(const ColorSpace& other) const {
- if (primaries_ < other.primaries_)
- return true;
- if (primaries_ > other.primaries_)
- return false;
- if (transfer_ < other.transfer_)
- return true;
- if (transfer_ > other.transfer_)
- return false;
- if (matrix_ < other.matrix_)
- return true;
- if (matrix_ > other.matrix_)
- return false;
- if (range_ < other.range_)
- return true;
- if (range_ > other.range_)
- return false;
- if (primaries_ == PrimaryID::CUSTOM) {
- int primary_result =
- memcmp(custom_primary_matrix_, other.custom_primary_matrix_,
- sizeof(custom_primary_matrix_));
- if (primary_result < 0)
- return true;
- if (primary_result > 0)
- return false;
- }
- if (size_t param_count = TransferParamCount(transfer_)) {
- int transfer_result = memcmp(transfer_params_, other.transfer_params_,
- param_count * sizeof(float));
- if (transfer_result < 0)
- return true;
- if (transfer_result > 0)
- return false;
- }
- return false;
- }
- size_t ColorSpace::GetHash() const {
- size_t result = (static_cast<size_t>(primaries_) << 0) |
- (static_cast<size_t>(transfer_) << 8) |
- (static_cast<size_t>(matrix_) << 16) |
- (static_cast<size_t>(range_) << 24);
- if (primaries_ == PrimaryID::CUSTOM) {
- const uint32_t* params =
- reinterpret_cast<const uint32_t*>(custom_primary_matrix_);
- result ^= params[0];
- result ^= params[4];
- result ^= params[8];
- }
- {
- // Note that |transfer_params_| must be zero when they are unused.
- const uint32_t* params =
- reinterpret_cast<const uint32_t*>(transfer_params_);
- result ^= params[3];
- result ^= params[6];
- }
- return result;
- }
- #define PRINT_ENUM_CASE(TYPE, NAME) \
- case TYPE::NAME: \
- ss << #NAME; \
- break;
- std::string ColorSpace::ToString() const {
- std::stringstream ss;
- ss << std::fixed << std::setprecision(4);
- if (primaries_ != PrimaryID::CUSTOM)
- ss << "{primaries:";
- switch (primaries_) {
- PRINT_ENUM_CASE(PrimaryID, INVALID)
- PRINT_ENUM_CASE(PrimaryID, BT709)
- PRINT_ENUM_CASE(PrimaryID, BT470M)
- PRINT_ENUM_CASE(PrimaryID, BT470BG)
- PRINT_ENUM_CASE(PrimaryID, SMPTE170M)
- PRINT_ENUM_CASE(PrimaryID, SMPTE240M)
- PRINT_ENUM_CASE(PrimaryID, FILM)
- PRINT_ENUM_CASE(PrimaryID, BT2020)
- PRINT_ENUM_CASE(PrimaryID, SMPTEST428_1)
- PRINT_ENUM_CASE(PrimaryID, SMPTEST431_2)
- PRINT_ENUM_CASE(PrimaryID, P3)
- PRINT_ENUM_CASE(PrimaryID, XYZ_D50)
- PRINT_ENUM_CASE(PrimaryID, ADOBE_RGB)
- PRINT_ENUM_CASE(PrimaryID, APPLE_GENERIC_RGB)
- PRINT_ENUM_CASE(PrimaryID, WIDE_GAMUT_COLOR_SPIN)
- case PrimaryID::CUSTOM:
- // |custom_primary_matrix_| is in row-major order.
- const float sum_R = custom_primary_matrix_[0] +
- custom_primary_matrix_[3] + custom_primary_matrix_[6];
- const float sum_G = custom_primary_matrix_[1] +
- custom_primary_matrix_[4] + custom_primary_matrix_[7];
- const float sum_B = custom_primary_matrix_[2] +
- custom_primary_matrix_[5] + custom_primary_matrix_[8];
- if (IsAlmostZero(sum_R) || IsAlmostZero(sum_G) || IsAlmostZero(sum_B))
- break;
- ss << "{primaries_d50_referred: [[" << (custom_primary_matrix_[0] / sum_R)
- << ", " << (custom_primary_matrix_[3] / sum_R) << "], "
- << " [" << (custom_primary_matrix_[1] / sum_G) << ", "
- << (custom_primary_matrix_[4] / sum_G) << "], "
- << " [" << (custom_primary_matrix_[2] / sum_B) << ", "
- << (custom_primary_matrix_[5] / sum_B) << "]]";
- break;
- }
- ss << ", transfer:";
- switch (transfer_) {
- PRINT_ENUM_CASE(TransferID, INVALID)
- PRINT_ENUM_CASE(TransferID, BT709)
- PRINT_ENUM_CASE(TransferID, BT709_APPLE)
- PRINT_ENUM_CASE(TransferID, GAMMA18)
- PRINT_ENUM_CASE(TransferID, GAMMA22)
- PRINT_ENUM_CASE(TransferID, GAMMA24)
- PRINT_ENUM_CASE(TransferID, GAMMA28)
- PRINT_ENUM_CASE(TransferID, SMPTE170M)
- PRINT_ENUM_CASE(TransferID, SMPTE240M)
- PRINT_ENUM_CASE(TransferID, LINEAR)
- PRINT_ENUM_CASE(TransferID, LOG)
- PRINT_ENUM_CASE(TransferID, LOG_SQRT)
- PRINT_ENUM_CASE(TransferID, IEC61966_2_4)
- PRINT_ENUM_CASE(TransferID, BT1361_ECG)
- PRINT_ENUM_CASE(TransferID, SRGB)
- PRINT_ENUM_CASE(TransferID, BT2020_10)
- PRINT_ENUM_CASE(TransferID, BT2020_12)
- PRINT_ENUM_CASE(TransferID, SMPTEST428_1)
- PRINT_ENUM_CASE(TransferID, SRGB_HDR)
- PRINT_ENUM_CASE(TransferID, LINEAR_HDR)
- case TransferID::HLG:
- ss << "HLG (SDR white point ";
- if (transfer_params_[0] == 0.f)
- ss << "default " << kDefaultSDRWhiteLevel;
- else
- ss << transfer_params_[0];
- ss << " nits)";
- break;
- case TransferID::PQ:
- ss << "PQ (SDR white point ";
- if (transfer_params_[0] == 0.f)
- ss << "default " << kDefaultSDRWhiteLevel;
- else
- ss << transfer_params_[0];
- ss << " nits)";
- break;
- case TransferID::CUSTOM: {
- skcms_TransferFunction fn;
- GetTransferFunction(&fn);
- ss << fn.c << "*x + " << fn.f << " if x < " << fn.d << " else (" << fn.a
- << "*x + " << fn.b << ")**" << fn.g << " + " << fn.e;
- break;
- }
- case TransferID::CUSTOM_HDR: {
- skcms_TransferFunction fn;
- GetTransferFunction(&fn);
- if (fn.g == 1.0f && fn.a > 0.0f && fn.b == 0.0f && fn.c == 0.0f &&
- fn.d == 0.0f && fn.e == 0.0f && fn.f == 0.0f) {
- ss << "LINEAR_HDR (slope " << fn.a << ")";
- break;
- }
- ss << fn.c << "*x + " << fn.f << " if |x| < " << fn.d << " else sign(x)*("
- << fn.a << "*|x| + " << fn.b << ")**" << fn.g << " + " << fn.e;
- break;
- }
- case TransferID::PIECEWISE_HDR: {
- skcms_TransferFunction fn;
- GetTransferFunction(&fn);
- ss << "sRGB to 1 at " << transfer_params_[0] << ", linear to "
- << transfer_params_[1] << " at 1";
- break;
- }
- case TransferID::SCRGB_LINEAR_80_NITS:
- ss << "scRGB linear (80 nit white)";
- break;
- }
- ss << ", matrix:";
- switch (matrix_) {
- PRINT_ENUM_CASE(MatrixID, INVALID)
- PRINT_ENUM_CASE(MatrixID, RGB)
- PRINT_ENUM_CASE(MatrixID, BT709)
- PRINT_ENUM_CASE(MatrixID, FCC)
- PRINT_ENUM_CASE(MatrixID, BT470BG)
- PRINT_ENUM_CASE(MatrixID, SMPTE170M)
- PRINT_ENUM_CASE(MatrixID, SMPTE240M)
- PRINT_ENUM_CASE(MatrixID, YCOCG)
- PRINT_ENUM_CASE(MatrixID, BT2020_NCL)
- PRINT_ENUM_CASE(MatrixID, BT2020_CL)
- PRINT_ENUM_CASE(MatrixID, YDZDX)
- PRINT_ENUM_CASE(MatrixID, GBR)
- }
- ss << ", range:";
- switch (range_) {
- PRINT_ENUM_CASE(RangeID, INVALID)
- PRINT_ENUM_CASE(RangeID, LIMITED)
- PRINT_ENUM_CASE(RangeID, FULL)
- PRINT_ENUM_CASE(RangeID, DERIVED)
- }
- ss << "}";
- return ss.str();
- }
- #undef PRINT_ENUM_CASE
- ColorSpace ColorSpace::GetAsFullRangeRGB() const {
- ColorSpace result(*this);
- if (!IsValid())
- return result;
- result.matrix_ = MatrixID::RGB;
- result.range_ = RangeID::FULL;
- return result;
- }
- ContentColorUsage ColorSpace::GetContentColorUsage() const {
- if (IsHDR())
- return ContentColorUsage::kHDR;
- if (IsWide())
- return ContentColorUsage::kWideColorGamut;
- return ContentColorUsage::kSRGB;
- }
- ColorSpace ColorSpace::GetAsRGB() const {
- ColorSpace result(*this);
- if (IsValid())
- result.matrix_ = MatrixID::RGB;
- return result;
- }
- ColorSpace ColorSpace::GetScaledColorSpace(float factor) const {
- ColorSpace result(*this);
- skcms_Matrix3x3 to_XYZD50;
- GetPrimaryMatrix(&to_XYZD50);
- for (int row = 0; row < 3; ++row) {
- for (int col = 0; col < 3; ++col) {
- to_XYZD50.vals[row][col] *= factor;
- }
- }
- result.SetCustomPrimaries(to_XYZD50);
- return result;
- }
- bool ColorSpace::IsSuitableForBlending() const {
- switch (transfer_) {
- case TransferID::PQ:
- // PQ is not an acceptable space to do blending in -- blending 0 and 1
- // evenly will get a result of sRGB 0.259 (instead of 0.5).
- return false;
- case TransferID::HLG:
- case TransferID::LINEAR_HDR:
- case TransferID::SCRGB_LINEAR_80_NITS:
- // If the color space is nearly-linear, then it is not suitable for
- // blending -- blending 0 and 1 evenly will get a result of sRGB 0.735
- // (instead of 0.5).
- return false;
- case TransferID::CUSTOM_HDR: {
- // A gamma close enough to linear is treated as linear.
- skcms_TransferFunction fn;
- if (GetTransferFunction(&fn)) {
- constexpr float kMinGamma = 1.25;
- if (fn.g < kMinGamma)
- return false;
- }
- break;
- }
- default:
- break;
- }
- return true;
- }
- ColorSpace ColorSpace::GetWithMatrixAndRange(MatrixID matrix,
- RangeID range) const {
- ColorSpace result(*this);
- if (!IsValid())
- return result;
- result.matrix_ = matrix;
- result.range_ = range;
- return result;
- }
- sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace(
- absl::optional<float> sdr_white_level) const {
- // Handle only valid, full-range RGB spaces.
- if (!IsValid() || matrix_ != MatrixID::RGB || range_ != RangeID::FULL)
- return nullptr;
- // Use the named SRGB and linear-SRGB instead of the generic constructors.
- if (primaries_ == PrimaryID::BT709) {
- if (transfer_ == TransferID::SRGB)
- return SkColorSpace::MakeSRGB();
- if (transfer_ == TransferID::LINEAR || transfer_ == TransferID::LINEAR_HDR)
- return SkColorSpace::MakeSRGBLinear();
- }
- skcms_TransferFunction transfer_fn = SkNamedTransferFn::kSRGB;
- switch (transfer_) {
- case TransferID::SRGB:
- break;
- case TransferID::LINEAR:
- case TransferID::LINEAR_HDR:
- transfer_fn = SkNamedTransferFn::kLinear;
- break;
- case TransferID::HLG:
- transfer_fn = GetHLGSkTransferFunction(
- sdr_white_level.value_or(kDefaultSDRWhiteLevel));
- break;
- case TransferID::PQ:
- transfer_fn = GetPQSkTransferFunction(
- sdr_white_level.value_or(kDefaultSDRWhiteLevel));
- break;
- default:
- if (!GetTransferFunction(&transfer_fn, sdr_white_level)) {
- DLOG(ERROR) << "Failed to get transfer function for SkColorSpace";
- return nullptr;
- }
- break;
- }
- skcms_Matrix3x3 gamut = SkNamedGamut::kSRGB;
- switch (primaries_) {
- case PrimaryID::BT709:
- break;
- case PrimaryID::ADOBE_RGB:
- gamut = SkNamedGamut::kAdobeRGB;
- break;
- case PrimaryID::P3:
- gamut = SkNamedGamut::kDisplayP3;
- break;
- case PrimaryID::BT2020:
- gamut = SkNamedGamut::kRec2020;
- break;
- default:
- GetPrimaryMatrix(&gamut);
- break;
- }
- sk_sp<SkColorSpace> sk_color_space =
- SkColorSpace::MakeRGB(transfer_fn, gamut);
- if (!sk_color_space)
- DLOG(ERROR) << "SkColorSpace::MakeRGB failed.";
- return sk_color_space;
- }
- const struct _GLcolorSpace* ColorSpace::AsGLColorSpace() const {
- return reinterpret_cast<const struct _GLcolorSpace*>(this);
- }
- ColorSpace::PrimaryID ColorSpace::GetPrimaryID() const {
- return primaries_;
- }
- ColorSpace::TransferID ColorSpace::GetTransferID() const {
- return transfer_;
- }
- ColorSpace::MatrixID ColorSpace::GetMatrixID() const {
- return matrix_;
- }
- ColorSpace::RangeID ColorSpace::GetRangeID() const {
- return range_;
- }
- bool ColorSpace::HasExtendedSkTransferFn() const {
- return matrix_ == MatrixID::RGB;
- }
- bool ColorSpace::Contains(const ColorSpace& other) const {
- if (primaries_ == PrimaryID::INVALID ||
- other.primaries_ == PrimaryID::INVALID)
- return false;
- // Contains() is commonly used to check if a color space contains sRGB. The
- // computation can be bypassed for known primary IDs.
- if (primaries_ != PrimaryID::CUSTOM && other.primaries_ == PrimaryID::BT709)
- return PrimaryIdContainsSRGB(primaries_);
- // |matrix| is the primary transform matrix from |other| to this color space.
- skcms_Matrix3x3 other_to_xyz;
- skcms_Matrix3x3 this_to_xyz;
- skcms_Matrix3x3 xyz_to_this;
- other.GetPrimaryMatrix(&other_to_xyz);
- GetPrimaryMatrix(&this_to_xyz);
- skcms_Matrix3x3_invert(&this_to_xyz, &xyz_to_this);
- skcms_Matrix3x3 matrix = skcms_Matrix3x3_concat(&xyz_to_this, &other_to_xyz);
- // Return true iff each primary is in the range [0, 1] after transforming.
- // Transforming a primary vector by |matrix| always results in a column of
- // |matrix|. So the multiplication can be skipped, and we can just check if
- // each value in the matrix is in the range [0, 1].
- constexpr float epsilon = 0.001f;
- for (int r = 0; r < 3; r++) {
- for (int c = 0; c < 3; c++) {
- if (matrix.vals[r][c] < -epsilon || matrix.vals[r][c] > 1 + epsilon)
- return false;
- }
- }
- return true;
- }
- // static
- SkColorSpacePrimaries ColorSpace::GetColorSpacePrimaries(
- PrimaryID primary_id,
- const skcms_Matrix3x3* custom_primary_matrix = nullptr) {
- SkColorSpacePrimaries primaries = {0};
- if (custom_primary_matrix && primary_id == PrimaryID::CUSTOM) {
- auto* matrix = custom_primary_matrix->vals;
- const float sum_R = matrix[0][0] + matrix[1][0] + matrix[2][0];
- const float sum_G = matrix[0][1] + matrix[1][1] + matrix[2][1];
- const float sum_B = matrix[0][2] + matrix[1][2] + matrix[2][2];
- primaries.fRX = matrix[0][0] / sum_R;
- primaries.fRY = matrix[1][0] / sum_R;
- primaries.fGX = matrix[0][1] / sum_G;
- primaries.fGY = matrix[1][1] / sum_G;
- primaries.fBX = matrix[0][2] / sum_B;
- primaries.fBY = matrix[1][2] / sum_B;
- // TODO(b/229646816): Currently only returns D65 whitepoint. If possible,
- // try to return an original whitepoint in the future.
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- return primaries;
- }
- switch (primary_id) {
- case ColorSpace::PrimaryID::CUSTOM:
- case ColorSpace::PrimaryID::INVALID:
- break;
- case ColorSpace::PrimaryID::BT709:
- // BT709 is our default case. Put it after the switch just
- // in case we somehow get an id which is not listed in the switch.
- // (We don't want to use "default", because we want the compiler
- // to tell us if we forgot some enum values.)
- primaries.fRX = 0.640f;
- primaries.fRY = 0.330f;
- primaries.fGX = 0.300f;
- primaries.fGY = 0.600f;
- primaries.fBX = 0.150f;
- primaries.fBY = 0.060f;
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- break;
- case ColorSpace::PrimaryID::BT470M:
- primaries.fRX = 0.67f;
- primaries.fRY = 0.33f;
- primaries.fGX = 0.21f;
- primaries.fGY = 0.71f;
- primaries.fBX = 0.14f;
- primaries.fBY = 0.08f;
- primaries.fWX = 0.31f;
- primaries.fWY = 0.316f;
- break;
- case ColorSpace::PrimaryID::BT470BG:
- primaries.fRX = 0.64f;
- primaries.fRY = 0.33f;
- primaries.fGX = 0.29f;
- primaries.fGY = 0.60f;
- primaries.fBX = 0.15f;
- primaries.fBY = 0.06f;
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- break;
- case ColorSpace::PrimaryID::SMPTE170M:
- case ColorSpace::PrimaryID::SMPTE240M:
- primaries.fRX = 0.630f;
- primaries.fRY = 0.340f;
- primaries.fGX = 0.310f;
- primaries.fGY = 0.595f;
- primaries.fBX = 0.155f;
- primaries.fBY = 0.070f;
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- break;
- case ColorSpace::PrimaryID::APPLE_GENERIC_RGB:
- primaries.fRX = 0.63002f;
- primaries.fRY = 0.34000f;
- primaries.fGX = 0.29505f;
- primaries.fGY = 0.60498f;
- primaries.fBX = 0.15501f;
- primaries.fBY = 0.07701f;
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- break;
- case ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN:
- primaries.fRX = 0.01f;
- primaries.fRY = 0.98f;
- primaries.fGX = 0.01f;
- primaries.fGY = 0.01f;
- primaries.fBX = 0.98f;
- primaries.fBY = 0.01f;
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- break;
- case ColorSpace::PrimaryID::FILM:
- primaries.fRX = 0.681f;
- primaries.fRY = 0.319f;
- primaries.fGX = 0.243f;
- primaries.fGY = 0.692f;
- primaries.fBX = 0.145f;
- primaries.fBY = 0.049f;
- primaries.fWX = 0.310f;
- primaries.fWY = 0.136f;
- break;
- case ColorSpace::PrimaryID::BT2020:
- primaries.fRX = 0.708f;
- primaries.fRY = 0.292f;
- primaries.fGX = 0.170f;
- primaries.fGY = 0.797f;
- primaries.fBX = 0.131f;
- primaries.fBY = 0.046f;
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- break;
- case ColorSpace::PrimaryID::SMPTEST428_1:
- primaries.fRX = 1.0f;
- primaries.fRY = 0.0f;
- primaries.fGX = 0.0f;
- primaries.fGY = 1.0f;
- primaries.fBX = 0.0f;
- primaries.fBY = 0.0f;
- primaries.fWX = 1.0f / 3.0f;
- primaries.fWY = 1.0f / 3.0f;
- break;
- case ColorSpace::PrimaryID::SMPTEST431_2:
- primaries.fRX = 0.680f;
- primaries.fRY = 0.320f;
- primaries.fGX = 0.265f;
- primaries.fGY = 0.690f;
- primaries.fBX = 0.150f;
- primaries.fBY = 0.060f;
- primaries.fWX = 0.314f;
- primaries.fWY = 0.351f;
- break;
- case ColorSpace::PrimaryID::P3:
- primaries.fRX = 0.680f;
- primaries.fRY = 0.320f;
- primaries.fGX = 0.265f;
- primaries.fGY = 0.690f;
- primaries.fBX = 0.150f;
- primaries.fBY = 0.060f;
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- break;
- case ColorSpace::PrimaryID::XYZ_D50:
- primaries.fRX = 1.0f;
- primaries.fRY = 0.0f;
- primaries.fGX = 0.0f;
- primaries.fGY = 1.0f;
- primaries.fBX = 0.0f;
- primaries.fBY = 0.0f;
- primaries.fWX = 0.34567f;
- primaries.fWY = 0.35850f;
- break;
- case ColorSpace::PrimaryID::ADOBE_RGB:
- primaries.fRX = 0.6400f;
- primaries.fRY = 0.3300f;
- primaries.fGX = 0.2100f;
- primaries.fGY = 0.7100f;
- primaries.fBX = 0.1500f;
- primaries.fBY = 0.0600f;
- primaries.fWX = 0.3127f;
- primaries.fWY = 0.3290f;
- break;
- }
- return primaries;
- }
- SkColorSpacePrimaries ColorSpace::GetColorSpacePrimaries() const {
- skcms_Matrix3x3 matrix;
- memcpy(&matrix, custom_primary_matrix_, 9 * sizeof(float));
- return GetColorSpacePrimaries(primaries_, &matrix);
- }
- // static
- void ColorSpace::GetPrimaryMatrix(PrimaryID primary_id,
- skcms_Matrix3x3* to_XYZD50) {
- SkColorSpacePrimaries primaries = GetColorSpacePrimaries(primary_id);
- if (primary_id == PrimaryID::CUSTOM || primary_id == PrimaryID::INVALID) {
- *to_XYZD50 = SkNamedGamut::kXYZ; // Identity
- return;
- }
- primaries.toXYZD50(to_XYZD50);
- }
- void ColorSpace::GetPrimaryMatrix(skcms_Matrix3x3* to_XYZD50) const {
- if (primaries_ == PrimaryID::CUSTOM) {
- memcpy(to_XYZD50, custom_primary_matrix_, 9 * sizeof(float));
- } else {
- GetPrimaryMatrix(primaries_, to_XYZD50);
- }
- }
- SkM44 ColorSpace::GetPrimaryMatrix() const {
- skcms_Matrix3x3 toXYZ_3x3;
- GetPrimaryMatrix(&toXYZ_3x3);
- return SkM44FromRowMajor3x3(&toXYZ_3x3.vals[0][0]);
- }
- // static
- bool ColorSpace::GetTransferFunction(TransferID transfer,
- skcms_TransferFunction* fn) {
- // Default to F(x) = pow(x, 1)
- fn->a = 1;
- fn->b = 0;
- fn->c = 0;
- fn->d = 0;
- fn->e = 0;
- fn->f = 0;
- fn->g = 1;
- switch (transfer) {
- case ColorSpace::TransferID::LINEAR:
- case ColorSpace::TransferID::LINEAR_HDR:
- return true;
- case ColorSpace::TransferID::GAMMA18:
- fn->g = 1.801f;
- return true;
- case ColorSpace::TransferID::GAMMA22:
- fn->g = 2.2f;
- return true;
- case ColorSpace::TransferID::GAMMA24:
- fn->g = 2.4f;
- return true;
- case ColorSpace::TransferID::GAMMA28:
- fn->g = 2.8f;
- return true;
- case ColorSpace::TransferID::SMPTE240M:
- fn->a = 0.899626676224f;
- fn->b = 0.100373323776f;
- fn->c = 0.250000000000f;
- fn->d = 0.091286342118f;
- fn->g = 2.222222222222f;
- return true;
- case ColorSpace::TransferID::BT709:
- case ColorSpace::TransferID::SMPTE170M:
- case ColorSpace::TransferID::BT2020_10:
- case ColorSpace::TransferID::BT2020_12:
- // With respect to rendering BT709
- // * SMPTE 1886 suggests that we should be using gamma 2.4.
- // * Most displays actually use a gamma of 2.2, and most media playing
- // software uses the sRGB transfer function.
- // * User studies shows that users don't really care.
- // * Apple's CoreVideo uses gamma=1.961.
- // Bearing all of that in mind, use the same transfer function as sRGB,
- // which will allow more optimization, and will more closely match other
- // media players.
- case ColorSpace::TransferID::SRGB:
- case ColorSpace::TransferID::SRGB_HDR:
- fn->a = 0.947867345704f;
- fn->b = 0.052132654296f;
- fn->c = 0.077399380805f;
- fn->d = 0.040449937172f;
- fn->g = 2.400000000000f;
- return true;
- case ColorSpace::TransferID::BT709_APPLE:
- fn->g = 1.961000000000f;
- return true;
- case ColorSpace::TransferID::SMPTEST428_1:
- fn->a = 1.034080527699f; // (52.37 / 48.0) ^ (1.0 / 2.6) per ITU-T H.273.
- fn->g = 2.600000000000f;
- return true;
- case ColorSpace::TransferID::IEC61966_2_4:
- // This could potentially be represented the same as SRGB, but it handles
- // negative values differently.
- break;
- case ColorSpace::TransferID::HLG:
- case ColorSpace::TransferID::BT1361_ECG:
- case ColorSpace::TransferID::LOG:
- case ColorSpace::TransferID::LOG_SQRT:
- case ColorSpace::TransferID::PQ:
- case ColorSpace::TransferID::CUSTOM:
- case ColorSpace::TransferID::CUSTOM_HDR:
- case ColorSpace::TransferID::PIECEWISE_HDR:
- case ColorSpace::TransferID::SCRGB_LINEAR_80_NITS:
- case ColorSpace::TransferID::INVALID:
- break;
- }
- return false;
- }
- bool ColorSpace::GetTransferFunction(
- skcms_TransferFunction* fn,
- absl::optional<float> sdr_white_level) const {
- switch (transfer_) {
- case TransferID::CUSTOM:
- case TransferID::CUSTOM_HDR:
- fn->a = transfer_params_[0];
- fn->b = transfer_params_[1];
- fn->c = transfer_params_[2];
- fn->d = transfer_params_[3];
- fn->e = transfer_params_[4];
- fn->f = transfer_params_[5];
- fn->g = transfer_params_[6];
- return true;
- case TransferID::SCRGB_LINEAR_80_NITS:
- if (sdr_white_level) {
- fn->a = 80.f / *sdr_white_level;
- fn->b = 0;
- fn->c = 0;
- fn->d = 0;
- fn->e = 0;
- fn->f = 0;
- fn->g = 1;
- return true;
- } else {
- // Using SCRGB_LINEAR_80_NITS without specifying an SDR white level is
- // guaranteed to produce incorrect results.
- return false;
- }
- default:
- return GetTransferFunction(transfer_, fn);
- }
- }
- bool ColorSpace::GetInverseTransferFunction(
- skcms_TransferFunction* fn,
- absl::optional<float> sdr_white_level) const {
- if (!GetTransferFunction(fn, sdr_white_level))
- return false;
- *fn = SkTransferFnInverse(*fn);
- return true;
- }
- bool ColorSpace::GetPiecewiseHDRParams(float* sdr_joint,
- float* hdr_level) const {
- if (transfer_ != TransferID::PIECEWISE_HDR)
- return false;
- *sdr_joint = transfer_params_[0];
- *hdr_level = transfer_params_[1];
- return true;
- }
- SkM44 ColorSpace::GetTransferMatrix(int bit_depth) const {
- DCHECK_GE(bit_depth, 8);
- // If chroma samples are real numbers in the range of −0.5 to 0.5, an offset
- // of 0.5 is added to get real numbers in the range of 0 to 1. When
- // represented as an unsigned |bit_depth|-bit integer, this 0.5 offset is
- // approximated by 1 << (bit_depth - 1). chroma_0_5 is this approximate value
- // converted to a real number in the range of 0 to 1.
- //
- // TODO(wtc): For now chroma_0_5 is only used for YCgCo. It should also be
- // used for YUV.
- const float chroma_0_5 =
- static_cast<float>(1 << (bit_depth - 1)) / ((1 << bit_depth) - 1);
- float Kr = 0;
- float Kb = 0;
- switch (matrix_) {
- case ColorSpace::MatrixID::RGB:
- case ColorSpace::MatrixID::INVALID:
- return SkM44();
- case ColorSpace::MatrixID::BT709:
- Kr = 0.2126f;
- Kb = 0.0722f;
- break;
- case ColorSpace::MatrixID::FCC:
- Kr = 0.30f;
- Kb = 0.11f;
- break;
- case ColorSpace::MatrixID::BT470BG:
- case ColorSpace::MatrixID::SMPTE170M:
- Kr = 0.299f;
- Kb = 0.114f;
- break;
- case ColorSpace::MatrixID::SMPTE240M:
- Kr = 0.212f;
- Kb = 0.087f;
- break;
- case ColorSpace::MatrixID::YCOCG: {
- float data[16] = {0.25f, 0.5f, 0.25f, 0.0f, // Y
- -0.25f, 0.5f, -0.25f, chroma_0_5, // Cg
- 0.5f, 0.0f, -0.5f, chroma_0_5, // Co
- 0.0f, 0.0f, 0.0f, 1.0f};
- return SkM44::RowMajor(data);
- }
- // BT2020_CL is a special case.
- // Basically we return a matrix that transforms RYB values
- // to YUV values. (Note that the green component have been replaced
- // with the luminance.)
- case ColorSpace::MatrixID::BT2020_CL: {
- Kr = 0.2627f;
- Kb = 0.0593f;
- float data[16] = {1.0f, 0.0f, 0.0f, 0.0f, // R
- Kr, 1.0f - Kr - Kb, Kb, 0.0f, // Y
- 0.0f, 0.0f, 1.0f, 0.0f, // B
- 0.0f, 0.0f, 0.0f, 1.0f};
- return SkM44::RowMajor(data);
- }
- case ColorSpace::MatrixID::BT2020_NCL:
- Kr = 0.2627f;
- Kb = 0.0593f;
- break;
- case ColorSpace::MatrixID::YDZDX: {
- // clang-format off
- float data[16] = {
- 0.0f, 1.0f, 0.0f, 0.0f, // Y
- 0.0f, -0.5f, 0.986566f / 2.0f, 0.5f, // DX or DZ
- 0.5f, -0.991902f / 2.0f, 0.0f, 0.5f, // DZ or DX
- 0.0f, 0.0f, 0.0f, 1.0f,
- };
- // clang-format on
- return SkM44::RowMajor(data);
- }
- case ColorSpace::MatrixID::GBR: {
- float data[16] = {0.0f, 1.0f, 0.0f, 0.0f, // G
- 0.0f, 0.0f, 1.0f, 0.0f, // B
- 1.0f, 0.0f, 0.0f, 0.0f, // R
- 0.0f, 0.0f, 0.0f, 1.0f};
- return SkM44::RowMajor(data);
- }
- }
- float Kg = 1.0f - Kr - Kb;
- float u_m = 0.5f / (1.0f - Kb);
- float v_m = 0.5f / (1.0f - Kr);
- // clang-format off
- float data[16] = {
- Kr, Kg, Kb, 0.0f, // Y
- u_m * -Kr, u_m * -Kg, u_m * (1.0f - Kb), 0.5f, // U
- v_m * (1.0f - Kr), v_m * -Kg, v_m * -Kb, 0.5f, // V
- 0.0f, 0.0f, 0.0f, 1.0f,
- };
- // clang-format on
- return SkM44::RowMajor(data);
- }
- SkM44 ColorSpace::GetRangeAdjustMatrix(int bit_depth) const {
- DCHECK_GE(bit_depth, 8);
- switch (range_) {
- case RangeID::FULL:
- case RangeID::INVALID:
- return SkM44();
- case RangeID::DERIVED:
- case RangeID::LIMITED:
- break;
- }
- // See ITU-T H.273 (2016), Section 8.3. The following is derived from
- // Equations 20-31.
- const int shift = bit_depth - 8;
- const float a_y = 219 << shift;
- const float c = (1 << bit_depth) - 1;
- const float scale_y = c / a_y;
- switch (matrix_) {
- case MatrixID::RGB:
- case MatrixID::GBR:
- case MatrixID::INVALID:
- case MatrixID::YCOCG:
- return SkM44::Scale(scale_y, scale_y, scale_y)
- .postTranslate(-16.0f / 219.0f, -16.0f / 219.0f, -16.0f / 219.0f);
- case MatrixID::BT709:
- case MatrixID::FCC:
- case MatrixID::BT470BG:
- case MatrixID::SMPTE170M:
- case MatrixID::SMPTE240M:
- case MatrixID::BT2020_NCL:
- case MatrixID::BT2020_CL:
- case MatrixID::YDZDX: {
- const float a_uv = 224 << shift;
- const float scale_uv = c / a_uv;
- const float translate_uv = (a_uv - c) / (2.0f * a_uv);
- return SkM44::Scale(scale_y, scale_uv, scale_uv)
- .postTranslate(-16.0f / 219.0f, translate_uv, translate_uv);
- }
- }
- NOTREACHED();
- return SkM44();
- }
- bool ColorSpace::ToSkYUVColorSpace(int bit_depth, SkYUVColorSpace* out) const {
- switch (matrix_) {
- case MatrixID::BT709:
- *out = range_ == RangeID::FULL ? kRec709_Full_SkYUVColorSpace
- : kRec709_Limited_SkYUVColorSpace;
- return true;
- case MatrixID::BT470BG:
- case MatrixID::SMPTE170M:
- *out = range_ == RangeID::FULL ? kJPEG_SkYUVColorSpace
- : kRec601_Limited_SkYUVColorSpace;
- return true;
- case MatrixID::BT2020_NCL:
- if (bit_depth == 8) {
- *out = range_ == RangeID::FULL ? kBT2020_8bit_Full_SkYUVColorSpace
- : kBT2020_8bit_Limited_SkYUVColorSpace;
- return true;
- }
- if (bit_depth == 10) {
- *out = range_ == RangeID::FULL ? kBT2020_10bit_Full_SkYUVColorSpace
- : kBT2020_10bit_Limited_SkYUVColorSpace;
- return true;
- }
- if (bit_depth == 12) {
- *out = range_ == RangeID::FULL ? kBT2020_12bit_Full_SkYUVColorSpace
- : kBT2020_12bit_Limited_SkYUVColorSpace;
- return true;
- }
- return false;
- default:
- break;
- }
- return false;
- }
- std::ostream& operator<<(std::ostream& out, const ColorSpace& color_space) {
- return out << color_space.ToString();
- }
- } // namespace gfx
|