Changeset 253018 in webkit


Ignore:
Timestamp:
Dec 2, 2019 5:46:11 PM (4 years ago)
Author:
Nikita Vasilyev
Message:

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:
Location:
trunk
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r253017 r253018  
     12019-12-02  Nikita Vasilyev  <nvasilyev@apple.com>
     2
     3        Web Inspector: Provide UI to convert between sRGB and p3 color spaces
     4        https://bugs.webkit.org/show_bug.cgi?id=203534
     5        <rdar://problem/56688523>
     6
     7        Reviewed by Devin Rousso.
     8
     9        * inspector/model/color-expected.txt:
     10        * inspector/model/color.html:
     11
    1122019-12-02  Said Abou-Hallawa  <sabouhallawa@apple.com>
    213
  • trunk/LayoutTests/inspector/model/color-expected.txt

    r252747 r253018  
    397397PASS: 'color(display-p3 0 1 0.5)' color should have a default alpha of 1
    398398PASS: color function is not a keyword
    399 PASS: 'color(display-p3 0 1 0.5)' has rgb of [0, 1, 0.5]
    400 PASS: 'color(display-p3 0 1 0.5)' has rgba of [0, 1, 0.5, 1]
     399PASS: 'color(display-p3 0 1 0.5)' has normalized RGB of [0, 1, 0.5]
     400PASS: 'color(display-p3 0 1 0.5)' has normalized RGBA of [0, 1, 0.5, 1]
    401401PASS: 'color(display-p3 0 1 0.5 / 0.3)' should have an alpha of 0.3
    402402PASS: color function is not a keyword
    403 PASS: 'color(display-p3 0 1 0.5 / 0.3)' has rgb of [0, 1, 0.5]
    404 PASS: 'color(display-p3 0 1 0.5 / 0.3)' has rgba of [0, 1, 0.5, 0.3]
     403PASS: 'color(display-p3 0 1 0.5 / 0.3)' has normalized RGB of [0, 1, 0.5]
     404PASS: 'color(display-p3 0 1 0.5 / 0.3)' has normalized RGBA of [0, 1, 0.5, 0.3]
    405405
    406406-- Running test case: WI.Color from components
     
    521521-- Running test case: WI.Color.displayP3toSRGB
    522522PASS: Should convert [0,0,0] to [0,0,0].
    523 PASS: Should convert [1,1,1] to [0.9999300658875597,1.0000101408119602,1.0001054916006267].
    524 PASS: Should convert [1,0,0] to [1.0929902391315327,-0.5433883521407902,-0.2537782522695441].
    525 PASS: Should convert [0,1,0] to [-2.9057643199691516,1.0182759798644396,-1.0162215225472337].
    526 PASS: Should convert [0,0,1] to [2.3902361583783006e-7,-2.0487738563140788e-7,1.0421313171983129].
    527 PASS: Should convert [2,0,0] to [1.0929902391315327,-0.5433883521407902,-0.2537782522695441].
     523PASS: Should convert [1,1,1] to [0.9999,1,1.0001].
     524PASS: Should convert [1,0,0] to [1.093,-0.5434,-0.2538].
     525PASS: Should convert [0,1,0] to [-2.9058,1.0183,-1.0162].
     526PASS: Should convert [0,0,1] to [0,0,1.0421].
     527PASS: Should convert [2,0,0] to [1.093,-0.5434,-0.2538].
    528528PASS: Should convert [-1,0,0] to [0,0,0].
    529529
     530-- Running test case: WI.Color.srgbToDisplayP3
     531PASS: Should convert [0,0,0] to [0,0,0].
     532PASS: Should convert [1,1,1] to [1.0001,1,0.9999].
     533PASS: Should convert [1,0,0] to [0.9176,0.2003,0.1386].
     534PASS: Should convert [0,1,0] to [0.4584,0.9853,0.2983].
     535PASS: Should convert [0,0,1] to [0,0,0.9595].
     536PASS: Should convert [2,0,0] to [0.9176,0.2003,0.1386].
     537PASS: Should convert [-1,0,0] to [0,0,0].
     538
     539-- Running test case: WI.Color.isOutsideSRGB
     540"color(display-p3 0 0 0)" is inside sRGB.
     541"color(display-p3 1 1 1)" is inside sRGB.
     542"color(display-p3 0.04 0.14 0.016)" is inside sRGB.
     543"color(display-p3 1 0 0)" is outside sRGB.
     544"color(display-p3 0.93 0.353 0.353)" is outside sRGB.
     545
  • trunk/LayoutTests/inspector/model/color.html

    r252747 r253018  
    324324            InspectorTest.expectThat(color.alpha === 1, "'color(display-p3 0 1 0.5)' color should have a default alpha of 1");
    325325            InspectorTest.expectThat(color.isKeyword() === false, "color function is not a keyword");
    326             InspectorTest.expectShallowEqual(color.rgb, [0, 1, 0.5], "'color(display-p3 0 1 0.5)' has rgb of [0, 1, 0.5]");
    327             InspectorTest.expectShallowEqual(color.rgba, [0, 1, 0.5, 1], "'color(display-p3 0 1 0.5)' has rgba of [0, 1, 0.5, 1]");
     326            InspectorTest.expectShallowEqual(color.normalizedRGB, [0, 1, 0.5], "'color(display-p3 0 1 0.5)' has normalized RGB of [0, 1, 0.5]");
     327            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]");
    328328
    329329            color = WI.Color.fromString("color(display-p3 0 1 0.5 / 0.3)");
    330330            InspectorTest.expectThat(color.alpha === 0.3, "'color(display-p3 0 1 0.5 / 0.3)' should have an alpha of 0.3");
    331331            InspectorTest.expectThat(color.isKeyword() === false, "color function is not a keyword");
    332             InspectorTest.expectShallowEqual(color.rgb, [0, 1, 0.5], "'color(display-p3 0 1 0.5 / 0.3)' has rgb of [0, 1, 0.5]");
    333             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]");
     332            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]");
     333            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]");
    334334
    335335            return true;
     
    403403            test("transparent", [
    404404                WI.Color.Format.RGBA,
     405                WI.Color.Format.ColorFunction,
    405406                WI.Color.Format.HSLA,
    406407                WI.Color.Format.Keyword,
     
    412413            test("red", [
    413414                WI.Color.Format.RGB,
     415                WI.Color.Format.ColorFunction,
    414416                WI.Color.Format.HSL,
    415417                WI.Color.Format.Keyword,
     
    421423            test("rgb(100, 150, 200)", [
    422424                WI.Color.Format.RGB,
     425                WI.Color.Format.ColorFunction,
    423426                WI.Color.Format.HSL,
    424427                WI.Color.Format.HEX,
     
    428431            test("rgba(100, 150, 200, 0.5)", [
    429432                WI.Color.Format.RGBA,
     433                WI.Color.Format.ColorFunction,
    430434                WI.Color.Format.HSLA,
    431435                WI.Color.Format.HEXAlpha,
     
    618622        test() {
    619623            testColorConversion(WI.Color.displayP3toSRGB, [0, 0, 0], [0, 0, 0]);
    620             testColorConversion(WI.Color.displayP3toSRGB, [1, 1, 1], [0.9999300658875597, 1.0000101408119602, 1.0001054916006267]);
    621             testColorConversion(WI.Color.displayP3toSRGB, [1, 0, 0], [1.0929902391315327, -0.5433883521407902, -0.2537782522695441]);
    622             testColorConversion(WI.Color.displayP3toSRGB, [0, 1, 0], [-2.9057643199691516, 1.0182759798644396, -1.0162215225472337]);
    623             testColorConversion(WI.Color.displayP3toSRGB, [0, 0, 1], [2.3902361583783006e-7, -2.0487738563140788e-7, 1.0421313171983129]);
     624            testColorConversion(WI.Color.displayP3toSRGB, [1, 1, 1], [0.9999, 1, 1.0001]);
     625            testColorConversion(WI.Color.displayP3toSRGB, [1, 0, 0], [1.093, -0.5434, -0.2538]);
     626            testColorConversion(WI.Color.displayP3toSRGB, [0, 1, 0], [-2.9058, 1.0183, -1.0162]);
     627            testColorConversion(WI.Color.displayP3toSRGB, [0, 0, 1], [0, 0, 1.0421]);
    624628
    625629            // Out-of-bounds inputs.
    626             testColorConversion(WI.Color.displayP3toSRGB, [2, 0, 0], [1.0929902391315327, -0.5433883521407902, -0.2537782522695441]);
     630            testColorConversion(WI.Color.displayP3toSRGB, [2, 0, 0], [1.093, -0.5434, -0.2538]);
    627631            testColorConversion(WI.Color.displayP3toSRGB, [-1, 0, 0], [0, 0, 0]);
     632
     633            return true;
     634        }
     635    });
     636
     637    suite.addTestCase({
     638        name: "WI.Color.srgbToDisplayP3",
     639        description: "Test conversion from sRGB gamut to display-P3.",
     640        test() {
     641            testColorConversion(WI.Color.srgbToDisplayP3, [0, 0, 0], [0, 0, 0]);
     642            testColorConversion(WI.Color.srgbToDisplayP3, [1, 1, 1], [1.0001, 1, 0.9999]);
     643            testColorConversion(WI.Color.srgbToDisplayP3, [1, 0, 0], [0.9176, 0.2003, 0.1386]);
     644            testColorConversion(WI.Color.srgbToDisplayP3, [0, 1, 0], [0.4584, 0.9853, 0.2983]);
     645            testColorConversion(WI.Color.srgbToDisplayP3, [0, 0, 1], [0, 0, 0.9595]);
     646
     647            // Out-of-bounds inputs.
     648            testColorConversion(WI.Color.srgbToDisplayP3, [2, 0, 0], [0.9176, 0.2003, 0.1386]);
     649            testColorConversion(WI.Color.srgbToDisplayP3, [-1, 0, 0], [0, 0, 0]);
     650
     651            return true;
     652        }
     653    });
     654
     655    suite.addTestCase({
     656        name: "WI.Color.isOutsideSRGB",
     657        description: "Test conversion from sRGB gamut to display-P3.",
     658        test() {
     659            function test(string) {
     660                let color = WI.Color.fromString(string);
     661                let result = color.isOutsideSRGB();
     662                InspectorTest.log(`"${string}" is ${result ? "outside" : "inside"} sRGB.`);
     663            }
     664
     665            test("color(display-p3 0 0 0)");
     666            test("color(display-p3 1 1 1)");
     667            test("color(display-p3 0.04 0.14 0.016)"); // Barely inside sRGB. Values chosen to test rounding behavior.
     668            test("color(display-p3 1 0 0)");
     669            test("color(display-p3 0.93 0.353 0.353)"); // Barely outside sRGB.
    628670
    629671            return true;
  • trunk/Source/WebInspectorUI/ChangeLog

    r253000 r253018  
     12019-12-02  Nikita Vasilyev  <nvasilyev@apple.com>
     2
     3        Web Inspector: Provide UI to convert between sRGB and p3 color spaces
     4        https://bugs.webkit.org/show_bug.cgi?id=203534
     5        <rdar://problem/56688523>
     6
     7        Reviewed by Devin Rousso.
     8
     9        Add context menus:
     10        - "Convert to sRGB" and "Clamp to sRGB" for p3 colors, such as `color(display-p3 0 1 0)`.
     11        - "Convert to Display-P3" for sRGB colors, such as `rgb(0, 255, 0)`.
     12
     13        Shift-clicking the color swatch of sRGB colors now goes through the color function syntax as well.
     14        Shift-clicking the color swatch of Display-P3 colors converts the color to sRGB when it can be lossless.
     15        When the convertion cannot be lossless, Web Inspector beeps.
     16
     17        * Localizations/en.lproj/localizedStrings.js:
     18        * UserInterface/Base/Utilities.js:
     19        * UserInterface/Models/Color.js:
     20        (WI.Color):
     21        Introduce `_normalizedRGB` property, which stores rgb values from 0 to 1.
     22        Previously, `_rgba` stored values from 0 to 1 for color function format, and from 0 to 255 otherwise.
     23        That required format checks before every `rgb` value access and resulted in silent errors when
     24        the values were in the wrong format.
     25
     26        Store alpha as a separate property to simplify format conversion. Previously, alpha was duplicated between `_rgba` and `_hsla`.
     27
     28        (WI.Color.displayP3toSRGB):
     29        (WI.Color.srgbToDisplayP3): Added.
     30        (WI.Color.prototype.nextFormat):
     31        (WI.Color.prototype.get rgb):
     32        (WI.Color.prototype.get hsl):
     33        (WI.Color.prototype.get normalizedRGB):
     34        (WI.Color.prototype.get rgba):
     35        (WI.Color.prototype.get hsla):
     36        (WI.Color.prototype.get normalizedRGBA):
     37        (WI.Color.prototype.get gamut):
     38        (WI.Color.prototype.set gamut):
     39        (WI.Color.prototype.copy):
     40        (WI.Color.prototype.isKeyword):
     41        (WI.Color.prototype.isOutsideSRGB): Added.
     42        (WI.Color.prototype.canBeSerializedAsShortHEX):
     43        (WI.Color.prototype._toKeywordString):
     44        (WI.Color.prototype._toShortHEXString):
     45        (WI.Color.prototype._toHEXString):
     46        (WI.Color.prototype._toShortHEXAlphaString):
     47        (WI.Color.prototype._toHEXAlphaString):
     48        (WI.Color.prototype._toRGBString):
     49        (WI.Color.prototype._toRGBAString):
     50        (WI.Color.prototype._toFunctionString):
     51        Limit the values to 4 decimals.
     52
     53        (WI.Color.prototype._toHSLString):
     54        (WI.Color.prototype._toHSLAString):
     55        (WI.Color.prototype._hslToRGB):
     56        * UserInterface/Views/ColorPicker.js:
     57        (WI.ColorPicker.prototype._updateColor):
     58        (WI.ColorPicker.prototype._updateOpacitySlider):
     59        * UserInterface/Views/ColorSquare.css:
     60        (.color-square > .svg-root):
     61        (.color-square > .svg-root > .srgb-edge):
     62        (.color-square > .srgb-label):
     63        (.color-square > .srgb-label:hover):
     64        (.color-square > .srgb-label:hover + .svg-root > .srgb-edge):
     65        (@media (-webkit-device-pixel-ratio: 1)):
     66        (.color-square > .srgb-edge):
     67        * UserInterface/Views/ColorSquare.js:
     68        (WI.ColorSquare.prototype.set tintedColor):
     69        (WI.ColorSquare.prototype._drawSRGBOutline):
     70        (WI.ColorSquare):
     71
     72        * UserInterface/Views/InlineSwatch.js:
     73        (WI.InlineSwatch):
     74        (WI.InlineSwatch.prototype._updateSwatch):
     75        (WI.InlineSwatch.prototype._allowShiftClickColor): Added.
     76        (WI.InlineSwatch.prototype._handleContextMenuEvent):
     77
    1782019-12-02  Devin Rousso  <drousso@apple.com>
    279
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r252747 r253018  
    241241localizedStrings["Children"] = "Children";
    242242localizedStrings["Cipher"] = "Cipher";
     243localizedStrings["Clamp to sRGB"] = "Clamp to sRGB";
    243244localizedStrings["Classes"] = "Classes";
    244245localizedStrings["Clear Filters"] = "Clear Filters";
     
    255256localizedStrings["Click to create a Local Override from this content"] = "Click to create a Local Override from this content";
    256257localizedStrings["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";
     258localizedStrings["Click to select a color"] = "Click to select a color";
    257259localizedStrings["Click to select a color\nShift-click to switch color formats"] = "Click to select a color\nShift-click to switch color formats";
    258260localizedStrings["Click to view variable value\nShift-click to replace variable with value"] = "Click to view variable value\nShift-click to replace variable with value";
     
    305307localizedStrings["Continue without automatically stopping"] = "Continue without automatically stopping";
    306308localizedStrings["Controls"] = "Controls";
     309localizedStrings["Convert to Display-P3"] = "Convert to Display-P3";
     310localizedStrings["Convert to sRGB"] = "Convert to sRGB";
    307311localizedStrings["Cookies"] = "Cookies";
    308312localizedStrings["Copy"] = "Copy";
     
    408412localizedStrings["Dynamically calculated for the selected element"] = "Dynamically calculated for the selected element";
    409413localizedStrings["Dynamically calculated for the selected element and did not match"] = "Dynamically calculated for the selected element and did not match";
     414/* Label for a guide within the color picker */
     415localizedStrings["Edge of sRGB color space"] = "Edge of sRGB color space";
    410416localizedStrings["Edit"] = "Edit";
    411417localizedStrings["Edit Breakpoint\u2026"] = "Edit Breakpoint\u2026";
     
    541547/* A context menu item to force (override) a DOM node's pseudo-classes */
    542548localizedStrings["Forced Pseudo-Classes"] = "Forced Pseudo-Classes";
     549localizedStrings["Format: Color Function"] = "Format: Color Function";
    543550localizedStrings["Format: HSL"] = "Format: HSL";
    544551localizedStrings["Format: HSLA"] = "Format: HSLA";
     
    11761183localizedStrings["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.";
    11771184localizedStrings["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.";
    1178 /* Label for a guide within the color picker */
    1179 localizedStrings["This line marks the edge of sRGB color space"] = "This line marks the edge of sRGB color space";
    11801185localizedStrings["This object is a root"] = "This object is a root";
    11811186localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects";
  • trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js

    r251531 r253018  
    12121212});
    12131213
     1214// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web#Multiplying_a_matrix_and_a_point
     1215Object.defineProperty(Math, "multiplyMatrixByVector",
     1216{
     1217    value(matrix, vector)
     1218    {
     1219        let height = matrix.length;
     1220        let width = matrix[0].length;
     1221        console.assert(width === vector.length);
     1222
     1223        let result = Array(width).fill(0);
     1224        for (let i = 0; i < width; ++i) {
     1225            for (let rowIndex = 0; rowIndex < height; ++rowIndex)
     1226                result[i] += vector[rowIndex] * matrix[i][rowIndex];
     1227        }
     1228
     1229        return result;
     1230    }
     1231});
     1232
    12141233Object.defineProperty(Number, "constrain",
    12151234{
  • trunk/Source/WebInspectorUI/UserInterface/Models/Color.js

    r252747 r253018  
    3535
    3636        console.assert(gamut === undefined || Object.values(WI.Color.Gamut).includes(gamut));
    37         this.gamut = gamut || WI.Color.Gamut.SRGB;
    38 
    39         if (components.length === 3)
    40             components.push(1);
     37        this._gamut = gamut || WI.Color.Gamut.SRGB;
     38
     39        console.assert(components.length === 3 || components.length === 4, components);
     40        this.alpha = components.length === 4 ? components[3] : 1;
     41
     42        this._rgb = null;
     43        this._normalizedRGB = null;
     44        this._hsl = null;
    4145
    4246        if (format === WI.Color.Format.HSL || format === WI.Color.Format.HSLA)
    43             this._hsla = components;
     47            this._hsl = components.slice(0, 3);
     48        else if (format === WI.Color.Format.ColorFunction)
     49            this._normalizedRGB = components.slice(0, 3);
    4450        else
    45             this._rgba = components;
     51            this._rgb = components.slice(0, 3);
    4652
    4753        this.valid = !components.some(isNaN);
     
    358364        let linearP3 = WI.Color._toLinearLight([r, g, b]);
    359365
    360         // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web#Multiplying_a_matrix_and_a_point
    361         function multiplyMatrixByVector(matrix, vector) {
    362             let height = matrix.length;
    363             let width = matrix[0].length;
    364             console.assert(width === vector.length);
    365 
    366             let result = Array(width).fill(0);
    367             for (let i = 0; i < width; i++)
    368                 for (let rowIndex = 0; rowIndex < height; rowIndex++)
    369                     result[i] += vector[rowIndex] * matrix[i][rowIndex];
    370 
    371             return result;
    372         }
    373 
    374366        // Convert an array of linear-light display-p3 values to CIE XYZ
    375367        // using D65 (no chromatic adaptation).
    376368        // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
    377         let matrix = [
     369        const rgbToXYZMatrix = [
    378370            [0.4865709486482162, 0.26566769316909306, 0.1982172852343625],
    379371            [0.2289745640697488, 0.6917385218365064,  0.079286914093745],
    380             [0.0000000000000000, 0.04511338185890264, 1.043944368900976]
     372            [0.0000000000000000, 0.04511338185890264, 1.043944368900976],
    381373        ];
    382         let xyz = multiplyMatrixByVector(matrix, linearP3);
     374        let xyz = Math.multiplyMatrixByVector(rgbToXYZMatrix, linearP3);
    383375
    384376        // Convert XYZ to linear-light sRGB.
    385         matrix = [
     377        const xyzToLinearSRGBMatrix = [
    386378            [ 3.2404542, -1.5371385, -0.4985314],
    387379            [-0.9692660,  1.8760108,  0.0415560],
    388             [ 0.0556434, -0.2040259,  1.0572252]
     380            [ 0.0556434, -0.2040259,  1.0572252],
    389381        ];
    390         let linearSRGB = multiplyMatrixByVector(matrix, xyz);
    391 
    392         return WI.Color._gammaCorrect(linearSRGB);
     382        let linearSRGB = Math.multiplyMatrixByVector(xyzToLinearSRGBMatrix, xyz);
     383
     384        let srgb = WI.Color._gammaCorrect(linearSRGB);
     385        return srgb.map((x) => x.maxDecimals(4));
     386    }
     387
     388    // https://www.w3.org/TR/css-color-4/#color-conversion-code
     389    static srgbToDisplayP3(r, g, b)
     390    {
     391        r = Number.constrain(r, 0, 1);
     392        g = Number.constrain(g, 0, 1);
     393        b = Number.constrain(b, 0, 1);
     394
     395        let linearSRGB = WI.Color._toLinearLight([r, g, b]);
     396
     397        // Convert an array of linear-light sRGB values to CIE XYZ
     398        // using sRGB's own white, D65 (no chromatic adaptation)
     399        // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
     400        const linearSRGBtoXYZMatrix = [
     401            [0.4124564,  0.3575761,  0.1804375],
     402            [0.2126729,  0.7151522,  0.0721750],
     403            [0.0193339,  0.1191920,  0.9503041],
     404        ];
     405        let xyz = Math.multiplyMatrixByVector(linearSRGBtoXYZMatrix, linearSRGB);
     406
     407        const xyzToLinearP3Matrix = [
     408            [ 2.493496911941425,   -0.9313836179191239, -0.40271078445071684],
     409            [-0.8294889695615747,   1.7626640603183463,  0.023624685841943577],
     410            [ 0.03584583024378447, -0.07617238926804182, 0.9568845240076872],
     411        ];
     412        let linearP3 = Math.multiplyMatrixByVector(xyzToLinearP3Matrix, xyz);
     413
     414        let p3 = WI.Color._gammaCorrect(linearP3);
     415        return p3.map((x) => x.maxDecimals(4));
    393416    }
    394417
    395418    // Convert gamma-corrected sRGB or Display-P3 to linear light form.
     419    // https://www.w3.org/TR/css-color-4/#color-conversion-code
    396420    static _toLinearLight(rgb)
    397421    {
     
    406430    // Convert linear-light sRGB or Display-P3 to gamma corrected form.
    407431    // Inverse of `toLinearLight`.
     432    // https://www.w3.org/TR/css-color-4/#color-conversion-code
    408433    static _gammaCorrect(rgb)
    409434    {
     
    457482        case WI.Color.Format.RGB:
    458483        case WI.Color.Format.RGBA:
    459             return this.simple ? WI.Color.Format.HSL : WI.Color.Format.HSLA;
     484            return WI.Color.Format.ColorFunction;
     485
     486        case WI.Color.Format.ColorFunction:
     487            if (this.simple)
     488                return WI.Color.Format.HSL;
     489            return WI.Color.Format.HSLA;
    460490
    461491        case WI.Color.Format.HSL:
     
    484514    }
    485515
    486     get alpha()
    487     {
    488         return this._rgba ? this._rgba[3] : this._hsla[3];
    489     }
    490 
    491516    get simple()
    492517    {
     
    496521    get rgb()
    497522    {
    498         let rgb = this.rgba.slice();
    499         rgb.pop();
    500         return rgb;
     523        if (!this._rgb) {
     524            if (this._hsl)
     525                this._rgb = WI.Color.hsl2rgb(...this._hsl);
     526            else if (this._normalizedRGB)
     527                this._rgb = this._normalizedRGB.map((component) => WI.Color._eightBitChannel(component * 255));
     528        }
     529        return this._rgb;
    501530    }
    502531
    503532    get hsl()
    504533    {
    505         let hsl = this.hsla.slice();
    506         hsl.pop();
    507         return hsl;
     534        if (!this._hsl)
     535            this._hsl = WI.Color.rgb2hsl(...this.rgb);
     536        return this._hsl;
     537    }
     538
     539    get normalizedRGB()
     540    {
     541        if (!this._normalizedRGB)
     542            this._normalizedRGB = this.rgb.map((component) => component / 255);
     543        return this._normalizedRGB;
    508544    }
    509545
    510546    get rgba()
    511547    {
    512         if (!this._rgba)
    513             this._rgba = this._hslaToRGBA(this._hsla);
    514         return this._rgba;
     548        return [...this.rgb, this.alpha];
    515549    }
    516550
    517551    get hsla()
    518552    {
    519         if (!this._hsla) {
    520             let rgba = this.rgba;
    521             if (this.format === WI.Color.Format.ColorFunction) {
    522                 rgba = [
    523                     rgba[0] * 255,
    524                     rgba[1] * 255,
    525                     rgba[2] * 255,
    526                     rgba[3],
    527                 ];
    528             }
    529             this._hsla = this._rgbaToHSLA(rgba);
    530         }
    531 
    532         return this._hsla;
     553        return [...this.hsl, this.alpha];
     554    }
     555
     556    get normalizedRGBA()
     557    {
     558        return [...this.normalizedRGB, this.alpha];
     559    }
     560
     561    get gamut()
     562    {
     563        return this._gamut;
     564    }
     565
     566    set gamut(gamut)
     567    {
     568        console.assert(gamut !== this._gamut);
     569
     570        if (this._gamut === WI.Color.Gamut.DisplayP3 && gamut === WI.Color.Gamut.SRGB) {
     571            this._normalizedRGB = WI.Color.displayP3toSRGB(...this.normalizedRGB).map((x) => Number.constrain(x, 0, 1));
     572            this._hsl = null;
     573            this._rgb = null;
     574        } else if (this._gamut === WI.Color.Gamut.SRGB && gamut === WI.Color.Gamut.DisplayP3) {
     575            this._normalizedRGB = WI.Color.srgbToDisplayP3(...this.normalizedRGB);
     576            this._hsl = null;
     577            this._rgb = null;
     578
     579            // Display-P3 is only available with the color function syntax.
     580            this.format = WI.Color.Format.ColorFunction;
     581        }
     582
     583        this._gamut = gamut;
    533584    }
    534585
     
    543594        case WI.Color.Format.Keyword:
    544595        case WI.Color.Format.RGBA:
    545         case WI.Color.Format.ColorFunction:
    546             return new WI.Color(this.format, this.rgba, this.gamut);
     596            return new WI.Color(this.format, this.rgba, this._gamut);
    547597        case WI.Color.Format.HSL:
    548598        case WI.Color.Format.HSLA:
    549             return new WI.Color(this.format, this.hsla, this.gamut);
    550         }
     599            return new WI.Color(this.format, this.hsla, this._gamut);
     600        case WI.Color.Format.ColorFunction:
     601            return new WI.Color(this.format, this.normalizedRGBA, this._gamut);
     602        }
     603
     604        console.error("Invalid color format: " + this.format);
    551605    }
    552606
     
    590644            return true;
    591645
    592         if (this.gamut !== WI.Color.Gamut.SRGB)
     646        if (this._gamut !== WI.Color.Gamut.SRGB)
    593647            return false;
    594648
    595649        if (!this.simple)
    596             return Array.shallowEqual(this._rgba, [0, 0, 0, 0]) || Array.shallowEqual(this._hsla, [0, 0, 0, 0]);
    597 
    598         let rgb = (this._rgba && this._rgba.slice(0, 3)) || WI.Color.hsl2rgb(...this._hsla);
    599         return Object.keys(WI.Color.Keywords).some(key => Array.shallowEqual(WI.Color.Keywords[key], rgb));
     650            return Array.shallowEqual(this.rgba, [0, 0, 0, 0]);
     651
     652        return Object.keys(WI.Color.Keywords).some(key => Array.shallowEqual(WI.Color.Keywords[key], this.rgb));
     653    }
     654
     655    isOutsideSRGB()
     656    {
     657        if (this._gamut !== WI.Color.Gamut.DisplayP3)
     658            return false;
     659
     660        let rgb = WI.Color.displayP3toSRGB(...this.normalizedRGB);
     661
     662        // displayP3toSRGB(1, 1, 1) produces [0.9999, 1, 1.0001], which aren't pure white color values.
     663        // However, `color(sRGB 0.9999 1 1.0001)` looks exactly the same as color `color(sRGB 1 1 1)`
     664        // because sRGB is only 8bit per channel. The values get rounded. For example,
     665        // `rgb(255, 254.51, 255)` looks exactly the same as `rgb(255, 255, 255)`.
     666        //
     667        // Consider a color to be within sRGB even if it's actually outside of sRGB by less than half a bit.
     668        const epsilon = (1 / 255) / 2;
     669        return rgb.some((x) => x <= -epsilon || x >= 1 + epsilon);
    600670    }
    601671
    602672    canBeSerializedAsShortHEX()
    603673    {
    604         let rgba = this.rgba || this._hslaToRGBA(this._hsla);
    605 
    606         let r = this._componentToHexValue(rgba[0]);
     674        let rgb = this.rgb;
     675
     676        let r = this._componentToHexValue(rgb[0]);
    607677        if (r[0] !== r[1])
    608678            return false;
    609679
    610         let g = this._componentToHexValue(rgba[1]);
     680        let g = this._componentToHexValue(rgb[1]);
    611681        if (g[0] !== g[1])
    612682            return false;
    613683
    614         let b = this._componentToHexValue(rgba[2]);
     684        let b = this._componentToHexValue(rgb[2]);
    615685        if (b[0] !== b[1])
    616686            return false;
    617687
    618688        if (!this.simple) {
    619             let a = this._componentToHexValue(Math.round(rgba[3] * 255));
     689            let a = this._componentToHexValue(Math.round(this.alpha * 255));
    620690            if (a[0] !== a[1])
    621691                return false;
     
    639709        let rgba = this.rgba;
    640710        if (!this.simple) {
    641             if (rgba[0] === 0 && rgba[1] === 0 && rgba[2] === 0 && rgba[3] === 0)
     711            if (Array.shallowEqual(rgba, [0, 0, 0, 0]))
    642712                return "transparent";
    643713            return this._toRGBAString();
     
    662732            return this._toRGBAString();
    663733
    664         let rgba = this.rgba;
    665         let r = this._componentToHexValue(rgba[0]);
    666         let g = this._componentToHexValue(rgba[1]);
    667         let b = this._componentToHexValue(rgba[2]);
    668 
     734        let [r, g, b] = this.rgb.map(this._componentToHexValue);
    669735        if (r[0] === r[1] && g[0] === g[1] && b[0] === b[1])
    670736            return "#" + r[0] + g[0] + b[0];
    671         else
    672             return "#" + r + g + b;
     737        return "#" + r + g + b;
    673738    }
    674739
     
    678743            return this._toRGBAString();
    679744
    680         let rgba = this.rgba;
    681         let r = this._componentToHexValue(rgba[0]);
    682         let g = this._componentToHexValue(rgba[1]);
    683         let b = this._componentToHexValue(rgba[2]);
    684 
     745        let [r, g, b] = this.rgb.map(this._componentToHexValue);
    685746        return "#" + r + g + b;
    686747    }
     
    688749    _toShortHEXAlphaString()
    689750    {
    690         let rgba = this.rgba;
    691         let r = this._componentToHexValue(rgba[0]);
    692         let g = this._componentToHexValue(rgba[1]);
    693         let b = this._componentToHexValue(rgba[2]);
    694         let a = this._componentToHexValue(Math.round(rgba[3] * 255));
    695 
     751        let [r, g, b] = this.rgb.map(this._componentToHexValue);
     752        let a = this._componentToHexValue(Math.round(this.alpha * 255));
    696753        if (r[0] === r[1] && g[0] === g[1] && b[0] === b[1] && a[0] === a[1])
    697754            return "#" + r[0] + g[0] + b[0] + a[0];
    698         else
    699             return "#" + r + g + b + a;
     755        return "#" + r + g + b + a;
    700756    }
    701757
    702758    _toHEXAlphaString()
    703759    {
    704         let rgba = this.rgba;
    705         let r = this._componentToHexValue(rgba[0]);
    706         let g = this._componentToHexValue(rgba[1]);
    707         let b = this._componentToHexValue(rgba[2]);
    708         let a = this._componentToHexValue(Math.round(rgba[3] * 255));
    709 
     760        let [r, g, b] = this.rgb.map(this._componentToHexValue);
     761        let a = this._componentToHexValue(Math.round(this.alpha * 255));
    710762        return "#" + r + g + b + a;
    711763    }
     
    716768            return this._toRGBAString();
    717769
    718         let r = WI.Color._eightBitChannel(Math.round(this.rgba[0]));
    719         let g = WI.Color._eightBitChannel(Math.round(this.rgba[1]));
    720         let b = WI.Color._eightBitChannel(Math.round(this.rgba[2]));
     770        let [r, g, b] = this.rgb.map(WI.Color._eightBitChannel);
    721771        return `rgb(${r}, ${g}, ${b})`;
    722772    }
     
    724774    _toRGBAString()
    725775    {
    726         let r = WI.Color._eightBitChannel(Math.round(this.rgba[0]));
    727         let g = WI.Color._eightBitChannel(Math.round(this.rgba[1]));
    728         let b = WI.Color._eightBitChannel(Math.round(this.rgba[2]));
     776        let [r, g, b] = this.rgb.map(WI.Color._eightBitChannel);
    729777        return `rgba(${r}, ${g}, ${b}, ${this.alpha})`;
    730778    }
     
    732780    _toFunctionString()
    733781    {
    734         let [r, g, b, alpha] = this.rgba;
    735         if (alpha === 1)
    736             return `color(${this.gamut} ${r} ${g} ${b})`;
    737         return `color(${this.gamut} ${r} ${g} ${b} / ${alpha})`;
     782        let [r, g, b] = this.normalizedRGB.map((x) => x.maxDecimals(4));
     783        if (this.alpha === 1)
     784            return `color(${this._gamut} ${r} ${g} ${b})`;
     785        return `color(${this._gamut} ${r} ${g} ${b} / ${this.alpha})`;
    738786    }
    739787
     
    743791            return this._toHSLAString();
    744792
    745         let h = this.hsla[0].maxDecimals(2);
    746         let s = this.hsla[1].maxDecimals(2);
    747         let l = this.hsla[2].maxDecimals(2);
     793        let [h, s, l] = this.hsl.map((x) => x.maxDecimals(2));
    748794        return `hsl(${h}, ${s}%, ${l}%)`;
    749795    }
     
    751797    _toHSLAString()
    752798    {
    753         let h = this.hsla[0].maxDecimals(2);
    754         let s = this.hsla[1].maxDecimals(2);
    755         let l = this.hsla[2].maxDecimals(2);
     799        let [h, s, l] = this.hsl.map((x) => x.maxDecimals(2));
    756800        return `hsla(${h}, ${s}%, ${l}%, ${this.alpha})`;
    757801    }
     
    763807            hex = "0" + hex;
    764808        return hex;
    765     }
    766 
    767     _rgbaToHSLA(rgba)
    768     {
    769         let hsla = WI.Color.rgb2hsl(...rgba);
    770         hsla.push(rgba[3]);
    771         return hsla;
    772     }
    773 
    774     _hslaToRGBA(hsla)
    775     {
    776         let rgba = WI.Color.hsl2rgb(...hsla);
    777         rgba.push(hsla[3]);
    778         return rgba;
    779809    }
    780810};
  • trunk/Source/WebInspectorUI/UserInterface/Views/ColorPicker.js

    r252168 r253018  
    175175            if (opacity !== 1)
    176176                format = WI.Color.Format.HSLA;
    177         } else {
     177        } else if (format === WI.Color.Format.ColorFunction)
     178            components = this._colorSquare.tintedColor.normalizedRGB.concat(opacity);
     179        else {
    178180            components = this._colorSquare.tintedColor.rgb.concat(opacity);
    179181            if (opacity !== 1 && format === WI.Color.Format.RGB)
     
    195197    _updateOpacitySlider()
    196198    {
    197         let rgb = this._colorSquare.tintedColor.rgb;
    198         let gamut = this._colorSquare.tintedColor.gamut;
     199        let color = this._colorSquare.tintedColor;
     200
     201        let rgb = color.format === WI.Color.Format.ColorFunction ? color.normalizedRGB : color.rgb;
     202        let gamut = color.gamut;
    199203        let format = gamut === WI.Color.Gamut.DisplayP3 ? WI.Color.Format.ColorFunction : WI.Color.Format.RGBA;
    200204        let opaque = new WI.Color(format, rgb.concat(1), gamut).toString();
  • trunk/Source/WebInspectorUI/UserInterface/Views/ColorSquare.css

    r252747 r253018  
    6262}
    6363
    64 .color-square .svg-root {
     64.color-square > .svg-root {
    6565    position: absolute;
    6666    top: 0;
     
    7171}
    7272
    73 .color-square .srgb-edge {
     73.color-square > .svg-root > .srgb-edge {
    7474    fill: none;
    7575    stroke: white;
     
    7878}
    7979
    80 .color-square .srgb-label {
     80.color-square > .srgb-label {
    8181    position: absolute;
    82     padding-right: 3px;
     82    padding-right: 5px;
    8383    color: hsla(0, 0%, 100%, var(--stroke-opacity));
    8484    font-size: 10px;
    8585}
    8686
    87 .color-square .srgb-label:hover {
     87.color-square > .srgb-label:hover {
    8888    color: white;
    8989}
    9090
    91 .color-square .srgb-label:hover + .svg-root > .srgb-edge {
     91.color-square > .srgb-label:hover + .svg-root > .srgb-edge {
    9292    stroke-width: 1px;
    9393}
    9494
    9595@media (-webkit-device-pixel-ratio: 1) {
    96     .color-square .srgb-edge {
     96    .color-square > .srgb-edge {
    9797        stroke-width: 1px;
    9898        stroke-opacity: var(--stroke-opacity) / 2;
  • trunk/Source/WebInspectorUI/UserInterface/Views/ColorSquare.js

    r252747 r253018  
    107107        if (tintedColor.format === WI.Color.Format.ColorFunction) {
    108108            // CSS color function only supports RGB. It doesn't support HSL.
    109             let hsv = WI.Color.rgb2hsv(...tintedColor.rgb);
     109            let hsv = WI.Color.rgb2hsv(...tintedColor.normalizedRGB);
    110110            let x = hsv[1] / 100 * this._dimension;
    111111            let y = (1 - (hsv[2] / 100)) * this._dimension;
     
    230230            this._srgbLabelElement.className = "srgb-label";
    231231            this._srgbLabelElement.textContent = WI.unlocalizedString("sRGB");
    232             this._srgbLabelElement.title = WI.UIString("This line marks the edge of sRGB color space", "Label for a guide within the color picker");
     232            this._srgbLabelElement.title = WI.UIString("Edge of sRGB color space", "Label for a guide within the color picker");
    233233
    234234            const svgNamespace = "http://www.w3.org/2000/svg";
    235             this._svgElement = document.createElementNS(svgNamespace, "svg");
     235            this._svgElement = this._element.appendChild(document.createElementNS(svgNamespace, "svg"));
    236236            this._svgElement.classList.add("svg-root");
    237             this._element.append(this._svgElement);
    238237
    239238            this._polylineElement = this._svgElement.appendChild(document.createElementNS(svgNamespace, "polyline"));
     
    245244        let x = 0;
    246245        for (let y = 0; y < this._dimension; y += step) {
    247             let value = 100 - (y / this._dimension) * 100;
     246            let value = 100 - ((y / this._dimension) * 100);
    248247
    249248            // Optimization: instead of starting from x = 0, we can benefit from the fact that the next point
     
    253252                let rgb = WI.Color.hsv2rgb(this._hue, saturation, value);
    254253                let srgb = WI.Color.displayP3toSRGB(rgb[0], rgb[1], rgb[2]);
    255                 if (srgb.some((value) => value > 1 || value < 0)) {
     254                if (srgb.some((value) => value < 0 || value > 1)) {
    256255                    // The point is outside of sRGB.
    257256                    points.push({x, y});
     
    275274
    276275        this._polylineElement.points.clear();
    277         for (let point of points) {
     276        for (let {x, y} of points) {
    278277            let svgPoint = this._svgElement.createSVGPoint();
    279             svgPoint.x = point.x;
    280             svgPoint.y = point.y;
     278            svgPoint.x = x;
     279            svgPoint.y = y;
    281280            this._polylineElement.points.appendItem(svgPoint);
    282281        }
  • trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js

    r252168 r253018  
    4747            switch (this._type) {
    4848            case WI.InlineSwatch.Type.Color:
    49                 this._swatchElement.title = WI.UIString("Click to select a color\nShift-click to switch color formats");
     49                // Handled later by _updateSwatch.
    5050                break;
    5151            case WI.InlineSwatch.Type.Gradient:
     
    7878        this._value = value || this._fallbackValue();
    7979        this._valueEditor = null;
     80        this._readOnly = readOnly;
    8081
    8182        this._updateSwatch();
     
    147148            this._swatchInnerElement.style.setProperty("background-image", `url(${value.src})`);
    148149
     150        if (this._type === WI.InlineSwatch.Type.Color) {
     151            if (this._allowShiftClickColor())
     152                this._swatchElement.title = WI.UIString("Click to select a color\nShift-click to switch color formats");
     153            else
     154                this._swatchElement.title = WI.UIString("Click to select a color");
     155        }
     156
    149157        if (!dontFireEvents)
    150158            this.dispatchEventToListeners(WI.InlineSwatch.Event.ValueChanged, {value});
    151159    }
    152160
     161    _allowShiftClickColor()
     162    {
     163        return !this._readOnly && !this.value.isOutsideSRGB();
     164    }
     165
    153166    _swatchElementClicked(event)
    154167    {
     
    159172        if (event.shiftKey && value) {
    160173            if (this._type === WI.InlineSwatch.Type.Color) {
     174                if (!this._allowShiftClickColor()) {
     175                    InspectorFrontendHost.beep();
     176                    return;
     177                }
     178
    161179                let nextFormat = value.nextFormat();
    162                 // FIXME: <https://webkit.org/b/203534> Provide UI to convert between sRGB and p3 color spaces
    163180                console.assert(nextFormat);
    164181                if (nextFormat) {
     
    314331
    315332        let contextMenu = WI.ContextMenu.createFromEvent(event);
    316 
    317         if (value.isKeyword() && value.format !== WI.Color.Format.Keyword) {
    318             contextMenu.appendItem(WI.UIString("Format: Keyword"), () => {
    319                 value.format = WI.Color.Format.Keyword;
     333        let isColorOutsideSRGB = value.isOutsideSRGB();
     334
     335        if (!isColorOutsideSRGB) {
     336            if (value.isKeyword() && value.format !== WI.Color.Format.Keyword) {
     337                contextMenu.appendItem(WI.UIString("Format: Keyword"), () => {
     338                    value.format = WI.Color.Format.Keyword;
     339                    this._updateSwatch();
     340                });
     341            }
     342
     343            let hexInfo = this._getNextValidHEXFormat();
     344            if (hexInfo) {
     345                contextMenu.appendItem(hexInfo.title, () => {
     346                    value.format = hexInfo.format;
     347                    this._updateSwatch();
     348                });
     349            }
     350
     351            if (value.simple && value.format !== WI.Color.Format.HSL) {
     352                contextMenu.appendItem(WI.UIString("Format: HSL"), () => {
     353                    value.format = WI.Color.Format.HSL;
     354                    this._updateSwatch();
     355                });
     356            } else if (value.format !== WI.Color.Format.HSLA) {
     357                contextMenu.appendItem(WI.UIString("Format: HSLA"), () => {
     358                    value.format = WI.Color.Format.HSLA;
     359                    this._updateSwatch();
     360                });
     361            }
     362
     363            if (value.simple && value.format !== WI.Color.Format.RGB) {
     364                contextMenu.appendItem(WI.UIString("Format: RGB"), () => {
     365                    value.format = WI.Color.Format.RGB;
     366                    this._updateSwatch();
     367                });
     368            } else if (value.format !== WI.Color.Format.RGBA) {
     369                contextMenu.appendItem(WI.UIString("Format: RGBA"), () => {
     370                    value.format = WI.Color.Format.RGBA;
     371                    this._updateSwatch();
     372                });
     373            }
     374
     375            if (value.format !== WI.Color.Format.ColorFunction) {
     376                contextMenu.appendItem(WI.UIString("Format: Color Function"), () => {
     377                    value.format = WI.Color.Format.ColorFunction;
     378                    this._updateSwatch();
     379                });
     380            }
     381
     382            contextMenu.appendSeparator();
     383        }
     384
     385        if (value.gamut !== WI.Color.Gamut.DisplayP3) {
     386            contextMenu.appendItem(WI.UIString("Convert to Display-P3"), () => {
     387                value.gamut = WI.Color.Gamut.DisplayP3;
    320388                this._updateSwatch();
    321389            });
    322390        }
    323391
    324         let hexInfo = this._getNextValidHEXFormat();
    325         if (hexInfo) {
    326             contextMenu.appendItem(hexInfo.title, () => {
    327                 value.format = hexInfo.format;
    328                 this._updateSwatch();
    329             });
    330         }
    331 
    332         if (value.simple && value.format !== WI.Color.Format.HSL) {
    333             contextMenu.appendItem(WI.UIString("Format: HSL"), () => {
    334                 value.format = WI.Color.Format.HSL;
    335                 this._updateSwatch();
    336             });
    337         } else if (value.format !== WI.Color.Format.HSLA) {
    338             contextMenu.appendItem(WI.UIString("Format: HSLA"), () => {
    339                 value.format = WI.Color.Format.HSLA;
    340                 this._updateSwatch();
    341             });
    342         }
    343 
    344         if (value.simple && value.format !== WI.Color.Format.RGB) {
    345             contextMenu.appendItem(WI.UIString("Format: RGB"), () => {
    346                 value.format = WI.Color.Format.RGB;
    347                 this._updateSwatch();
    348             });
    349         } else if (value.format !== WI.Color.Format.RGBA) {
    350             contextMenu.appendItem(WI.UIString("Format: RGBA"), () => {
    351                 value.format = WI.Color.Format.RGBA;
     392        if (value.gamut !== WI.Color.Gamut.SRGB) {
     393            let label = isColorOutsideSRGB ? WI.UIString("Clamp to sRGB") : WI.UIString("Convert to sRGB");
     394            contextMenu.appendItem(label, () => {
     395                value.gamut = WI.Color.Gamut.SRGB;
    352396                this._updateSwatch();
    353397            });
Note: See TracChangeset for help on using the changeset viewer.