Changeset 252747 in webkit


Ignore:
Timestamp:
Nov 21, 2019 1:11:07 PM (4 years ago)
Author:
Nikita Vasilyev
Message:

Web Inspector: Outline sRGB-safe areas on P3 color picker
https://bugs.webkit.org/show_bug.cgi?id=203533
<rdar://problem/56688057>

Reviewed by Brian Burg.

Source/WebInspectorUI:

Visualize the edge of sRGB gamut as a white line.

  • Localizations/en.lproj/localizedStrings.js:
  • UserInterface/Models/Color.js:

(WI.Color.displayP3toSRGB.multiplyMatrixByVector):
(WI.Color.displayP3toSRGB):
(WI.Color._toLinearLight):
(WI.Color._gammaCorrect):
Use equations from CSS Color Module Level 4.

  • UserInterface/Views/ColorSquare.css:

(.color-square):
(.color-square > .crosshair):
Place the crosshair above the sRGB edge line.

(.color-square .svg-root):
(.color-square .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)):
Make the optical weight of the line on non-retina screens the same as on retina screens.

  • UserInterface/Views/ColorSquare.js:

(WI.ColorSquare):
(WI.ColorSquare.prototype.set tintedColor):
(WI.ColorSquare.prototype._updateBaseColor):
(WI.ColorSquare.prototype._drawSRGBOutline):

LayoutTests:

Test WI.Color.displayP3toSRGB.

