Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Changes DlColor to support wide gamut colors (#54473) #54648

Merged
merged 5 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -42320,6 +42320,7 @@ ORIGIN: ../../../flutter/display_list/dl_builder.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/display_list/dl_builder.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/display_list/dl_canvas.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/display_list/dl_canvas.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/display_list/dl_color.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/display_list/dl_color.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/display_list/dl_op_flags.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/display_list/dl_op_flags.h + ../../../flutter/LICENSE
Expand Down Expand Up @@ -45199,6 +45200,7 @@ FILE: ../../../flutter/display_list/dl_builder.cc
FILE: ../../../flutter/display_list/dl_builder.h
FILE: ../../../flutter/display_list/dl_canvas.cc
FILE: ../../../flutter/display_list/dl_canvas.h
FILE: ../../../flutter/display_list/dl_color.cc
FILE: ../../../flutter/display_list/dl_color.h
FILE: ../../../flutter/display_list/dl_op_flags.cc
FILE: ../../../flutter/display_list/dl_op_flags.h
Expand Down
1 change: 1 addition & 0 deletions display_list/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ source_set("display_list") {
"dl_builder.h",
"dl_canvas.cc",
"dl_canvas.h",
"dl_color.cc",
"dl_color.h",
"dl_op_flags.cc",
"dl_op_flags.h",
Expand Down
2 changes: 1 addition & 1 deletion display_list/display_list_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1847,7 +1847,7 @@ TEST_F(DisplayListTest, FlutterSvgIssue661BoundsWereEmpty) {
// This is the more practical result. The bounds are "almost" 0,0,100x100
EXPECT_EQ(display_list->bounds().roundOut(), SkIRect::MakeWH(100, 100));
EXPECT_EQ(display_list->op_count(), 19u);
EXPECT_EQ(display_list->bytes(), sizeof(DisplayList) + 408u);
EXPECT_EQ(display_list->bytes(), sizeof(DisplayList) + 424u);
EXPECT_EQ(display_list->total_depth(), 3u);
}

Expand Down
2 changes: 1 addition & 1 deletion display_list/dl_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ class DisplayListBuilder final : public virtual DlCanvas,

// kAnyColor is a non-opaque and non-transparent color that will not
// trigger any short-circuit tests about the results of a blend.
static constexpr DlColor kAnyColor = DlColor::kMidGrey().withAlpha(0x80);
static constexpr DlColor kAnyColor = DlColor::kMidGrey().withAlphaF(0.5f);
static_assert(!kAnyColor.isOpaque());
static_assert(!kAnyColor.isTransparent());
static DlColor GetEffectiveColor(const DlPaint& paint,
Expand Down
75 changes: 75 additions & 0 deletions display_list/dl_color.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2013 The Flutter 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 "flutter/display_list/dl_color.h"

namespace flutter {

namespace {
const std::array<DlScalar, 12> kP3ToSrgb = {
1.306671048092539, -0.298061942172353,
0.213228303487995, -0.213580156254466, //
-0.117390025596251, 1.127722006101976,
0.109727644608938, -0.109450321455370, //
0.214813187718391, 0.054268702864647,
1.406898424029350, -0.364892765879631};

DlColor transform(const DlColor& color,
const std::array<DlScalar, 12>& matrix,
DlColorSpace color_space) {
return DlColor(color.getAlphaF(),
matrix[0] * color.getRedF() + //
matrix[1] * color.getGreenF() + //
matrix[2] * color.getBlueF() + //
matrix[3], //
matrix[4] * color.getRedF() + //
matrix[5] * color.getGreenF() + //
matrix[6] * color.getBlueF() + //
matrix[7], //
matrix[8] * color.getRedF() + //
matrix[9] * color.getGreenF() + //
matrix[10] * color.getBlueF() + //
matrix[11], //
color_space);
}
} // namespace

DlColor DlColor::withColorSpace(DlColorSpace color_space) const {
switch (color_space_) {
case DlColorSpace::kSRGB:
switch (color_space) {
case DlColorSpace::kSRGB:
return *this;
case DlColorSpace::kExtendedSRGB:
return DlColor(alpha_, red_, green_, blue_,
DlColorSpace::kExtendedSRGB);
case DlColorSpace::kDisplayP3:
FML_CHECK(false) << "not implemented";
return *this;
}
case DlColorSpace::kExtendedSRGB:
switch (color_space) {
case DlColorSpace::kSRGB:
FML_CHECK(false) << "not implemented";
return *this;
case DlColorSpace::kExtendedSRGB:
return *this;
case DlColorSpace::kDisplayP3:
FML_CHECK(false) << "not implemented";
return *this;
}
case DlColorSpace::kDisplayP3:
switch (color_space) {
case DlColorSpace::kSRGB:
FML_CHECK(false) << "not implemented";
return *this;
case DlColorSpace::kExtendedSRGB:
return transform(*this, kP3ToSrgb, DlColorSpace::kExtendedSRGB);
case DlColorSpace::kDisplayP3:
return *this;
}
}
}

} // namespace flutter
152 changes: 113 additions & 39 deletions display_list/dl_color.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,39 @@

namespace flutter {

// These should match the enumerations defined in //lib/ui/painting.dart.
enum class DlColorSpace { kSRGB = 0, kExtendedSRGB = 1, kDisplayP3 = 2 };

/// A representation of a color.
///
/// The color belongs to a DlColorSpace. Using deprecated integer data accessors
/// on colors not in the kSRGB colorspace can lead to data loss. Using the
/// floating point accessors and constructors that were added for wide-gamut
/// support are preferred.
struct DlColor {
public:
constexpr DlColor() : argb_(0xFF000000) {}
constexpr explicit DlColor(uint32_t argb) : argb_(argb) {}
constexpr DlColor()
: alpha_(1.f),
red_(0.f),
green_(0.f),
blue_(0.f),
color_space_(DlColorSpace::kSRGB) {}
constexpr explicit DlColor(uint32_t argb)
: alpha_(toF((argb >> 24) & 0xff)),
red_(toF((argb >> 16) & 0xff)),
green_(toF((argb >> 8) & 0xff)),
blue_(toF((argb >> 0) & 0xff)),
color_space_(DlColorSpace::kSRGB) {}
constexpr DlColor(DlScalar alpha,
DlScalar red,
DlScalar green,
DlScalar blue,
DlColorSpace colorspace)
: alpha_(alpha),
red_(red),
green_(green),
blue_(blue),
color_space_(colorspace) {}

/// @brief Construct a 32 bit color from floating point R, G, B, and A color
/// channels.
Expand All @@ -29,10 +58,7 @@ struct DlColor {
DlScalar r,
DlScalar g,
DlScalar b) {
return DlColor(toC(a) << 24 | //
toC(r) << 16 | //
toC(g) << 8 | //
toC(b));
return DlColor(a, r, g, b, DlColorSpace::kSRGB);
}

static constexpr uint8_t toAlpha(DlScalar opacity) { return toC(opacity); }
Expand Down Expand Up @@ -66,58 +92,106 @@ struct DlColor {
static constexpr DlColor kOrangeRed() {return DlColor(0xFFFF4500);};
// clang-format on

constexpr bool isOpaque() const { return getAlpha() == 0xFF; }
constexpr bool isTransparent() const { return getAlpha() == 0; }

constexpr int getAlpha() const { return argb_ >> 24; }
constexpr int getRed() const { return (argb_ >> 16) & 0xFF; }
constexpr int getGreen() const { return (argb_ >> 8) & 0xFF; }
constexpr int getBlue() const { return argb_ & 0xFF; }

constexpr DlScalar getAlphaF() const { return toF(getAlpha()); }
constexpr DlScalar getRedF() const { return toF(getRed()); }
constexpr DlScalar getGreenF() const { return toF(getGreen()); }
constexpr DlScalar getBlueF() const { return toF(getBlue()); }

constexpr uint32_t premultipliedArgb() const {
if (isOpaque()) {
return argb_;
}
DlScalar f = getAlphaF();
return (argb_ & 0xFF000000) | //
toC(getRedF() * f) << 16 | //
toC(getGreenF() * f) << 8 | //
toC(getBlueF() * f);
}
constexpr bool isOpaque() const { return alpha_ >= 1.f; }
constexpr bool isTransparent() const { return alpha_ <= 0.f; }

///\deprecated Use floating point accessors to avoid data loss when using wide
/// gamut colors.
constexpr int getAlpha() const { return toC(alpha_); }
///\deprecated Use floating point accessors to avoid data loss when using wide
/// gamut colors.
constexpr int getRed() const { return toC(red_); }
///\deprecated Use floating point accessors to avoid data loss when using wide
/// gamut colors.
constexpr int getGreen() const { return toC(green_); }
///\deprecated Use floating point accessors to avoid data loss when using wide
/// gamut colors.
constexpr int getBlue() const { return toC(blue_); }

constexpr DlScalar getAlphaF() const { return alpha_; }
constexpr DlScalar getRedF() const { return red_; }
constexpr DlScalar getGreenF() const { return green_; }
constexpr DlScalar getBlueF() const { return blue_; }

constexpr DlColorSpace getColorSpace() const { return color_space_; }

constexpr DlColor withAlpha(uint8_t alpha) const { //
return DlColor((argb_ & 0x00FFFFFF) | (alpha << 24));
return DlColor((argb() & 0x00FFFFFF) | (alpha << 24));
}
constexpr DlColor withRed(uint8_t red) const { //
return DlColor((argb_ & 0xFF00FFFF) | (red << 16));
return DlColor((argb() & 0xFF00FFFF) | (red << 16));
}
constexpr DlColor withGreen(uint8_t green) const { //
return DlColor((argb_ & 0xFFFF00FF) | (green << 8));
return DlColor((argb() & 0xFFFF00FF) | (green << 8));
}
constexpr DlColor withBlue(uint8_t blue) const { //
return DlColor((argb_ & 0xFFFFFF00) | (blue << 0));
return DlColor((argb() & 0xFFFFFF00) | (blue << 0));
}
constexpr DlColor withAlphaF(float alpha) const { //
return DlColor(alpha, red_, green_, blue_, color_space_);
}
constexpr DlColor withRedF(float red) const { //
return DlColor(alpha_, red, green_, blue_, color_space_);
}
constexpr DlColor withGreenF(float green) const { //
return DlColor(alpha_, red_, green, blue_, color_space_);
}
constexpr DlColor withBlueF(float blue) const { //
return DlColor(alpha_, red_, green_, blue, color_space_);
}
/// Performs a colorspace transformation.
///
/// This isn't just a replacement of the color space field, the new color
/// components are calculated.
DlColor withColorSpace(DlColorSpace color_space) const;

constexpr DlColor modulateOpacity(DlScalar opacity) const {
return opacity <= 0 ? withAlpha(0)
: opacity >= 1 ? *this
: withAlpha(round(getAlpha() * opacity));
}

constexpr uint32_t argb() const { return argb_; }
///\deprecated Use floating point accessors to avoid data loss when using wide
/// gamut colors.
constexpr uint32_t argb() const {
return toC(alpha_) << 24 | //
toC(red_) << 16 | //
toC(green_) << 8 | //
toC(blue_) << 0;
}

bool operator==(DlColor const& other) const { return argb_ == other.argb_; }
bool operator!=(DlColor const& other) const { return argb_ != other.argb_; }
bool operator==(uint32_t const& other) const { return argb_ == other; }
bool operator!=(uint32_t const& other) const { return argb_ != other; }
/// Checks that no difference in color components exceeds the delta.
///
/// This doesn't check against the actual distance between the colors in some
/// space.
bool isClose(DlColor const& other, DlScalar delta = 1.0f / 256.0f) {
return color_space_ == other.color_space_ &&
std::abs(alpha_ - other.alpha_) < delta &&
std::abs(red_ - other.red_) < delta &&
std::abs(green_ - other.green_) < delta &&
std::abs(blue_ - other.blue_) < delta;
}
bool operator==(DlColor const& other) const {
return alpha_ == other.alpha_ && red_ == other.red_ &&
green_ == other.green_ && blue_ == other.blue_ &&
color_space_ == other.color_space_;
}
bool operator!=(DlColor const& other) const {
return !this->operator==(other);
}
bool operator==(uint32_t const& other) const {
return argb() == other && color_space_ == DlColorSpace::kSRGB;
}
bool operator!=(uint32_t const& other) const {
return !this->operator==(other);
}

private:
uint32_t argb_;
DlScalar alpha_;
DlScalar red_;
DlScalar green_;
DlScalar blue_;
DlColorSpace color_space_;

static constexpr DlScalar toF(uint8_t comp) { return comp * (1.0f / 255); }
static constexpr uint8_t toC(DlScalar fComp) { return round(fComp * 255); }
Expand Down
Loading