Backed out changeset 09a6e2a57a02 (bug 1847503) for causing canvas failures on canvas-display-p3-drawImage.https.html

This commit is contained in:
Norisz Fay 2023-11-01 17:58:20 +02:00
parent d34ac63bb2
commit 458130d63a
8 changed files with 38 additions and 271 deletions

View File

@ -46,14 +46,16 @@ fuzzy(0-1,0-580) == anim-css-fill-3-from-by-ident-ident.svg anim-css-fill-3-ref.
fuzzy(0-1,0-580) == anim-css-fill-3-from-by-rgb-ident.svg anim-css-fill-3-ref.svg
# check handling of overflowing color values
# Gamut mapping is now available and presentation colors are not clipped
# any more. Because the result is dependent on hardware, we use a fairly high
# fuzzy for now.
# NOTE: Some of the tests below fail in Gecko because we compute
# "from + by" as the animation end-point, and we clamp that final color value
# (due to bug 515919) and use the clamped value for interpolation.
# That's earlier than the SVG spec wants us to clamp -- we're only supposed to
# clamp *final presentation values*.
# (Reference: SVG 1.1 Appendix F.4)
fuzzy(0-19,0-900) == anim-css-fill-overflow-1-by.svg anim-css-fill-overflow-1-ref.svg
fuzzy(0-19,0-900) == anim-css-fill-overflow-1-from-by.svg anim-css-fill-overflow-1-ref.svg
fuzzy(0-19,0-900) == anim-css-stopcolor-overflow-1-from-by.svg anim-css-stopcolor-overflow-1-ref.svg
fuzzy(0-19,0-1444) == anim-css-floodcolor-overflow-1-from-by.svg anim-css-floodcolor-overflow-1-ref.svg
== anim-css-fill-overflow-1-by.svg anim-css-fill-overflow-1-ref.svg
== anim-css-fill-overflow-1-from-by.svg anim-css-fill-overflow-1-ref.svg # bug 515919
== anim-css-stopcolor-overflow-1-from-by.svg anim-css-stopcolor-overflow-1-ref.svg # bug 515919
== anim-css-floodcolor-overflow-1-from-by.svg anim-css-floodcolor-overflow-1-ref.svg # bug 515919
# 'fill-opacity' property
fuzzy(0-1,0-885) == anim-css-fillopacity-1-by.svg anim-css-fillopacity-1-ref.svg

View File