LayoutTests:

  • inspector/model/color-expected.txt:
  • inspector/model/color.html:
Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r252744 r252747  
     12019-11-21  Nikita Vasilyev  <nvasilyev@apple.com>
     2
     3        Web Inspector: Outline sRGB-safe areas on P3 color picker
     4        https://bugs.webkit.org/show_bug.cgi?id=203533
     5        <rdar://problem/56688057>
     6
     7        Reviewed by Brian Burg.
     8
     9        Test WI.Color.displayP3toSRGB.
     10
     11        LayoutTests:
     12        * inspector/model/color-expected.txt:
     13        * inspector/model/color.html:
     14
    1152019-11-21  Kate Cheney  <katherine_cheney@apple.com>
    216
  • trunk/LayoutTests/inspector/model/color-expected.txt

    r252168 r252747  
    519519PASS: Should convert [-1,0,0] to [0,0,0].
    520520
     521-- Running test case: WI.Color.displayP3toSRGB
     522PASS: Should convert [0,0,0] to [0,0,0].
     523PASS: Should convert [1,1,1] to [0.9999300658875597,1.0000101408119602,1.0001054916006267].
     524PASS: Should convert [1,0,0] to [1.0929902391315327,-0.5433883521407902,-0.2537782522695441].
     525PASS: Should convert [0,1,0] to [-2.9057643199691516,1.0182759798644396,-1.0162215225472337].
     526PASS: Should convert [0,0,1] to [2.3902361583783006e-7,-2.0487738563140788e-7,1.0421313171983129].
     527PASS: Should convert [2,0,0] to [1.0929902391315327,-0.5433883521407902,-0.2537782522695441].
     528PASS: Should convert [-1,0,0] to [0,0,0].
     529
  • trunk/LayoutTests/inspector/model/color.html

    r252168 r252747  
    613613    });
    614614
     615    suite.addTestCase({
     616        name: "WI.Color.displayP3toSRGB",
     617        description: "Test conversion from display-P3 gamut to sRGB.",
     618        test() {
     619            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
     625            // Out-of-bounds inputs.
     626            testColorConversion(WI.Color.displayP3toSRGB, [2, 0, 0], [1.0929902391315327, -0.5433883521407902, -0.2537782522695441]);
     627            testColorConversion(WI.Color.displayP3toSRGB, [-1, 0, 0], [0, 0, 0]);
     628
     629            return true;
     630        }
     631    });
     632
    615633    suite.runTestCasesAndFinish();
    616634}
  • trunk/Source/WebInspectorUI/ChangeLog

    r252745 r252747  
     12019-11-21  Nikita Vasilyev  <nvasilyev@apple.com>
     2
     3        Web Inspector: Outline sRGB-safe areas on P3 color picker
     4        https://bugs.webkit.org/show_bug.cgi?id=203533
     5        <rdar://problem/56688057>
     6
     7        Reviewed by Brian Burg.
     8
     9        Visualize the edge of sRGB gamut as a white line.
     10
     11        * Localizations/en.lproj/localizedStrings.js:
     12
     13        * UserInterface/Models/Color.js:
     14        (WI.Color.displayP3toSRGB.multiplyMatrixByVector):
     15        (WI.Color.displayP3toSRGB):
     16        (WI.Color._toLinearLight):
     17        (WI.Color._gammaCorrect):
     18        Use equations from CSS Color Module Level 4.
     19
     20        * UserInterface/Views/ColorSquare.css:
     21        (.color-square):
     22        (.color-square > .crosshair):
     23        Place the crosshair above the sRGB edge line.
     24
     25        (.color-square .svg-root):
     26        (.color-square .srgb-edge):
     27        (.color-square .srgb-label):
     28        (.color-square .srgb-label:hover):
     29        (.color-square .srgb-label:hover + .svg-root > .srgb-edge):
     30        (@media (-webkit-device-pixel-ratio: 1)):
     31        Make the optical weight of the line on non-retina screens the same as on retina screens.
     32
     33        * UserInterface/Views/ColorSquare.js:
     34        (WI.ColorSquare):
     35        (WI.ColorSquare.prototype.set tintedColor):
     36        (WI.ColorSquare.prototype._updateBaseColor):
     37        (WI.ColorSquare.prototype._drawSRGBOutline):
     38
    1392019-11-21  Devin Rousso  <drousso@apple.com>
    240
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r252745 r252747  
    11761176localizedStrings["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.";
    11771177localizedStrings["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 */
     1179localizedStrings["This line marks the edge of sRGB color space"] = "This line marks the edge of sRGB color space";
    11781180localizedStrings["This object is a root"] = "This object is a root";
    11791181localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects";
  • trunk/Source/WebInspectorUI/UserInterface/Models/Color.js

    r252448 r252747  
    347347        }
    348348        return [fraction(5), fraction(3), fraction(1)];
     349    }
     350
     351    // https://www.w3.org/TR/css-color-4/#color-conversion-code
     352    static displayP3toSRGB(r, g, b)
     353    {
     354        r = Number.constrain(r, 0, 1);
     355        g = Number.constrain(g, 0, 1);
     356        b = Number.constrain(b, 0, 1);
     357
     358        let linearP3 = WI.Color._toLinearLight([r, g, b]);
     359
     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
     374        // Convert an array of linear-light display-p3 values to CIE XYZ
     375        // using D65 (no chromatic adaptation).
     376        // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
     377        let matrix = [
     378            [0.4865709486482162, 0.26566769316909306, 0.1982172852343625],
     379            [0.2289745640697488, 0.6917385218365064,  0.079286914093745],
     380            [0.0000000000000000, 0.04511338185890264, 1.043944368900976]
     381        ];
     382        let xyz = multiplyMatrixByVector(matrix, linearP3);
     383
     384        // Convert XYZ to linear-light sRGB.
     385        matrix = [
     386            [ 3.2404542, -1.5371385, -0.4985314],
     387            [-0.9692660,  1.8760108,  0.0415560],
     388            [ 0.0556434, -0.2040259,  1.0572252]
     389        ];
     390        let linearSRGB = multiplyMatrixByVector(matrix, xyz);
     391
     392        return WI.Color._gammaCorrect(linearSRGB);
     393    }
     394
     395    // Convert gamma-corrected sRGB or Display-P3 to linear light form.
     396    static _toLinearLight(rgb)
     397    {
     398        return rgb.map(function(value) {
     399            if (value < 0.04045)
     400                return value / 12.92;
     401
     402            return Math.pow((value + 0.055) / 1.055, 2.4);
     403        });
     404    }
     405
     406    // Convert linear-light sRGB or Display-P3 to gamma corrected form.
     407    // Inverse of `toLinearLight`.
     408    static _gammaCorrect(rgb)
     409    {
     410        return rgb.map(function(value) {
     411            if (value > 0.0031308)
     412                return 1.055 * Math.pow(value, 1 / 2.4) - 0.055;
     413
     414            return 12.92 * value;
     415        });
    349416    }
    350417
  • trunk/Source/WebInspectorUI/UserInterface/Views/ColorSquare.css

    r252168 r252747  
    2626.color-square {
    2727    position: relative;
     28
     29    --stroke-opacity: 0.8;
    2830}
    2931
     
    5456    border-radius: 3px;
    5557    pointer-events: none;
     58    z-index: 9;
    5659
    5760    --border-width: 1px;
    5861    --crosshair-size: 7px;
    5962}
     63
     64.color-square .svg-root {
     65    position: absolute;
     66    top: 0;
     67    left: 0;
     68    width: 100%;
     69    height: 100%;
     70    pointer-events: none;
     71}
     72
     73.color-square .srgb-edge {
     74    fill: none;
     75    stroke: white;
     76    stroke-width: 0.5px;
     77    stroke-opacity: var(--stroke-opacity);
     78}
     79
     80.color-square .srgb-label {
     81    position: absolute;
     82    padding-right: 3px;
     83    color: hsla(0, 0%, 100%, var(--stroke-opacity));
     84    font-size: 10px;
     85}
     86
     87.color-square .srgb-label:hover {
     88    color: white;
     89}
     90
     91.color-square .srgb-label:hover + .svg-root > .srgb-edge {
     92    stroke-width: 1px;
     93}
     94
     95@media (-webkit-device-pixel-ratio: 1) {
     96    .color-square .srgb-edge {
     97        stroke-width: 1px;
     98        stroke-opacity: var(--stroke-opacity) / 2;
     99    }
     100}
  • trunk/Source/WebInspectorUI/UserInterface/Views/ColorSquare.js

    r252168 r252747  
    4545        lightnessGradientElement.className = "lightness-gradient fill";
    4646
     47        this._srgbLabelElement = null;
     48        this._svgElement = null;
     49        this._polylineElement = null;
     50
    4751        this._element.addEventListener("mousedown", this);
    4852
     
    107111            let y = (1 - (hsv[2] / 100)) * this._dimension;
    108112            this._setCrosshairPosition(new WI.Point(x, y));
     113            if (this._gamut === WI.Color.Gamut.DisplayP3)
     114                this._drawSRGBOutline();
    109115        } else {
    110116            let hsl = tintedColor.hsl;
     
    208214
    209215        this._updateCrosshairBackground();
     216
     217        if (this._gamut === WI.Color.Gamut.DisplayP3)
     218            this._drawSRGBOutline();
    210219    }
    211220
     
    214223        this._crosshairElement.style.backgroundColor = this.tintedColor.toString();
    215224    }
     225
     226    _drawSRGBOutline()
     227    {
     228        if (!this._svgElement) {
     229            this._srgbLabelElement = this._element.appendChild(document.createElement("span"));
     230            this._srgbLabelElement.className = "srgb-label";
     231            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");
     233
     234            const svgNamespace = "http://www.w3.org/2000/svg";
     235            this._svgElement = document.createElementNS(svgNamespace, "svg");
     236            this._svgElement.classList.add("svg-root");
     237            this._element.append(this._svgElement);
     238
     239            this._polylineElement = this._svgElement.appendChild(document.createElementNS(svgNamespace, "polyline"));
     240            this._polylineElement.classList.add("srgb-edge");
     241        }
     242
     243        let points = [];
     244        let step = 1 / window.devicePixelRatio;
     245        let x = 0;
     246        for (let y = 0; y < this._dimension; y += step) {
     247            let value = 100 - (y / this._dimension) * 100;
     248
     249            // Optimization: instead of starting from x = 0, we can benefit from the fact that the next point
     250            // always has x >= of the current x. This minimizes processing time over 100 times.
     251            for (; x < this._dimension; x += step) {
     252                let saturation = x / this._dimension * 100;
     253                let rgb = WI.Color.hsv2rgb(this._hue, saturation, value);
     254                let srgb = WI.Color.displayP3toSRGB(rgb[0], rgb[1], rgb[2]);
     255                if (srgb.some((value) => value > 1 || value < 0)) {
     256                    // The point is outside of sRGB.
     257                    points.push({x, y});
     258                    break;
     259                }
     260            }
     261        }
     262
     263        if (points.lastValue.y < this._dimension * 0.95) {
     264            // For `color(display-p3 0 0 1)`, the line is almost horizontal.
     265            // Position the label directly under the line.
     266            points.push({x: this._dimension, y: points.lastValue.y});
     267            this._srgbLabelElement.style.removeProperty("bottom");
     268            this._srgbLabelElement.style.top = `${points.lastValue.y}px`;
     269        } else {
     270            this._srgbLabelElement.style.removeProperty("top");
     271            this._srgbLabelElement.style.bottom = "0px";
     272        }
     273
     274        this._srgbLabelElement.style.right = `${this._dimension - points.lastValue.x}px`;
     275
     276        this._polylineElement.points.clear();
     277        for (let point of points) {
     278            let svgPoint = this._svgElement.createSVGPoint();
     279            svgPoint.x = point.x;
     280            svgPoint.y = point.y;
     281            this._polylineElement.points.appendItem(svgPoint);
     282        }
     283    }
    216284};
Note: See TracChangeset for help on using the changeset viewer.