123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- // Copyright 2014 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/display/util/display_util.h"
- #include <stddef.h>
- #include "base/command_line.h"
- #include "base/containers/contains.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/no_destructor.h"
- #include "base/notreached.h"
- #include "ui/display/types/display_snapshot.h"
- #include "ui/display/util/edid_parser.h"
- #include "ui/gfx/icc_profile.h"
- namespace display {
- namespace {
- base::flat_set<int64_t>* internal_display_ids() {
- static base::NoDestructor<base::flat_set<int64_t>> display_ids;
- return display_ids.get();
- };
- // A list of bogus sizes in mm that should be ignored.
- // See crbug.com/136533. The first element maintains the minimum
- // size required to be valid size.
- const int kInvalidDisplaySizeList[][2] = {
- {40, 30},
- {50, 40},
- {160, 90},
- {160, 100},
- };
- // Used in the GetColorSpaceFromEdid function to collect data on whether the
- // color space extracted from an EDID blob passed the sanity checks.
- void EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome outcome) {
- base::UmaHistogramEnumeration("DrmUtil.GetColorSpaceFromEdid.ChecksOutcome",
- outcome);
- }
- // Returns true if each and all matrix values are within |epsilon| distance.
- bool NearlyEqual(const skcms_Matrix3x3& lhs,
- const skcms_Matrix3x3& rhs,
- float epsilon) {
- for (int r = 0; r < 3; r++) {
- for (int c = 0; c < 3; c++) {
- if (std::abs(lhs.vals[r][c] - rhs.vals[r][c]) > epsilon)
- return false;
- }
- }
- return true;
- }
- } // namespace
- bool IsDisplaySizeValid(const gfx::Size& physical_size) {
- // Ignore if the reported display is smaller than minimum size.
- if (physical_size.width() <= kInvalidDisplaySizeList[0][0] ||
- physical_size.height() <= kInvalidDisplaySizeList[0][1]) {
- VLOG(1) << "Smaller than minimum display size";
- return false;
- }
- for (size_t i = 1; i < std::size(kInvalidDisplaySizeList); ++i) {
- const gfx::Size size(kInvalidDisplaySizeList[i][0],
- kInvalidDisplaySizeList[i][1]);
- if (physical_size == size) {
- VLOG(1) << "Invalid display size detected:" << size.ToString();
- return false;
- }
- }
- return true;
- }
- int64_t GenerateDisplayID(uint16_t manufacturer_id,
- uint32_t product_code_hash,
- uint8_t output_index) {
- return ((static_cast<int64_t>(manufacturer_id) << 40) |
- (static_cast<int64_t>(product_code_hash) << 8) | output_index);
- }
- gfx::ColorSpace GetColorSpaceFromEdid(const display::EdidParser& edid_parser) {
- const SkColorSpacePrimaries primaries = edid_parser.primaries();
- // Sanity check: primaries should verify By <= Ry <= Gy, Bx <= Rx and Gx <=
- // Rx, to guarantee that the R, G and B colors are each in the correct region.
- if (!(primaries.fBX <= primaries.fRX && primaries.fGX <= primaries.fRX &&
- primaries.fBY <= primaries.fRY && primaries.fRY <= primaries.fGY)) {
- EmitEdidColorSpaceChecksOutcomeUma(
- EdidColorSpaceChecksOutcome::kErrorBadCoordinates);
- return gfx::ColorSpace();
- }
- // Sanity check: the area spawned by the primaries' triangle is too small,
- // i.e. less than half the surface of the triangle spawned by sRGB/BT.709.
- constexpr double kBT709PrimariesArea = 0.0954;
- const float primaries_area_twice =
- (primaries.fRX * primaries.fGY) + (primaries.fBX * primaries.fRY) +
- (primaries.fGX * primaries.fBY) - (primaries.fBX * primaries.fGY) -
- (primaries.fGX * primaries.fRY) - (primaries.fRX * primaries.fBY);
- if (primaries_area_twice < kBT709PrimariesArea) {
- EmitEdidColorSpaceChecksOutcomeUma(
- EdidColorSpaceChecksOutcome::kErrorPrimariesAreaTooSmall);
- return gfx::ColorSpace();
- }
- // Sanity check: https://crbug.com/809909, the blue primary coordinates should
- // not be too far left/upwards of the expected location (namely [0.15, 0.06]
- // for sRGB/ BT.709/ Adobe RGB/ DCI-P3, and [0.131, 0.046] for BT.2020).
- constexpr float kExpectedBluePrimaryX = 0.15f;
- constexpr float kBluePrimaryXDelta = 0.02f;
- constexpr float kExpectedBluePrimaryY = 0.06f;
- constexpr float kBluePrimaryYDelta = 0.031f;
- const bool is_blue_primary_broken =
- (std::abs(primaries.fBX - kExpectedBluePrimaryX) > kBluePrimaryXDelta) ||
- (std::abs(primaries.fBY - kExpectedBluePrimaryY) > kBluePrimaryYDelta);
- if (is_blue_primary_broken) {
- EmitEdidColorSpaceChecksOutcomeUma(
- EdidColorSpaceChecksOutcome::kErrorBluePrimaryIsBroken);
- return gfx::ColorSpace();
- }
- skcms_Matrix3x3 primaries_matrix;
- if (!primaries.toXYZD50(&primaries_matrix)) {
- EmitEdidColorSpaceChecksOutcomeUma(
- EdidColorSpaceChecksOutcome::kErrorCannotExtractToXYZD50);
- return gfx::ColorSpace();
- }
- // Snap the primaries to those of BT.709/sRGB for performance purposes, see
- // crbug.com/1073467. kPrimariesTolerance is an educated guess from various
- // ChromeOS panels observations.
- auto color_space_primaries = gfx::ColorSpace::PrimaryID::INVALID;
- constexpr float kPrimariesTolerance = 0.025;
- if (NearlyEqual(primaries_matrix, SkNamedGamut::kSRGB, kPrimariesTolerance))
- color_space_primaries = gfx::ColorSpace::PrimaryID::BT709;
- const float gamma = edid_parser.gamma();
- if (gamma < 1.0f) {
- EmitEdidColorSpaceChecksOutcomeUma(
- EdidColorSpaceChecksOutcome::kErrorBadGamma);
- return gfx::ColorSpace();
- }
- EmitEdidColorSpaceChecksOutcomeUma(EdidColorSpaceChecksOutcome::kSuccess);
- auto transfer_id = gfx::ColorSpace::TransferID::INVALID;
- if (base::Contains(edid_parser.supported_color_primary_ids(),
- gfx::ColorSpace::PrimaryID::BT2020)) {
- if (base::Contains(edid_parser.supported_color_transfer_ids(),
- gfx::ColorSpace::TransferID::PQ)) {
- transfer_id = gfx::ColorSpace::TransferID::PQ;
- } else if (base::Contains(edid_parser.supported_color_transfer_ids(),
- gfx::ColorSpace::TransferID::HLG)) {
- transfer_id = gfx::ColorSpace::TransferID::HLG;
- }
- } else if (gamma == 2.2f) {
- transfer_id = gfx::ColorSpace::TransferID::GAMMA22;
- } else if (gamma == 2.4f) {
- transfer_id = gfx::ColorSpace::TransferID::GAMMA24;
- }
- // Prefer to return a name-based ColorSpace to ease subsequent calculations.
- if (transfer_id != gfx::ColorSpace::TransferID::INVALID) {
- if (color_space_primaries != gfx::ColorSpace::PrimaryID::INVALID)
- return gfx::ColorSpace(color_space_primaries, transfer_id);
- return gfx::ColorSpace::CreateCustom(primaries_matrix, transfer_id);
- }
- skcms_TransferFunction transfer = {gamma, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f};
- if (color_space_primaries == gfx::ColorSpace::PrimaryID::INVALID)
- return gfx::ColorSpace::CreateCustom(primaries_matrix, transfer);
- return gfx::ColorSpace(
- color_space_primaries, gfx::ColorSpace::TransferID::CUSTOM,
- gfx::ColorSpace::MatrixID::RGB, gfx::ColorSpace::RangeID::FULL,
- /*custom_primary_matrix=*/nullptr, &transfer);
- }
- bool CompareDisplayIds(int64_t id1, int64_t id2) {
- if (id1 == id2)
- return false;
- // Output index is stored in the first 8 bits. See GetDisplayIdFromEDID
- // in edid_parser.cc.
- int index_1 = id1 & 0xFF;
- int index_2 = id2 & 0xFF;
- DCHECK_NE(index_1, index_2) << id1 << " and " << id2;
- bool first_is_internal = IsInternalDisplayId(id1);
- bool second_is_internal = IsInternalDisplayId(id2);
- if (first_is_internal && !second_is_internal)
- return true;
- if (!first_is_internal && second_is_internal)
- return false;
- return index_1 < index_2;
- }
- bool IsInternalDisplayId(int64_t display_id) {
- return base::Contains(*internal_display_ids(), display_id);
- }
- const base::flat_set<int64_t>& GetInternalDisplayIds() {
- return *internal_display_ids();
- }
- // static
- bool HasInternalDisplay() {
- return !GetInternalDisplayIds().empty();
- }
- void SetInternalDisplayIds(base::flat_set<int64_t> display_ids) {
- *internal_display_ids() = std::move(display_ids);
- }
- gfx::ColorSpace ForcedColorProfileStringToColorSpace(const std::string& value) {
- if (value == "srgb")
- return gfx::ColorSpace::CreateSRGB();
- if (value == "display-p3-d65")
- return gfx::ColorSpace::CreateDisplayP3D65();
- if (value == "scrgb-linear")
- return gfx::ColorSpace::CreateSRGBLinear();
- if (value == "hdr10")
- return gfx::ColorSpace::CreateHDR10();
- if (value == "extended-srgb")
- return gfx::ColorSpace::CreateExtendedSRGB();
- if (value == "generic-rgb") {
- return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::APPLE_GENERIC_RGB,
- gfx::ColorSpace::TransferID::GAMMA18);
- }
- if (value == "color-spin-gamma24") {
- // Run this color profile through an ICC profile. The resulting color space
- // is slightly different from the input color space, and removing the ICC
- // profile would require rebaselineing many layout tests.
- gfx::ColorSpace color_space(
- gfx::ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN,
- gfx::ColorSpace::TransferID::GAMMA24);
- return gfx::ICCProfile::FromColorSpace(color_space).GetColorSpace();
- }
- LOG(ERROR) << "Invalid forced color profile: \"" << value << "\"";
- return gfx::ColorSpace::CreateSRGB();
- }
- gfx::ColorSpace GetForcedDisplayColorProfile() {
- DCHECK(HasForceDisplayColorProfile());
- std::string value =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- /*switches::kForceDisplayColorProfile=*/"force-color-profile");
- return ForcedColorProfileStringToColorSpace(value);
- }
- bool HasForceDisplayColorProfile() {
- return base::CommandLine::ForCurrentProcess()->HasSwitch(
- /*switches::kForceDisplayColorProfile=*/"force-color-profile");
- }
- #if BUILDFLAG(IS_CHROMEOS)
- // Constructs the raster DisplayColorSpaces out of |snapshot_color_space|,
- // including the HDR ones if present and |allow_high_bit_depth| is set.
- gfx::DisplayColorSpaces CreateDisplayColorSpaces(
- const gfx::ColorSpace& snapshot_color_space,
- bool allow_high_bit_depth,
- const absl::optional<gfx::HDRStaticMetadata>& hdr_static_metadata) {
- if (HasForceDisplayColorProfile()) {
- return gfx::DisplayColorSpaces(GetForcedDisplayColorProfile(),
- DisplaySnapshot::PrimaryFormat());
- }
- // ChromeOS VMs (e.g. amd64-generic or betty) have INVALID Primaries; just
- // pass the color space along.
- if (!snapshot_color_space.IsValid()) {
- return gfx::DisplayColorSpaces(snapshot_color_space,
- DisplaySnapshot::PrimaryFormat());
- }
- const auto primary_id = snapshot_color_space.GetPrimaryID();
- skcms_Matrix3x3 primary_matrix{};
- if (primary_id == gfx::ColorSpace::PrimaryID::CUSTOM)
- snapshot_color_space.GetPrimaryMatrix(&primary_matrix);
- // Reconstruct the native colorspace with an IEC61966 2.1 transfer function
- // for SDR content (matching that of sRGB).
- gfx::ColorSpace sdr_color_space;
- if (primary_id == gfx::ColorSpace::PrimaryID::CUSTOM) {
- sdr_color_space = gfx::ColorSpace::CreateCustom(
- primary_matrix, gfx::ColorSpace::TransferID::SRGB);
- } else {
- sdr_color_space =
- gfx::ColorSpace(primary_id, gfx::ColorSpace::TransferID::SRGB);
- }
- gfx::DisplayColorSpaces display_color_spaces = gfx::DisplayColorSpaces(
- sdr_color_space, DisplaySnapshot::PrimaryFormat());
- if (allow_high_bit_depth && snapshot_color_space.IsHDR()) {
- gfx::ColorSpace hdr_color_space;
- if (primary_id == gfx::ColorSpace::PrimaryID::CUSTOM) {
- hdr_color_space = gfx::ColorSpace::CreatePiecewiseHDR(
- primary_id, display::kSDRJoint, display::kHDRLevel, &primary_matrix);
- } else {
- hdr_color_space = gfx::ColorSpace::CreatePiecewiseHDR(
- primary_id, display::kSDRJoint, display::kHDRLevel);
- }
- display_color_spaces.SetOutputColorSpaceAndBufferFormat(
- gfx::ContentColorUsage::kHDR, false /* needs_alpha */, hdr_color_space,
- gfx::BufferFormat::RGBA_1010102);
- display_color_spaces.SetOutputColorSpaceAndBufferFormat(
- gfx::ContentColorUsage::kHDR, true /* needs_alpha */, hdr_color_space,
- gfx::BufferFormat::RGBA_1010102);
- // TODO(https://crbug.com/1286074): Populate maximum luminance based on
- // `hdr_static_metadata`. For now, assume that the HDR maximum luminance
- // is 1,000% of the SDR maximum luminance.
- constexpr float kHDRMaxLuminanceRelative = 10.f;
- display_color_spaces.SetHDRMaxLuminanceRelative(kHDRMaxLuminanceRelative);
- }
- return display_color_spaces;
- }
- #endif // BUILDFLAG(IS_CHROMEOS)
- } // namespace display
|