Web Inspector: Provide UI to convert between sRGB and p3 color spaces
https://bugs.webkit.org/show_bug.cgi?id=203534
<rdar://problem/56688523>
Reviewed by Devin Rousso.
Source/WebInspectorUI:
Add context menus:
- "Convert to sRGB" and "Clamp to sRGB" for p3 colors, such as `color(display-p3 0 1 0)`.
- "Convert to Display-P3" for sRGB colors, such as `rgb(0, 255, 0)`.
Shift-clicking the color swatch of sRGB colors now goes through the color function syntax as well.
Shift-clicking the color swatch of Display-P3 colors converts the color to sRGB when it can be lossless.
When the convertion cannot be lossless, Web Inspector beeps.
* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Base/Utilities.js:
* UserInterface/Models/Color.js:
(WI.Color):
Introduce `_normalizedRGB` property, which stores rgb values from 0 to 1.
Previously, `_rgba` stored values from 0 to 1 for color function format, and from 0 to 255 otherwise.
That required format checks before every `rgb` value access and resulted in silent errors when
the values were in the wrong format.
Store alpha as a separate property to simplify format conversion. Previously, alpha was duplicated between `_rgba` and `_hsla`.
(WI.Color.displayP3toSRGB):
(WI.Color.srgbToDisplayP3): Added.
(WI.Color.prototype.nextFormat):
(WI.Color.prototype.get rgb):
(WI.Color.prototype.get hsl):
(WI.Color.prototype.get normalizedRGB):
(WI.Color.prototype.get rgba):
(WI.Color.prototype.get hsla):
(WI.Color.prototype.get normalizedRGBA):
(WI.Color.prototype.get gamut):
(WI.Color.prototype.set gamut):
(WI.Color.prototype.copy):
(WI.Color.prototype.isKeyword):
(WI.Color.prototype.isOutsideSRGB): Added.
(WI.Color.prototype.canBeSerializedAsShortHEX):
(WI.Color.prototype._toKeywordString):
(WI.Color.prototype._toShortHEXString):
(WI.Color.prototype._toHEXString):
(WI.Color.prototype._toShortHEXAlphaString):
(WI.Color.prototype._toHEXAlphaString):
(WI.Color.prototype._toRGBString):
(WI.Color.prototype._toRGBAString):
(WI.Color.prototype._toFunctionString):
Limit the values to 4 decimals.
(WI.Color.prototype._toHSLString):
(WI.Color.prototype._toHSLAString):
(WI.Color.prototype._hslToRGB):
* UserInterface/Views/ColorPicker.js:
(WI.ColorPicker.prototype._updateColor):
(WI.ColorPicker.prototype._updateOpacitySlider):
* UserInterface/Views/ColorSquare.css:
(.color-square > .svg-root):
(.color-square > .svg-root > .srgb-edge):
(.color-square > .srgb-label):
(.color-square > .srgb-label:hover):
(.color-square > .srgb-label:hover + .svg-root > .srgb-edge):
(@media (-webkit-device-pixel-ratio: 1)):
(.color-square > .srgb-edge):
* UserInterface/Views/ColorSquare.js:
(WI.ColorSquare.prototype.set tintedColor):
(WI.ColorSquare.prototype._drawSRGBOutline):
(WI.ColorSquare):
* UserInterface/Views/InlineSwatch.js:
(WI.InlineSwatch):
(WI.InlineSwatch.prototype._updateSwatch):
(WI.InlineSwatch.prototype._allowShiftClickColor): Added.
(WI.InlineSwatch.prototype._handleContextMenuEvent):
LayoutTests:
* inspector/model/color-expected.txt:
* inspector/model/color.html:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@253018 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 1186d2d..8bbec85 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,14 @@
+2019-12-02 Nikita Vasilyev <nvasilyev@apple.com>
+
+ Web Inspector: Provide UI to convert between sRGB and p3 color spaces
+ https://bugs.webkit.org/show_bug.cgi?id=203534
+ <rdar://problem/56688523>
+
+ Reviewed by Devin Rousso.
+
+ * inspector/model/color-expected.txt:
+ * inspector/model/color.html:
+
2019-12-02 Said Abou-Hallawa <sabouhallawa@apple.com>
Crash when animating an enum attribute for multiple instances of an SVG element
diff --git a/LayoutTests/inspector/model/color-expected.txt b/LayoutTests/inspector/model/color-expected.txt
index 89267b9..93cf93a 100644
--- a/LayoutTests/inspector/model/color-expected.txt
+++ b/LayoutTests/inspector/model/color-expected.txt
@@ -396,12 +396,12 @@
PASS: '#11223345' should not be serializable as a short Hex
PASS: 'color(display-p3 0 1 0.5)' color should have a default alpha of 1
PASS: color function is not a keyword
-PASS: 'color(display-p3 0 1 0.5)' has rgb of [0, 1, 0.5]
-PASS: 'color(display-p3 0 1 0.5)' has rgba of [0, 1, 0.5, 1]
+PASS: 'color(display-p3 0 1 0.5)' has normalized RGB of [0, 1, 0.5]
+PASS: 'color(display-p3 0 1 0.5)' has normalized RGBA of [0, 1, 0.5, 1]
PASS: 'color(display-p3 0 1 0.5 / 0.3)' should have an alpha of 0.3
PASS: color function is not a keyword
-PASS: 'color(display-p3 0 1 0.5 / 0.3)' has rgb of [0, 1, 0.5]
-PASS: 'color(display-p3 0 1 0.5 / 0.3)' has rgba of [0, 1, 0.5, 0.3]
+PASS: 'color(display-p3 0 1 0.5 / 0.3)' has normalized RGB of [0, 1, 0.5]
+PASS: 'color(display-p3 0 1 0.5 / 0.3)' has normalized RGBA of [0, 1, 0.5, 0.3]
-- Running test case: WI.Color from components
Check components for color 'rgb(255, 0, 0)'.
@@ -520,10 +520,26 @@
-- Running test case: WI.Color.displayP3toSRGB
PASS: Should convert [0,0,0] to [0,0,0].
-PASS: Should convert [1,1,1] to [0.9999300658875597,1.0000101408119602,1.0001054916006267].
-PASS: Should convert [1,0,0] to [1.0929902391315327,-0.5433883521407902,-0.2537782522695441].
-PASS: Should convert [0,1,0] to [-2.9057643199691516,1.0182759798644396,-1.0162215225472337].
-PASS: Should convert [0,0,1] to [2.3902361583783006e-7,-2.0487738563140788e-7,1.0421313171983129].
-PASS: Should convert [2,0,0] to [1.0929902391315327,-0.5433883521407902,-0.2537782522695441].
+PASS: Should convert [1,1,1] to [0.9999,1,1.0001].
+PASS: Should convert [1,0,0] to [1.093,-0.5434,-0.2538].
+PASS: Should convert [0,1,0] to [-2.9058,1.0183,-1.0162].
+PASS: Should convert [0,0,1] to [0,0,1.0421].
+PASS: Should convert [2,0,0] to [1.093,-0.5434,-0.2538].
PASS: Should convert [-1,0,0] to [0,0,0].
+-- Running test case: WI.Color.srgbToDisplayP3
+PASS: Should convert [0,0,0] to [0,0,0].
+PASS: Should convert [1,1,1] to [1.0001,1,0.9999].
+PASS: Should convert [1,0,0] to [0.9176,0.2003,0.1386].
+PASS: Should convert [0,1,0] to [0.4584,0.9853,0.2983].
+PASS: Should convert [0,0,1] to [0,0,0.9595].
+PASS: Should convert [2,0,0] to [0.9176,0.2003,0.1386].
+PASS: Should convert [-1,0,0] to [0,0,0].
+
+-- Running test case: WI.Color.isOutsideSRGB
+"color(display-p3 0 0 0)" is inside sRGB.
+"color(display-p3 1 1 1)" is inside sRGB.
+"color(display-p3 0.04 0.14 0.016)" is inside sRGB.
+"color(display-p3 1 0 0)" is outside sRGB.
+"color(display-p3 0.93 0.353 0.353)" is outside sRGB.
+
diff --git a/LayoutTests/inspector/model/color.html b/LayoutTests/inspector/model/color.html
index 1dd4b6d..040d2f7 100644
--- a/LayoutTests/inspector/model/color.html
+++ b/LayoutTests/inspector/model/color.html
@@ -323,14 +323,14 @@
color = WI.Color.fromString("color(display-p3 0 1 0.5)");
InspectorTest.expectThat(color.alpha === 1, "'color(display-p3 0 1 0.5)' color should have a default alpha of 1");
InspectorTest.expectThat(color.isKeyword() === false, "color function is not a keyword");
- InspectorTest.expectShallowEqual(color.rgb, [0, 1, 0.5], "'color(display-p3 0 1 0.5)' has rgb of [0, 1, 0.5]");
- InspectorTest.expectShallowEqual(color.rgba, [0, 1, 0.5, 1], "'color(display-p3 0 1 0.5)' has rgba of [0, 1, 0.5, 1]");
+ InspectorTest.expectShallowEqual(color.normalizedRGB, [0, 1, 0.5], "'color(display-p3 0 1 0.5)' has normalized RGB of [0, 1, 0.5]");
+ InspectorTest.expectShallowEqual(color.normalizedRGBA, [0, 1, 0.5, 1], "'color(display-p3 0 1 0.5)' has normalized RGBA of [0, 1, 0.5, 1]");
color = WI.Color.fromString("color(display-p3 0 1 0.5 / 0.3)");
InspectorTest.expectThat(color.alpha === 0.3, "'color(display-p3 0 1 0.5 / 0.3)' should have an alpha of 0.3");
InspectorTest.expectThat(color.isKeyword() === false, "color function is not a keyword");
- InspectorTest.expectShallowEqual(color.rgb, [0, 1, 0.5], "'color(display-p3 0 1 0.5 / 0.3)' has rgb of [0, 1, 0.5]");
- InspectorTest.expectShallowEqual(color.rgba, [0, 1, 0.5, 0.3], "'color(display-p3 0 1 0.5 / 0.3)' has rgba of [0, 1, 0.5, 0.3]");
+ InspectorTest.expectShallowEqual(color.normalizedRGB, [0, 1, 0.5], "'color(display-p3 0 1 0.5 / 0.3)' has normalized RGB of [0, 1, 0.5]");
+ InspectorTest.expectShallowEqual(color.normalizedRGBA, [0, 1, 0.5, 0.3], "'color(display-p3 0 1 0.5 / 0.3)' has normalized RGBA of [0, 1, 0.5, 0.3]");
return true;
}
@@ -402,6 +402,7 @@
// All with alpha.
test("transparent", [
WI.Color.Format.RGBA,
+ WI.Color.Format.ColorFunction,
WI.Color.Format.HSLA,
WI.Color.Format.Keyword,
WI.Color.Format.ShortHEXAlpha,
@@ -411,6 +412,7 @@
// All without alpha.
test("red", [
WI.Color.Format.RGB,
+ WI.Color.Format.ColorFunction,
WI.Color.Format.HSL,
WI.Color.Format.Keyword,
WI.Color.Format.ShortHEX,
@@ -420,6 +422,7 @@
// No short hex or keyword.
test("rgb(100, 150, 200)", [
WI.Color.Format.RGB,
+ WI.Color.Format.ColorFunction,
WI.Color.Format.HSL,
WI.Color.Format.HEX,
]);
@@ -427,6 +430,7 @@
// No short hex alpha or keyword.
test("rgba(100, 150, 200, 0.5)", [
WI.Color.Format.RGBA,
+ WI.Color.Format.ColorFunction,
WI.Color.Format.HSLA,
WI.Color.Format.HEXAlpha,
]);
@@ -617,19 +621,57 @@
description: "Test conversion from display-P3 gamut to sRGB.",
test() {
testColorConversion(WI.Color.displayP3toSRGB, [0, 0, 0], [0, 0, 0]);
- testColorConversion(WI.Color.displayP3toSRGB, [1, 1, 1], [0.9999300658875597, 1.0000101408119602, 1.0001054916006267]);
- testColorConversion(WI.Color.displayP3toSRGB, [1, 0, 0], [1.0929902391315327, -0.5433883521407902, -0.2537782522695441]);
- testColorConversion(WI.Color.displayP3toSRGB, [0, 1, 0], [-2.9057643199691516, 1.0182759798644396, -1.0162215225472337]);
- testColorConversion(WI.Color.displayP3toSRGB, [0, 0, 1], [2.3902361583783006e-7, -2.0487738563140788e-7, 1.0421313171983129]);
+ testColorConversion(WI.Color.displayP3toSRGB, [1, 1, 1], [0.9999, 1, 1.0001]);
+ testColorConversion(WI.Color.displayP3toSRGB, [1, 0, 0], [1.093, -0.5434, -0.2538]);
+ testColorConversion(WI.Color.displayP3toSRGB, [0, 1, 0], [-2.9058, 1.0183, -1.0162]);
+ testColorConversion(WI.Color.displayP3toSRGB, [0, 0, 1], [0, 0, 1.0421]);
// Out-of-bounds inputs.
- testColorConversion(WI.Color.displayP3toSRGB, [2, 0, 0], [1.0929902391315327, -0.5433883521407902, -0.2537782522695441]);
+ testColorConversion(WI.Color.displayP3toSRGB, [2, 0, 0], [1.093, -0.5434, -0.2538]);
testColorConversion(WI.Color.displayP3toSRGB, [-1, 0, 0], [0, 0, 0]);
return true;
}
});
+ suite.addTestCase({
+ name: "WI.Color.srgbToDisplayP3",
+ description: "Test conversion from sRGB gamut to display-P3.",
+ test() {
+ testColorConversion(WI.Color.srgbToDisplayP3, [0, 0, 0], [0, 0, 0]);
+ testColorConversion(WI.Color.srgbToDisplayP3, [1, 1, 1], [1.0001, 1, 0.9999]);
+ testColorConversion(WI.Color.srgbToDisplayP3, [1, 0, 0], [0.9176, 0.2003, 0.1386]);
+ testColorConversion(WI.Color.srgbToDisplayP3, [0, 1, 0], [0.4584, 0.9853, 0.2983]);
+ testColorConversion(WI.Color.srgbToDisplayP3, [0, 0, 1], [0, 0, 0.9595]);
+
+ // Out-of-bounds inputs.
+ testColorConversion(WI.Color.srgbToDisplayP3, [2, 0, 0], [0.9176, 0.2003, 0.1386]);
+ testColorConversion(WI.Color.srgbToDisplayP3, [-1, 0, 0], [0, 0, 0]);
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "WI.Color.isOutsideSRGB",
+ description: "Test conversion from sRGB gamut to display-P3.",
+ test() {
+ function test(string) {
+ let color = WI.Color.fromString(string);
+ let result = color.isOutsideSRGB();
+ InspectorTest.log(`"${string}" is ${result ? "outside" : "inside"} sRGB.`);
+ }
+
+ test("color(display-p3 0 0 0)");
+ test("color(display-p3 1 1 1)");
+ test("color(display-p3 0.04 0.14 0.016)"); // Barely inside sRGB. Values chosen to test rounding behavior.
+ test("color(display-p3 1 0 0)");
+ test("color(display-p3 0.93 0.353 0.353)"); // Barely outside sRGB.
+
+ return true;
+ }
+ });
+
suite.runTestCasesAndFinish();
}
</script>
diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog
index 9b37f79..5e1da34 100644
--- a/Source/WebInspectorUI/ChangeLog
+++ b/Source/WebInspectorUI/ChangeLog
@@ -1,3 +1,80 @@
+2019-12-02 Nikita Vasilyev <nvasilyev@apple.com>
+
+ Web Inspector: Provide UI to convert between sRGB and p3 color spaces
+ https://bugs.webkit.org/show_bug.cgi?id=203534
+ <rdar://problem/56688523>
+
+ Reviewed by Devin Rousso.
+
+ Add context menus:
+ - "Convert to sRGB" and "Clamp to sRGB" for p3 colors, such as `color(display-p3 0 1 0)`.
+ - "Convert to Display-P3" for sRGB colors, such as `rgb(0, 255, 0)`.
+
+ Shift-clicking the color swatch of sRGB colors now goes through the color function syntax as well.
+ Shift-clicking the color swatch of Display-P3 colors converts the color to sRGB when it can be lossless.
+ When the convertion cannot be lossless, Web Inspector beeps.
+
+ * Localizations/en.lproj/localizedStrings.js:
+ * UserInterface/Base/Utilities.js:
+ * UserInterface/Models/Color.js:
+ (WI.Color):
+ Introduce `_normalizedRGB` property, which stores rgb values from 0 to 1.
+ Previously, `_rgba` stored values from 0 to 1 for color function format, and from 0 to 255 otherwise.
+ That required format checks before every `rgb` value access and resulted in silent errors when
+ the values were in the wrong format.
+
+ Store alpha as a separate property to simplify format conversion. Previously, alpha was duplicated between `_rgba` and `_hsla`.
+
+ (WI.Color.displayP3toSRGB):
+ (WI.Color.srgbToDisplayP3): Added.
+ (WI.Color.prototype.nextFormat):
+ (WI.Color.prototype.get rgb):
+ (WI.Color.prototype.get hsl):
+ (WI.Color.prototype.get normalizedRGB):
+ (WI.Color.prototype.get rgba):
+ (WI.Color.prototype.get hsla):
+ (WI.Color.prototype.get normalizedRGBA):
+ (WI.Color.prototype.get gamut):
+ (WI.Color.prototype.set gamut):
+ (WI.Color.prototype.copy):
+ (WI.Color.prototype.isKeyword):
+ (WI.Color.prototype.isOutsideSRGB): Added.
+ (WI.Color.prototype.canBeSerializedAsShortHEX):
+ (WI.Color.prototype._toKeywordString):
+ (WI.Color.prototype._toShortHEXString):
+ (WI.Color.prototype._toHEXString):
+ (WI.Color.prototype._toShortHEXAlphaString):
+ (WI.Color.prototype._toHEXAlphaString):
+ (WI.Color.prototype._toRGBString):
+ (WI.Color.prototype._toRGBAString):
+ (WI.Color.prototype._toFunctionString):
+ Limit the values to 4 decimals.
+
+ (WI.Color.prototype._toHSLString):
+ (WI.Color.prototype._toHSLAString):
+ (WI.Color.prototype._hslToRGB):
+ * UserInterface/Views/ColorPicker.js:
+ (WI.ColorPicker.prototype._updateColor):
+ (WI.ColorPicker.prototype._updateOpacitySlider):
+ * UserInterface/Views/ColorSquare.css:
+ (.color-square > .svg-root):
+ (.color-square > .svg-root > .srgb-edge):
+ (.color-square > .srgb-label):
+ (.color-square > .srgb-label:hover):
+ (.color-square > .srgb-label:hover + .svg-root > .srgb-edge):
+ (@media (-webkit-device-pixel-ratio: 1)):
+ (.color-square > .srgb-edge):
+ * UserInterface/Views/ColorSquare.js:
+ (WI.ColorSquare.prototype.set tintedColor):
+ (WI.ColorSquare.prototype._drawSRGBOutline):
+ (WI.ColorSquare):
+
+ * UserInterface/Views/InlineSwatch.js:
+ (WI.InlineSwatch):
+ (WI.InlineSwatch.prototype._updateSwatch):
+ (WI.InlineSwatch.prototype._allowShiftClickColor): Added.
+ (WI.InlineSwatch.prototype._handleContextMenuEvent):
+
2019-12-02 Devin Rousso <drousso@apple.com>
Web Inspector: Console: the saved result value is still shown after page reload
diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
index 9c49ad7..d3ca407 100644
--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
@@ -240,6 +240,7 @@
localizedStrings["Child added to "] = "Child added to ";
localizedStrings["Children"] = "Children";
localizedStrings["Cipher"] = "Cipher";
+localizedStrings["Clamp to sRGB"] = "Clamp to sRGB";
localizedStrings["Classes"] = "Classes";
localizedStrings["Clear Filters"] = "Clear Filters";
localizedStrings["Clear Log"] = "Clear Log";
@@ -254,6 +255,7 @@
localizedStrings["Click to close this tab"] = "Click to close this tab";
localizedStrings["Click to create a Local Override from this content"] = "Click to create a Local Override from this content";
localizedStrings["Click to import a file and create a Local Override\nShift-click to create a Local Override from this content"] = "Click to import a file and create a Local Override\nShift-click to create a Local Override from this content";
+localizedStrings["Click to select a color"] = "Click to select a color";
localizedStrings["Click to select a color\nShift-click to switch color formats"] = "Click to select a color\nShift-click to switch color formats";
localizedStrings["Click to view variable value\nShift-click to replace variable with value"] = "Click to view variable value\nShift-click to replace variable with value";
localizedStrings["Clickable"] = "Clickable";
@@ -304,6 +306,8 @@
localizedStrings["Continue to Here"] = "Continue to Here";
localizedStrings["Continue without automatically stopping"] = "Continue without automatically stopping";
localizedStrings["Controls"] = "Controls";
+localizedStrings["Convert to Display-P3"] = "Convert to Display-P3";
+localizedStrings["Convert to sRGB"] = "Convert to sRGB";
localizedStrings["Cookies"] = "Cookies";
localizedStrings["Copy"] = "Copy";
localizedStrings["Copy Action"] = "Copy Action";
@@ -407,6 +411,8 @@
localizedStrings["Dynamically calculated for the parent element"] = "Dynamically calculated for the parent element";
localizedStrings["Dynamically calculated for the selected element"] = "Dynamically calculated for the selected element";
localizedStrings["Dynamically calculated for the selected element and did not match"] = "Dynamically calculated for the selected element and did not match";
+/* Label for a guide within the color picker */
+localizedStrings["Edge of sRGB color space"] = "Edge of sRGB color space";
localizedStrings["Edit"] = "Edit";
localizedStrings["Edit Breakpoint\u2026"] = "Edit Breakpoint\u2026";
localizedStrings["Edit Local Override\u2026"] = "Edit Local Override\u2026";
@@ -540,6 +546,7 @@
localizedStrings["Forced Layout"] = "Forced Layout";
/* A context menu item to force (override) a DOM node's pseudo-classes */
localizedStrings["Forced Pseudo-Classes"] = "Forced Pseudo-Classes";
+localizedStrings["Format: Color Function"] = "Format: Color Function";
localizedStrings["Format: HSL"] = "Format: HSL";
localizedStrings["Format: HSLA"] = "Format: HSLA";
localizedStrings["Format: Hex"] = "Format: Hex";
@@ -1175,8 +1182,6 @@
localizedStrings["This is what the result of a test that threw an error with no data looks like."] = "This is what the result of a test that threw an error with no data looks like.";
localizedStrings["This is what the result of a warning test with no data looks like."] = "This is what the result of a warning test with no data looks like.";
localizedStrings["This is what the result of an unsupported test with no data looks like."] = "This is what the result of an unsupported test with no data looks like.";
-/* Label for a guide within the color picker */
-localizedStrings["This line marks the edge of sRGB color space"] = "This line marks the edge of sRGB color space";
localizedStrings["This object is a root"] = "This object is a root";
localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects";
localizedStrings["This test will pass with a variety of accessibility information about the <body> element."] = "This test will pass with a variety of accessibility information about the <body> element.";
diff --git a/Source/WebInspectorUI/UserInterface/Base/Utilities.js b/Source/WebInspectorUI/UserInterface/Base/Utilities.js
index f88c562..0459622 100644
--- a/Source/WebInspectorUI/UserInterface/Base/Utilities.js
+++ b/Source/WebInspectorUI/UserInterface/Base/Utilities.js
@@ -1211,6 +1211,25 @@
}
});
+// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web#Multiplying_a_matrix_and_a_point
+Object.defineProperty(Math, "multiplyMatrixByVector",
+{
+ value(matrix, vector)
+ {
+ let height = matrix.length;
+ let width = matrix[0].length;
+ console.assert(width === vector.length);
+
+ let result = Array(width).fill(0);
+ for (let i = 0; i < width; ++i) {
+ for (let rowIndex = 0; rowIndex < height; ++rowIndex)
+ result[i] += vector[rowIndex] * matrix[i][rowIndex];
+ }
+
+ return result;
+ }
+});
+
Object.defineProperty(Number, "constrain",
{
value(num, min, max)
diff --git a/Source/WebInspectorUI/UserInterface/Models/Color.js b/Source/WebInspectorUI/UserInterface/Models/Color.js
index bb5e4d8..fbb3320 100644
--- a/Source/WebInspectorUI/UserInterface/Models/Color.js
+++ b/Source/WebInspectorUI/UserInterface/Models/Color.js
@@ -34,15 +34,21 @@
this.format = format;
console.assert(gamut === undefined || Object.values(WI.Color.Gamut).includes(gamut));
- this.gamut = gamut || WI.Color.Gamut.SRGB;
+ this._gamut = gamut || WI.Color.Gamut.SRGB;
- if (components.length === 3)
- components.push(1);
+ console.assert(components.length === 3 || components.length === 4, components);
+ this.alpha = components.length === 4 ? components[3] : 1;
+
+ this._rgb = null;
+ this._normalizedRGB = null;
+ this._hsl = null;
if (format === WI.Color.Format.HSL || format === WI.Color.Format.HSLA)
- this._hsla = components;
+ this._hsl = components.slice(0, 3);
+ else if (format === WI.Color.Format.ColorFunction)
+ this._normalizedRGB = components.slice(0, 3);
else
- this._rgba = components;
+ this._rgb = components.slice(0, 3);
this.valid = !components.some(isNaN);
}
@@ -357,42 +363,60 @@
let linearP3 = WI.Color._toLinearLight([r, g, b]);
- // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web#Multiplying_a_matrix_and_a_point
- function multiplyMatrixByVector(matrix, vector) {
- let height = matrix.length;
- let width = matrix[0].length;
- console.assert(width === vector.length);
-
- let result = Array(width).fill(0);
- for (let i = 0; i < width; i++)
- for (let rowIndex = 0; rowIndex < height; rowIndex++)
- result[i] += vector[rowIndex] * matrix[i][rowIndex];
-
- return result;
- }
-
// Convert an array of linear-light display-p3 values to CIE XYZ
// using D65 (no chromatic adaptation).
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
- let matrix = [
+ const rgbToXYZMatrix = [
[0.4865709486482162, 0.26566769316909306, 0.1982172852343625],
[0.2289745640697488, 0.6917385218365064, 0.079286914093745],
- [0.0000000000000000, 0.04511338185890264, 1.043944368900976]
+ [0.0000000000000000, 0.04511338185890264, 1.043944368900976],
];
- let xyz = multiplyMatrixByVector(matrix, linearP3);
+ let xyz = Math.multiplyMatrixByVector(rgbToXYZMatrix, linearP3);
// Convert XYZ to linear-light sRGB.
- matrix = [
+ const xyzToLinearSRGBMatrix = [
[ 3.2404542, -1.5371385, -0.4985314],
[-0.9692660, 1.8760108, 0.0415560],
- [ 0.0556434, -0.2040259, 1.0572252]
+ [ 0.0556434, -0.2040259, 1.0572252],
];
- let linearSRGB = multiplyMatrixByVector(matrix, xyz);
+ let linearSRGB = Math.multiplyMatrixByVector(xyzToLinearSRGBMatrix, xyz);
- return WI.Color._gammaCorrect(linearSRGB);
+ let srgb = WI.Color._gammaCorrect(linearSRGB);
+ return srgb.map((x) => x.maxDecimals(4));
+ }
+
+ // https://www.w3.org/TR/css-color-4/#color-conversion-code
+ static srgbToDisplayP3(r, g, b)
+ {
+ r = Number.constrain(r, 0, 1);
+ g = Number.constrain(g, 0, 1);
+ b = Number.constrain(b, 0, 1);
+
+ let linearSRGB = WI.Color._toLinearLight([r, g, b]);
+
+ // Convert an array of linear-light sRGB values to CIE XYZ
+ // using sRGB's own white, D65 (no chromatic adaptation)
+ // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+ const linearSRGBtoXYZMatrix = [
+ [0.4124564, 0.3575761, 0.1804375],
+ [0.2126729, 0.7151522, 0.0721750],
+ [0.0193339, 0.1191920, 0.9503041],
+ ];
+ let xyz = Math.multiplyMatrixByVector(linearSRGBtoXYZMatrix, linearSRGB);
+
+ const xyzToLinearP3Matrix = [
+ [ 2.493496911941425, -0.9313836179191239, -0.40271078445071684],
+ [-0.8294889695615747, 1.7626640603183463, 0.023624685841943577],
+ [ 0.03584583024378447, -0.07617238926804182, 0.9568845240076872],
+ ];
+ let linearP3 = Math.multiplyMatrixByVector(xyzToLinearP3Matrix, xyz);
+
+ let p3 = WI.Color._gammaCorrect(linearP3);
+ return p3.map((x) => x.maxDecimals(4));
}
// Convert gamma-corrected sRGB or Display-P3 to linear light form.
+ // https://www.w3.org/TR/css-color-4/#color-conversion-code
static _toLinearLight(rgb)
{
return rgb.map(function(value) {
@@ -405,6 +429,7 @@
// Convert linear-light sRGB or Display-P3 to gamma corrected form.
// Inverse of `toLinearLight`.
+ // https://www.w3.org/TR/css-color-4/#color-conversion-code
static _gammaCorrect(rgb)
{
return rgb.map(function(value) {
@@ -456,7 +481,12 @@
case WI.Color.Format.RGB:
case WI.Color.Format.RGBA:
- return this.simple ? WI.Color.Format.HSL : WI.Color.Format.HSLA;
+ return WI.Color.Format.ColorFunction;
+
+ case WI.Color.Format.ColorFunction:
+ if (this.simple)
+ return WI.Color.Format.HSL;
+ return WI.Color.Format.HSLA;
case WI.Color.Format.HSL:
case WI.Color.Format.HSLA:
@@ -483,11 +513,6 @@
}
}
- get alpha()
- {
- return this._rgba ? this._rgba[3] : this._hsla[3];
- }
-
get simple()
{
return this.alpha === 1;
@@ -495,41 +520,67 @@
get rgb()
{
- let rgb = this.rgba.slice();
- rgb.pop();
- return rgb;
+ if (!this._rgb) {
+ if (this._hsl)
+ this._rgb = WI.Color.hsl2rgb(...this._hsl);
+ else if (this._normalizedRGB)
+ this._rgb = this._normalizedRGB.map((component) => WI.Color._eightBitChannel(component * 255));
+ }
+ return this._rgb;
}
get hsl()
{
- let hsl = this.hsla.slice();
- hsl.pop();
- return hsl;
+ if (!this._hsl)
+ this._hsl = WI.Color.rgb2hsl(...this.rgb);
+ return this._hsl;
+ }
+
+ get normalizedRGB()
+ {
+ if (!this._normalizedRGB)
+ this._normalizedRGB = this.rgb.map((component) => component / 255);
+ return this._normalizedRGB;
}
get rgba()
{
- if (!this._rgba)
- this._rgba = this._hslaToRGBA(this._hsla);
- return this._rgba;
+ return [...this.rgb, this.alpha];
}
get hsla()
{
- if (!this._hsla) {
- let rgba = this.rgba;
- if (this.format === WI.Color.Format.ColorFunction) {
- rgba = [
- rgba[0] * 255,
- rgba[1] * 255,
- rgba[2] * 255,
- rgba[3],
- ];
- }
- this._hsla = this._rgbaToHSLA(rgba);
+ return [...this.hsl, this.alpha];
+ }
+
+ get normalizedRGBA()
+ {
+ return [...this.normalizedRGB, this.alpha];
+ }
+
+ get gamut()
+ {
+ return this._gamut;
+ }
+
+ set gamut(gamut)
+ {
+ console.assert(gamut !== this._gamut);
+
+ if (this._gamut === WI.Color.Gamut.DisplayP3 && gamut === WI.Color.Gamut.SRGB) {
+ this._normalizedRGB = WI.Color.displayP3toSRGB(...this.normalizedRGB).map((x) => Number.constrain(x, 0, 1));
+ this._hsl = null;
+ this._rgb = null;
+ } else if (this._gamut === WI.Color.Gamut.SRGB && gamut === WI.Color.Gamut.DisplayP3) {
+ this._normalizedRGB = WI.Color.srgbToDisplayP3(...this.normalizedRGB);
+ this._hsl = null;
+ this._rgb = null;
+
+ // Display-P3 is only available with the color function syntax.
+ this.format = WI.Color.Format.ColorFunction;
}
- return this._hsla;
+ this._gamut = gamut;
}
copy()
@@ -542,12 +593,15 @@
case WI.Color.Format.ShortHEXAlpha:
case WI.Color.Format.Keyword:
case WI.Color.Format.RGBA:
- case WI.Color.Format.ColorFunction:
- return new WI.Color(this.format, this.rgba, this.gamut);
+ return new WI.Color(this.format, this.rgba, this._gamut);
case WI.Color.Format.HSL:
case WI.Color.Format.HSLA:
- return new WI.Color(this.format, this.hsla, this.gamut);
+ return new WI.Color(this.format, this.hsla, this._gamut);
+ case WI.Color.Format.ColorFunction:
+ return new WI.Color(this.format, this.normalizedRGBA, this._gamut);
}
+
+ console.error("Invalid color format: " + this.format);
}
toString(format)
@@ -589,34 +643,50 @@
if (this.keyword)
return true;
- if (this.gamut !== WI.Color.Gamut.SRGB)
+ if (this._gamut !== WI.Color.Gamut.SRGB)
return false;
if (!this.simple)
- return Array.shallowEqual(this._rgba, [0, 0, 0, 0]) || Array.shallowEqual(this._hsla, [0, 0, 0, 0]);
+ return Array.shallowEqual(this.rgba, [0, 0, 0, 0]);
- let rgb = (this._rgba && this._rgba.slice(0, 3)) || WI.Color.hsl2rgb(...this._hsla);
- return Object.keys(WI.Color.Keywords).some(key => Array.shallowEqual(WI.Color.Keywords[key], rgb));
+ return Object.keys(WI.Color.Keywords).some(key => Array.shallowEqual(WI.Color.Keywords[key], this.rgb));
+ }
+
+ isOutsideSRGB()
+ {
+ if (this._gamut !== WI.Color.Gamut.DisplayP3)
+ return false;
+
+ let rgb = WI.Color.displayP3toSRGB(...this.normalizedRGB);
+
+ // displayP3toSRGB(1, 1, 1) produces [0.9999, 1, 1.0001], which aren't pure white color values.
+ // However, `color(sRGB 0.9999 1 1.0001)` looks exactly the same as color `color(sRGB 1 1 1)`
+ // because sRGB is only 8bit per channel. The values get rounded. For example,
+ // `rgb(255, 254.51, 255)` looks exactly the same as `rgb(255, 255, 255)`.
+ //
+ // Consider a color to be within sRGB even if it's actually outside of sRGB by less than half a bit.
+ const epsilon = (1 / 255) / 2;
+ return rgb.some((x) => x <= -epsilon || x >= 1 + epsilon);
}
canBeSerializedAsShortHEX()
{
- let rgba = this.rgba || this._hslaToRGBA(this._hsla);
+ let rgb = this.rgb;
- let r = this._componentToHexValue(rgba[0]);
+ let r = this._componentToHexValue(rgb[0]);
if (r[0] !== r[1])
return false;
- let g = this._componentToHexValue(rgba[1]);
+ let g = this._componentToHexValue(rgb[1]);
if (g[0] !== g[1])
return false;
- let b = this._componentToHexValue(rgba[2]);
+ let b = this._componentToHexValue(rgb[2]);
if (b[0] !== b[1])
return false;
if (!this.simple) {
- let a = this._componentToHexValue(Math.round(rgba[3] * 255));
+ let a = this._componentToHexValue(Math.round(this.alpha * 255));
if (a[0] !== a[1])
return false;
}
@@ -638,7 +708,7 @@
let rgba = this.rgba;
if (!this.simple) {
- if (rgba[0] === 0 && rgba[1] === 0 && rgba[2] === 0 && rgba[3] === 0)
+ if (Array.shallowEqual(rgba, [0, 0, 0, 0]))
return "transparent";
return this._toRGBAString();
}
@@ -661,15 +731,10 @@
if (!this.simple)
return this._toRGBAString();
- let rgba = this.rgba;
- let r = this._componentToHexValue(rgba[0]);
- let g = this._componentToHexValue(rgba[1]);
- let b = this._componentToHexValue(rgba[2]);
-
+ let [r, g, b] = this.rgb.map(this._componentToHexValue);
if (r[0] === r[1] && g[0] === g[1] && b[0] === b[1])
return "#" + r[0] + g[0] + b[0];
- else
- return "#" + r + g + b;
+ return "#" + r + g + b;
}
_toHEXString()
@@ -677,36 +742,23 @@
if (!this.simple)
return this._toRGBAString();
- let rgba = this.rgba;
- let r = this._componentToHexValue(rgba[0]);
- let g = this._componentToHexValue(rgba[1]);
- let b = this._componentToHexValue(rgba[2]);
-
+ let [r, g, b] = this.rgb.map(this._componentToHexValue);
return "#" + r + g + b;
}
_toShortHEXAlphaString()
{
- let rgba = this.rgba;
- let r = this._componentToHexValue(rgba[0]);
- let g = this._componentToHexValue(rgba[1]);
- let b = this._componentToHexValue(rgba[2]);
- let a = this._componentToHexValue(Math.round(rgba[3] * 255));
-
+ let [r, g, b] = this.rgb.map(this._componentToHexValue);
+ let a = this._componentToHexValue(Math.round(this.alpha * 255));
if (r[0] === r[1] && g[0] === g[1] && b[0] === b[1] && a[0] === a[1])
return "#" + r[0] + g[0] + b[0] + a[0];
- else
- return "#" + r + g + b + a;
+ return "#" + r + g + b + a;
}
_toHEXAlphaString()
{
- let rgba = this.rgba;
- let r = this._componentToHexValue(rgba[0]);
- let g = this._componentToHexValue(rgba[1]);
- let b = this._componentToHexValue(rgba[2]);
- let a = this._componentToHexValue(Math.round(rgba[3] * 255));
-
+ let [r, g, b] = this.rgb.map(this._componentToHexValue);
+ let a = this._componentToHexValue(Math.round(this.alpha * 255));
return "#" + r + g + b + a;
}
@@ -715,26 +767,22 @@
if (!this.simple)
return this._toRGBAString();
- let r = WI.Color._eightBitChannel(Math.round(this.rgba[0]));
- let g = WI.Color._eightBitChannel(Math.round(this.rgba[1]));
- let b = WI.Color._eightBitChannel(Math.round(this.rgba[2]));
+ let [r, g, b] = this.rgb.map(WI.Color._eightBitChannel);
return `rgb(${r}, ${g}, ${b})`;
}
_toRGBAString()
{
- let r = WI.Color._eightBitChannel(Math.round(this.rgba[0]));
- let g = WI.Color._eightBitChannel(Math.round(this.rgba[1]));
- let b = WI.Color._eightBitChannel(Math.round(this.rgba[2]));
+ let [r, g, b] = this.rgb.map(WI.Color._eightBitChannel);
return `rgba(${r}, ${g}, ${b}, ${this.alpha})`;
}
_toFunctionString()
{
- let [r, g, b, alpha] = this.rgba;
- if (alpha === 1)
- return `color(${this.gamut} ${r} ${g} ${b})`;
- return `color(${this.gamut} ${r} ${g} ${b} / ${alpha})`;
+ let [r, g, b] = this.normalizedRGB.map((x) => x.maxDecimals(4));
+ if (this.alpha === 1)
+ return `color(${this._gamut} ${r} ${g} ${b})`;
+ return `color(${this._gamut} ${r} ${g} ${b} / ${this.alpha})`;
}
_toHSLString()
@@ -742,17 +790,13 @@
if (!this.simple)
return this._toHSLAString();
- let h = this.hsla[0].maxDecimals(2);
- let s = this.hsla[1].maxDecimals(2);
- let l = this.hsla[2].maxDecimals(2);
+ let [h, s, l] = this.hsl.map((x) => x.maxDecimals(2));
return `hsl(${h}, ${s}%, ${l}%)`;
}
_toHSLAString()
{
- let h = this.hsla[0].maxDecimals(2);
- let s = this.hsla[1].maxDecimals(2);
- let l = this.hsla[2].maxDecimals(2);
+ let [h, s, l] = this.hsl.map((x) => x.maxDecimals(2));
return `hsla(${h}, ${s}%, ${l}%, ${this.alpha})`;
}
@@ -763,20 +807,6 @@
hex = "0" + hex;
return hex;
}
-
- _rgbaToHSLA(rgba)
- {
- let hsla = WI.Color.rgb2hsl(...rgba);
- hsla.push(rgba[3]);
- return hsla;
- }
-
- _hslaToRGBA(hsla)
- {
- let rgba = WI.Color.hsl2rgb(...hsla);
- rgba.push(hsla[3]);
- return rgba;
- }
};
WI.Color.Format = {
diff --git a/Source/WebInspectorUI/UserInterface/Views/ColorPicker.js b/Source/WebInspectorUI/UserInterface/Views/ColorPicker.js
index 818a666..54a79d9 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ColorPicker.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ColorPicker.js
@@ -174,7 +174,9 @@
components = this._colorSquare.tintedColor.hsl.concat(opacity);
if (opacity !== 1)
format = WI.Color.Format.HSLA;
- } else {
+ } else if (format === WI.Color.Format.ColorFunction)
+ components = this._colorSquare.tintedColor.normalizedRGB.concat(opacity);
+ else {
components = this._colorSquare.tintedColor.rgb.concat(opacity);
if (opacity !== 1 && format === WI.Color.Format.RGB)
format = WI.Color.Format.RGBA;
@@ -194,8 +196,10 @@
_updateOpacitySlider()
{
- let rgb = this._colorSquare.tintedColor.rgb;
- let gamut = this._colorSquare.tintedColor.gamut;
+ let color = this._colorSquare.tintedColor;
+
+ let rgb = color.format === WI.Color.Format.ColorFunction ? color.normalizedRGB : color.rgb;
+ let gamut = color.gamut;
let format = gamut === WI.Color.Gamut.DisplayP3 ? WI.Color.Format.ColorFunction : WI.Color.Format.RGBA;
let opaque = new WI.Color(format, rgb.concat(1), gamut).toString();
let transparent = new WI.Color(format, rgb.concat(0), gamut).toString();
diff --git a/Source/WebInspectorUI/UserInterface/Views/ColorSquare.css b/Source/WebInspectorUI/UserInterface/Views/ColorSquare.css
index 683b2da..1056a7d 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ColorSquare.css
+++ b/Source/WebInspectorUI/UserInterface/Views/ColorSquare.css
@@ -61,7 +61,7 @@
--crosshair-size: 7px;
}
-.color-square .svg-root {
+.color-square > .svg-root {
position: absolute;
top: 0;
left: 0;
@@ -70,30 +70,30 @@
pointer-events: none;
}
-.color-square .srgb-edge {
+.color-square > .svg-root > .srgb-edge {
fill: none;
stroke: white;
stroke-width: 0.5px;
stroke-opacity: var(--stroke-opacity);
}
-.color-square .srgb-label {
+.color-square > .srgb-label {
position: absolute;
- padding-right: 3px;
+ padding-right: 5px;
color: hsla(0, 0%, 100%, var(--stroke-opacity));
font-size: 10px;
}
-.color-square .srgb-label:hover {
+.color-square > .srgb-label:hover {
color: white;
}
-.color-square .srgb-label:hover + .svg-root > .srgb-edge {
+.color-square > .srgb-label:hover + .svg-root > .srgb-edge {
stroke-width: 1px;
}
@media (-webkit-device-pixel-ratio: 1) {
- .color-square .srgb-edge {
+ .color-square > .srgb-edge {
stroke-width: 1px;
stroke-opacity: var(--stroke-opacity) / 2;
}
diff --git a/Source/WebInspectorUI/UserInterface/Views/ColorSquare.js b/Source/WebInspectorUI/UserInterface/Views/ColorSquare.js
index bb0fd87..7d87937 100644
--- a/Source/WebInspectorUI/UserInterface/Views/ColorSquare.js
+++ b/Source/WebInspectorUI/UserInterface/Views/ColorSquare.js
@@ -106,7 +106,7 @@
if (tintedColor.format === WI.Color.Format.ColorFunction) {
// CSS color function only supports RGB. It doesn't support HSL.
- let hsv = WI.Color.rgb2hsv(...tintedColor.rgb);
+ let hsv = WI.Color.rgb2hsv(...tintedColor.normalizedRGB);
let x = hsv[1] / 100 * this._dimension;
let y = (1 - (hsv[2] / 100)) * this._dimension;
this._setCrosshairPosition(new WI.Point(x, y));
@@ -229,12 +229,11 @@
this._srgbLabelElement = this._element.appendChild(document.createElement("span"));
this._srgbLabelElement.className = "srgb-label";
this._srgbLabelElement.textContent = WI.unlocalizedString("sRGB");
- this._srgbLabelElement.title = WI.UIString("This line marks the edge of sRGB color space", "Label for a guide within the color picker");
+ this._srgbLabelElement.title = WI.UIString("Edge of sRGB color space", "Label for a guide within the color picker");
const svgNamespace = "http://www.w3.org/2000/svg";
- this._svgElement = document.createElementNS(svgNamespace, "svg");
+ this._svgElement = this._element.appendChild(document.createElementNS(svgNamespace, "svg"));
this._svgElement.classList.add("svg-root");
- this._element.append(this._svgElement);
this._polylineElement = this._svgElement.appendChild(document.createElementNS(svgNamespace, "polyline"));
this._polylineElement.classList.add("srgb-edge");
@@ -244,7 +243,7 @@
let step = 1 / window.devicePixelRatio;
let x = 0;
for (let y = 0; y < this._dimension; y += step) {
- let value = 100 - (y / this._dimension) * 100;
+ let value = 100 - ((y / this._dimension) * 100);
// Optimization: instead of starting from x = 0, we can benefit from the fact that the next point
// always has x >= of the current x. This minimizes processing time over 100 times.
@@ -252,7 +251,7 @@
let saturation = x / this._dimension * 100;
let rgb = WI.Color.hsv2rgb(this._hue, saturation, value);
let srgb = WI.Color.displayP3toSRGB(rgb[0], rgb[1], rgb[2]);
- if (srgb.some((value) => value > 1 || value < 0)) {
+ if (srgb.some((value) => value < 0 || value > 1)) {
// The point is outside of sRGB.
points.push({x, y});
break;
@@ -274,10 +273,10 @@
this._srgbLabelElement.style.right = `${this._dimension - points.lastValue.x}px`;
this._polylineElement.points.clear();
- for (let point of points) {
+ for (let {x, y} of points) {
let svgPoint = this._svgElement.createSVGPoint();
- svgPoint.x = point.x;
- svgPoint.y = point.y;
+ svgPoint.x = x;
+ svgPoint.y = y;
this._polylineElement.points.appendItem(svgPoint);
}
}
diff --git a/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js b/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js
index fff2311..0ea7d89 100644
--- a/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js
+++ b/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js
@@ -46,7 +46,7 @@
else {
switch (this._type) {
case WI.InlineSwatch.Type.Color:
- this._swatchElement.title = WI.UIString("Click to select a color\nShift-click to switch color formats");
+ // Handled later by _updateSwatch.
break;
case WI.InlineSwatch.Type.Gradient:
this._swatchElement.title = WI.UIString("Edit custom gradient");
@@ -77,6 +77,7 @@
this._value = value || this._fallbackValue();
this._valueEditor = null;
+ this._readOnly = readOnly;
this._updateSwatch();
}
@@ -146,10 +147,22 @@
else if (this._type === WI.InlineSwatch.Type.Image)
this._swatchInnerElement.style.setProperty("background-image", `url(${value.src})`);
+ if (this._type === WI.InlineSwatch.Type.Color) {
+ if (this._allowShiftClickColor())
+ this._swatchElement.title = WI.UIString("Click to select a color\nShift-click to switch color formats");
+ else
+ this._swatchElement.title = WI.UIString("Click to select a color");
+ }
+
if (!dontFireEvents)
this.dispatchEventToListeners(WI.InlineSwatch.Event.ValueChanged, {value});
}
+ _allowShiftClickColor()
+ {
+ return !this._readOnly && !this.value.isOutsideSRGB();
+ }
+
_swatchElementClicked(event)
{
event.stop();
@@ -158,8 +171,12 @@
if (event.shiftKey && value) {
if (this._type === WI.InlineSwatch.Type.Color) {
+ if (!this._allowShiftClickColor()) {
+ InspectorFrontendHost.beep();
+ return;
+ }
+
let nextFormat = value.nextFormat();
- // FIXME: <https://webkit.org/b/203534> Provide UI to convert between sRGB and p3 color spaces
console.assert(nextFormat);
if (nextFormat) {
value.format = nextFormat;
@@ -313,42 +330,69 @@
return;
let contextMenu = WI.ContextMenu.createFromEvent(event);
+ let isColorOutsideSRGB = value.isOutsideSRGB();
- if (value.isKeyword() && value.format !== WI.Color.Format.Keyword) {
- contextMenu.appendItem(WI.UIString("Format: Keyword"), () => {
- value.format = WI.Color.Format.Keyword;
+ if (!isColorOutsideSRGB) {
+ if (value.isKeyword() && value.format !== WI.Color.Format.Keyword) {
+ contextMenu.appendItem(WI.UIString("Format: Keyword"), () => {
+ value.format = WI.Color.Format.Keyword;
+ this._updateSwatch();
+ });
+ }
+
+ let hexInfo = this._getNextValidHEXFormat();
+ if (hexInfo) {
+ contextMenu.appendItem(hexInfo.title, () => {
+ value.format = hexInfo.format;
+ this._updateSwatch();
+ });
+ }
+
+ if (value.simple && value.format !== WI.Color.Format.HSL) {
+ contextMenu.appendItem(WI.UIString("Format: HSL"), () => {
+ value.format = WI.Color.Format.HSL;
+ this._updateSwatch();
+ });
+ } else if (value.format !== WI.Color.Format.HSLA) {
+ contextMenu.appendItem(WI.UIString("Format: HSLA"), () => {
+ value.format = WI.Color.Format.HSLA;
+ this._updateSwatch();
+ });
+ }
+
+ if (value.simple && value.format !== WI.Color.Format.RGB) {
+ contextMenu.appendItem(WI.UIString("Format: RGB"), () => {
+ value.format = WI.Color.Format.RGB;
+ this._updateSwatch();
+ });
+ } else if (value.format !== WI.Color.Format.RGBA) {
+ contextMenu.appendItem(WI.UIString("Format: RGBA"), () => {
+ value.format = WI.Color.Format.RGBA;
+ this._updateSwatch();
+ });
+ }
+
+ if (value.format !== WI.Color.Format.ColorFunction) {
+ contextMenu.appendItem(WI.UIString("Format: Color Function"), () => {
+ value.format = WI.Color.Format.ColorFunction;
+ this._updateSwatch();
+ });
+ }
+
+ contextMenu.appendSeparator();
+ }
+
+ if (value.gamut !== WI.Color.Gamut.DisplayP3) {
+ contextMenu.appendItem(WI.UIString("Convert to Display-P3"), () => {
+ value.gamut = WI.Color.Gamut.DisplayP3;
this._updateSwatch();
});
}
- let hexInfo = this._getNextValidHEXFormat();
- if (hexInfo) {
- contextMenu.appendItem(hexInfo.title, () => {
- value.format = hexInfo.format;
- this._updateSwatch();
- });
- }
-
- if (value.simple && value.format !== WI.Color.Format.HSL) {
- contextMenu.appendItem(WI.UIString("Format: HSL"), () => {
- value.format = WI.Color.Format.HSL;
- this._updateSwatch();
- });
- } else if (value.format !== WI.Color.Format.HSLA) {
- contextMenu.appendItem(WI.UIString("Format: HSLA"), () => {
- value.format = WI.Color.Format.HSLA;
- this._updateSwatch();
- });
- }
-
- if (value.simple && value.format !== WI.Color.Format.RGB) {
- contextMenu.appendItem(WI.UIString("Format: RGB"), () => {
- value.format = WI.Color.Format.RGB;
- this._updateSwatch();
- });
- } else if (value.format !== WI.Color.Format.RGBA) {
- contextMenu.appendItem(WI.UIString("Format: RGBA"), () => {
- value.format = WI.Color.Format.RGBA;
+ if (value.gamut !== WI.Color.Gamut.SRGB) {
+ let label = isColorOutsideSRGB ? WI.UIString("Clamp to sRGB") : WI.UIString("Convert to sRGB");
+ contextMenu.appendItem(label, () => {
+ value.gamut = WI.Color.Gamut.SRGB;
this._updateSwatch();
});
}