@ -7,7 +7,8 @@
#include "mozilla/StyleColorInlines.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/ComputedStyleInlines.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsIFrame.h"
#include "nsStyleStruct.h"
@ -64,33 +65,16 @@ StyleAbsoluteColor StyleAbsoluteColor::ToColorSpace(
nscolor StyleAbsoluteColor::ToColor() const {
auto srgb = ToColorSpace(StyleColorSpace::Srgb);
constexpr float MIN = 0.0f;
constexpr float MAX = 1.0f;
// TODO(tlouw): Needs gamut mapping here. Right now we just hard clip the
// components to [0..1], which will yield invalid colors.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1626624
auto red = std::clamp(srgb.components._0, 0.0f, 1.0f);
auto green = std::clamp(srgb.components._1, 0.0f, 1.0f);
auto blue = std::clamp(srgb.components._2, 0.0f, 1.0f);
// We KNOW the values are in srgb so we can do a quick gamut limit check
// here and avoid calling into Servo_MapColorIntoGamutLimits and let it
// return early anyway.
auto isColorInGamut =
(srgb.components._0 >= MIN && srgb.components._0 <= MAX &&
srgb.components._1 >= MIN && srgb.components._1 <= MAX &&
srgb.components._2 >= MIN && srgb.components._2 <= MAX);
if (!isColorInGamut) {
if (StaticPrefs::layout_css_gamut_map_for_rendering_enabled()) {
srgb = Servo_MapColorIntoGamutLimits(&srgb);
} else {
// If gamut mapping is not enabled, we just naively clip the colors at
// sRGB gamut limits. This will go away completely when gamut mapping is
// enabled.
srgb.components._0 = std::clamp(srgb.components._0, 0.0f, 1.0f);
srgb.components._1 = std::clamp(srgb.components._1, 0.0f, 1.0f);
srgb.components._2 = std::clamp(srgb.components._2, 0.0f, 1.0f);
}
}
return NS_RGBA(nsStyleUtil::FloatToColorComponent(srgb.components._0),
nsStyleUtil::FloatToColorComponent(srgb.components._1),
nsStyleUtil::FloatToColorComponent(srgb.components._2),
return NS_RGBA(nsStyleUtil::FloatToColorComponent(red),
nsStyleUtil::FloatToColorComponent(green),
nsStyleUtil::FloatToColorComponent(blue),
nsStyleUtil::FloatToColorComponent(srgb.alpha));
}

View File

@ -8413,12 +8413,6 @@
value: 3
mirror: always
# Should colors that are out of gamut for sRGB mapped to within limits?
- name: layout.css.gamut-map-for-rendering.enabled
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
# Is support for GeometryUtils.getBoxQuads enabled?
- name: layout.css.getBoxQuads.enabled
type: bool

View File

@ -1,196 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Gamut mapping.
//! <https://drafts.csswg.org/css-color-4/#gamut-mapping>
use super::{AbsoluteColor, ColorSpace};
impl AbsoluteColor {
/// Map the components of this color into it's gamut limits if needed.
/// <https://drafts.csswg.org/css-color-4/#binsearch>
pub fn map_into_gamut_limits(&self) -> Self {
// 1. if destination has no gamut limits (XYZ-D65, XYZ-D50, Lab, LCH,
// Oklab, Oklch) return origin.
if matches!(
self.color_space,
ColorSpace::Lab |
ColorSpace::Lch |
ColorSpace::Oklab |
ColorSpace::Oklch |
ColorSpace::XyzD50 |
ColorSpace::XyzD65
) {
return self.clone();
}
// If the color does have gamut limits, then we can check it here and
// possibly skip the binary search.
if self.in_gamut() {
return self.clone();
}
// 2. let origin_Oklch be origin converted from origin color space to
// the Oklch color space.
let origin_oklch = self.to_color_space(ColorSpace::Oklch);
// 3. if the Lightness of origin_Oklch is greater than or equal to
// 100%, return { 1 1 1 origin.alpha } in destination.
if origin_oklch.components.1 >= 1.0 {
return AbsoluteColor::new(self.color_space, 1.0, 1.0, 1.0, self.alpha);
}
// 4. if the Lightness of origin_Oklch is less than than or equal to
// 0%, return { 0 0 0 origin.alpha } in destination.
if origin_oklch.components.1 <= 0.0 {
return AbsoluteColor::new(self.color_space, 0.0, 0.0, 0.0, self.alpha);
}
// 5. let inGamut(color) be a function which returns true if, when
// passed a color, that color is inside the gamut of destination.
// For HSL and HWB, it returns true if the color is inside the gamut
// of sRGB.
// See [`Color::in_gamut`].
// 6. if inGamut(origin_Oklch) is true, convert origin_Oklch to
// destination and return it as the gamut mapped color.
// We already checked if the color is in gamut limits above.
// 7. otherwise, let delta(one, two) be a function which returns the
// deltaEOK of color one compared to color two.
// See the [`delta_eok`] function.
// 8. let JND be 0.02
const JND: f32 = 0.02;
// 9. let epsilon be 0.0001
const EPSILON: f32 = 0.0001;
// 10. let clip(color) be a function which converts color to
// destination, converts all negative components to zero, converts
// all components greater that one to one, and returns the result.
// See [`Color::clip`].
// 11. set min to zero
let mut min = 0.0;
// 12. set max to the Oklch chroma of origin_Oklch.
let mut max = origin_oklch.components.1;
// 13. let min_inGamut be a boolean that represents when min is still
// in gamut, and set it to true
let mut min_in_gamut = true;
let mut current = origin_oklch.clone();
let mut current_in_space = self.clone();
// If we are already within the JND threshold, return the clipped color
// and skip the binary search.
let clipped = current_in_space.clip();
if delta_eok(&clipped, &current) < JND {
return clipped;
}
// 14. while (max - min is greater than epsilon) repeat the following
// steps.
while max - min > EPSILON {
// 14.1. set chroma to (min + max) / 2
let chroma = (min + max) / 2.0;
// 14.2. set current to origin_Oklch and then set the chroma
// component to chroma
current.components.1 = chroma;
current_in_space = current.to_color_space(self.color_space);
// 14.3. if min_inGamut is true and also if inGamut(current) is
// true, set min to chroma and continue to repeat these steps.
if min_in_gamut && current_in_space.in_gamut() {
min = chroma;
continue;
}
// 14.4. otherwise, if inGamut(current) is false carry out these
// steps:
// 14.4.1. set clipped to clip(current)
let clipped = current_in_space.clip();
// 14.4.2. set E to delta(clipped, current)
let e = delta_eok(&clipped, &current);
// 14.4.3. if E < JND
if e < JND {
// 14.4.3.1. if (JND - E < epsilon) return clipped as the gamut
// mapped color
if JND - e < EPSILON {
return clipped;
}
// 14.4.3.2. otherwise
// 14.4.3.2.1. set min_inGamut to false
min_in_gamut = false;
// 14.4.3.2.2. set min to chroma
min = chroma;
} else {
// 14.4.4. otherwise, set max to chroma and continue to repeat
// these steps
max = chroma;
}
}
// 15. return current as the gamut mapped color current
current_in_space
}
/// Clamp this color to within the [0..1] range.
fn clip(&self) -> Self {
let mut result = self.clone();
result.components = result.components.map(|c| c.clamp(0.0, 1.0));
result
}
/// Returns true if this color is within its gamut limits.
fn in_gamut(&self) -> bool {
macro_rules! in_range {
($c:expr) => {{
$c >= 0.0 && $c <= 1.0
}};
}
match self.color_space {
ColorSpace::Hsl | ColorSpace::Hwb => self.to_color_space(ColorSpace::Srgb).in_gamut(),
ColorSpace::Srgb |
ColorSpace::SrgbLinear |
ColorSpace::DisplayP3 |
ColorSpace::A98Rgb |
ColorSpace::ProphotoRgb |
ColorSpace::Rec2020 => {
in_range!(self.components.0) &&
in_range!(self.components.1) &&
in_range!(self.components.2)
},
ColorSpace::Lab |
ColorSpace::Lch |
ColorSpace::Oklab |
ColorSpace::Oklch |
ColorSpace::XyzD50 |
ColorSpace::XyzD65 => true,
}
}
}
/// Calculate deltaE OK (simple root sum of squares).
/// <https://drafts.csswg.org/css-color-4/#color-difference-OK>
fn delta_eok(reference: &AbsoluteColor, sample: &AbsoluteColor) -> f32 {
// Delta is calculated in the oklab color space.
let reference = reference.to_color_space(ColorSpace::Oklab);
let sample = sample.to_color_space(ColorSpace::Oklab);
let diff = reference.components - sample.components;
let diff = diff * diff;
(diff.0 + diff.1 + diff.2).sqrt()
}

View File

@ -9,8 +9,6 @@ pub mod convert;
pub mod mix;
pub mod parsing;
mod gamut;
use cssparser::color::PredefinedColorSpace;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
@ -28,14 +26,6 @@ impl ColorComponents {
}
}
impl std::ops::Sub for ColorComponents {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0, self.1 - rhs.1, self.2 - rhs.2)
}
}
impl std::ops::Mul for ColorComponents {
type Output = Self;

View File

@ -7855,13 +7855,6 @@ pub extern "C" fn Servo_ConvertColorSpace(
color.to_color_space(color_space)
}
#[no_mangle]
pub extern "C" fn Servo_MapColorIntoGamutLimits(
color: &AbsoluteColor
) -> AbsoluteColor {
color.map_into_gamut_limits()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_IntersectionObserverRootMargin_Parse(
value: &nsACString,

View File

@ -9,15 +9,15 @@ div { color: black; }
<body>
<script>
const TEST_CASES = [
["red", "green", "xyz", "color(xyz 0.24479 0.183508 0.0225301)"],
["red", "green", "lab", "lab(50.2841 16.6263 59.2386)"],
["red", "green", "lch", "lch(50.2841 87.4108 87.6208)"],
["red", "green 90%", "xyz", "color(xyz 0.110709 0.160203 0.0250896)"],
["red", "green 90%", "lab", "lab(47.079 -34.7167 50.7168)"],
["red", "green 90%", "lch", "lch(47.079 71.8696 125.031)"],
["red 90%", "green", "xyz", "color(xyz 0.378871 0.206813 0.0199707)"],
["red 90%", "green", "lab", "lab(53.4893 67.9692 67.7605)"],
["red 90%", "green", "lch", "lch(53.4893 102.952 50.2103)"],
["red", "green", "xyz", "rgb(188, 92, 0)"],
["red", "green", "lab", "rgb(161, 108, 0)"],
["red", "green", "lch", "rgb(145, 116, 0)"],
["red", "green 90%", "xyz", "rgb(89, 122, 0)"],
["red", "green 90%", "lab", "rgb(65, 126, 0)"],
["red", "green 90%", "lch", "rgb(49, 128, 0)"],
["red 90%", "green", "xyz", "rgb(243, 40, 0)"],
["red 90%", "green", "lab", "rgb(237, 55, 0)"],
["red 90%", "green", "lch", "rgb(235, 59, 0)"],
];
for (let [from, to, space, expected] of TEST_CASES) {

View File

@ -12,15 +12,15 @@ div { color: black; }
<body>
<script>
const TEST_CASES = [
["red", "green", "xyz", "color(xyz 0.24479 0.183508 0.0225301)"],
["red", "green", "lab", "lab(50.2841 16.6263 59.2386)"],
["red", "green", "lch", "lch(50.2841 87.4108 87.6208)"],
["red", "green 90%", "xyz", "color(xyz 0.110709 0.160203 0.0250896)"],
["red", "green 90%", "lab", "lab(47.079 -34.7167 50.7168)"],
["red", "green 90%", "lch", "lch(47.079 71.8696 125.031)"],
["red 90%", "green", "xyz", "color(xyz 0.378871 0.206813 0.0199707)"],
["red 90%", "green", "lab", "lab(53.4893 67.9692 67.7605)"],
["red 90%", "green", "lch", "lch(53.4893 102.952 50.2103)"],
["red", "green", "xyz", "rgb(188, 92, 0)"],
["red", "green", "lab", "rgb(161, 108, 0)"],
["red", "green", "lch", "rgb(145, 116, 0)"],
["red", "green 90%", "xyz", "rgb(89, 122, 0)"],
["red", "green 90%", "lab", "rgb(65, 126, 0)"],
["red", "green 90%", "lch", "rgb(49, 128, 0)"],
["red 90%", "green", "xyz", "rgb(243, 40, 0)"],
["red 90%", "green", "lab", "rgb(237, 55, 0)"],
["red 90%", "green", "lch", "rgb(235, 59, 0)"],
];
for (let [from, to, space, expected] of TEST_CASES) {