Changeset 253018 in webkit
- Timestamp:
- Dec 2, 2019 5:46:11 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r253017 r253018 1 2019-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 1 12 2019-12-02 Said Abou-Hallawa <sabouhallawa@apple.com> 2 13 -
trunk/LayoutTests/inspector/model/color-expected.txt
r252747 r253018 397 397 PASS: 'color(display-p3 0 1 0.5)' color should have a default alpha of 1 398 398 PASS: color function is not a keyword 399 PASS: 'color(display-p3 0 1 0.5)' has rgbof [0, 1, 0.5]400 PASS: 'color(display-p3 0 1 0.5)' has rgbaof [0, 1, 0.5, 1]399 PASS: 'color(display-p3 0 1 0.5)' has normalized RGB of [0, 1, 0.5] 400 PASS: 'color(display-p3 0 1 0.5)' has normalized RGBA of [0, 1, 0.5, 1] 401 401 PASS: 'color(display-p3 0 1 0.5 / 0.3)' should have an alpha of 0.3 402 402 PASS: color function is not a keyword 403 PASS: 'color(display-p3 0 1 0.5 / 0.3)' has rgbof [0, 1, 0.5]404 PASS: 'color(display-p3 0 1 0.5 / 0.3)' has rgbaof [0, 1, 0.5, 0.3]403 PASS: 'color(display-p3 0 1 0.5 / 0.3)' has normalized RGB of [0, 1, 0.5] 404 PASS: 'color(display-p3 0 1 0.5 / 0.3)' has normalized RGBA of [0, 1, 0.5, 0.3] 405 405 406 406 -- Running test case: WI.Color from components … … 521 521 -- Running test case: WI.Color.displayP3toSRGB 522 522 PASS: Should convert [0,0,0] to [0,0,0]. 523 PASS: Should convert [1,1,1] to [0.9999 300658875597,1.0000101408119602,1.0001054916006267].524 PASS: Should convert [1,0,0] to [1.09 29902391315327,-0.5433883521407902,-0.2537782522695441].525 PASS: Should convert [0,1,0] to [-2.905 7643199691516,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.09 29902391315327,-0.5433883521407902,-0.2537782522695441].523 PASS: Should convert [1,1,1] to [0.9999,1,1.0001]. 524 PASS: Should convert [1,0,0] to [1.093,-0.5434,-0.2538]. 525 PASS: Should convert [0,1,0] to [-2.9058,1.0183,-1.0162]. 526 PASS: Should convert [0,0,1] to [0,0,1.0421]. 527 PASS: Should convert [2,0,0] to [1.093,-0.5434,-0.2538]. 528 528 PASS: Should convert [-1,0,0] to [0,0,0]. 529 529 530 -- Running test case: WI.Color.srgbToDisplayP3 531 PASS: Should convert [0,0,0] to [0,0,0]. 532 PASS: Should convert [1,1,1] to [1.0001,1,0.9999]. 533 PASS: Should convert [1,0,0] to [0.9176,0.2003,0.1386]. 534 PASS: Should convert [0,1,0] to [0.4584,0.9853,0.2983]. 535 PASS: Should convert [0,0,1] to [0,0,0.9595]. 536 PASS: Should convert [2,0,0] to [0.9176,0.2003,0.1386]. 537 PASS: 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 324 324 InspectorTest.expectThat(color.alpha === 1, "'color(display-p3 0 1 0.5)' color should have a default alpha of 1"); 325 325 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 rgbof [0, 1, 0.5]");327 InspectorTest.expectShallowEqual(color. rgba, [0, 1, 0.5, 1], "'color(display-p3 0 1 0.5)' has rgbaof [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]"); 328 328 329 329 color = WI.Color.fromString("color(display-p3 0 1 0.5 / 0.3)"); 330 330 InspectorTest.expectThat(color.alpha === 0.3, "'color(display-p3 0 1 0.5 / 0.3)' should have an alpha of 0.3"); 331 331 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 rgbof [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 rgbaof [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]"); 334 334 335 335 return true; … … 403 403 test("transparent", [ 404 404 WI.Color.Format.RGBA, 405 WI.Color.Format.ColorFunction, 405 406 WI.Color.Format.HSLA, 406 407 WI.Color.Format.Keyword, … … 412 413 test("red", [ 413 414 WI.Color.Format.RGB, 415 WI.Color.Format.ColorFunction, 414 416 WI.Color.Format.HSL, 415 417 WI.Color.Format.Keyword, … … 421 423 test("rgb(100, 150, 200)", [ 422 424 WI.Color.Format.RGB, 425 WI.Color.Format.ColorFunction, 423 426 WI.Color.Format.HSL, 424 427 WI.Color.Format.HEX, … … 428 431 test("rgba(100, 150, 200, 0.5)", [ 429 432 WI.Color.Format.RGBA, 433 WI.Color.Format.ColorFunction, 430 434 WI.Color.Format.HSLA, 431 435 WI.Color.Format.HEXAlpha, … … 618 622 test() { 619 623 testColorConversion(WI.Color.displayP3toSRGB, [0, 0, 0], [0, 0, 0]); 620 testColorConversion(WI.Color.displayP3toSRGB, [1, 1, 1], [0.9999 300658875597, 1.0000101408119602, 1.0001054916006267]);621 testColorConversion(WI.Color.displayP3toSRGB, [1, 0, 0], [1.09 29902391315327, -0.5433883521407902, -0.2537782522695441]);622 testColorConversion(WI.Color.displayP3toSRGB, [0, 1, 0], [-2.905 7643199691516, 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]); 624 628 625 629 // Out-of-bounds inputs. 626 testColorConversion(WI.Color.displayP3toSRGB, [2, 0, 0], [1.09 29902391315327, -0.5433883521407902, -0.2537782522695441]);630 testColorConversion(WI.Color.displayP3toSRGB, [2, 0, 0], [1.093, -0.5434, -0.2538]); 627 631 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. 628 670 629 671 return true; -
trunk/Source/WebInspectorUI/ChangeLog
r253000 r253018 1 2019-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 1 78 2019-12-02 Devin Rousso <drousso@apple.com> 2 79 -
trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
r252747 r253018 241 241 localizedStrings["Children"] = "Children"; 242 242 localizedStrings["Cipher"] = "Cipher"; 243 localizedStrings["Clamp to sRGB"] = "Clamp to sRGB"; 243 244 localizedStrings["Classes"] = "Classes"; 244 245 localizedStrings["Clear Filters"] = "Clear Filters"; … … 255 256 localizedStrings["Click to create a Local Override from this content"] = "Click to create a Local Override from this content"; 256 257 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"; 258 localizedStrings["Click to select a color"] = "Click to select a color"; 257 259 localizedStrings["Click to select a color\nShift-click to switch color formats"] = "Click to select a color\nShift-click to switch color formats"; 258 260 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"; … … 305 307 localizedStrings["Continue without automatically stopping"] = "Continue without automatically stopping"; 306 308 localizedStrings["Controls"] = "Controls"; 309 localizedStrings["Convert to Display-P3"] = "Convert to Display-P3"; 310 localizedStrings["Convert to sRGB"] = "Convert to sRGB"; 307 311 localizedStrings["Cookies"] = "Cookies"; 308 312 localizedStrings["Copy"] = "Copy"; … … 408 412 localizedStrings["Dynamically calculated for the selected element"] = "Dynamically calculated for the selected element"; 409 413 localizedStrings["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 */ 415 localizedStrings["Edge of sRGB color space"] = "Edge of sRGB color space"; 410 416 localizedStrings["Edit"] = "Edit"; 411 417 localizedStrings["Edit Breakpoint\u2026"] = "Edit Breakpoint\u2026"; … … 541 547 /* A context menu item to force (override) a DOM node's pseudo-classes */ 542 548 localizedStrings["Forced Pseudo-Classes"] = "Forced Pseudo-Classes"; 549 localizedStrings["Format: Color Function"] = "Format: Color Function"; 543 550 localizedStrings["Format: HSL"] = "Format: HSL"; 544 551 localizedStrings["Format: HSLA"] = "Format: HSLA"; … … 1176 1183 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."; 1177 1184 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."; 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";1180 1185 localizedStrings["This object is a root"] = "This object is a root"; 1181 1186 localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects"; -
trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js
r251531 r253018 1212 1212 }); 1213 1213 1214 // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web#Multiplying_a_matrix_and_a_point 1215 Object.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 1214 1233 Object.defineProperty(Number, "constrain", 1215 1234 { -
trunk/Source/WebInspectorUI/UserInterface/Models/Color.js
r252747 r253018 35 35 36 36 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; 41 45 42 46 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); 44 50 else 45 this._rgb a = components;51 this._rgb = components.slice(0, 3); 46 52 47 53 this.valid = !components.some(isNaN); … … 358 364 let linearP3 = WI.Color._toLinearLight([r, g, b]); 359 365 360 // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web#Multiplying_a_matrix_and_a_point361 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 366 // Convert an array of linear-light display-p3 values to CIE XYZ 375 367 // using D65 (no chromatic adaptation). 376 368 // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html 377 let matrix = [369 const rgbToXYZMatrix = [ 378 370 [0.4865709486482162, 0.26566769316909306, 0.1982172852343625], 379 371 [0.2289745640697488, 0.6917385218365064, 0.079286914093745], 380 [0.0000000000000000, 0.04511338185890264, 1.043944368900976] 372 [0.0000000000000000, 0.04511338185890264, 1.043944368900976], 381 373 ]; 382 let xyz = multiplyMatrixByVector(matrix, linearP3);374 let xyz = Math.multiplyMatrixByVector(rgbToXYZMatrix, linearP3); 383 375 384 376 // Convert XYZ to linear-light sRGB. 385 matrix = [377 const xyzToLinearSRGBMatrix = [ 386 378 [ 3.2404542, -1.5371385, -0.4985314], 387 379 [-0.9692660, 1.8760108, 0.0415560], 388 [ 0.0556434, -0.2040259, 1.0572252] 380 [ 0.0556434, -0.2040259, 1.0572252], 389 381 ]; 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)); 393 416 } 394 417 395 418 // Convert gamma-corrected sRGB or Display-P3 to linear light form. 419 // https://www.w3.org/TR/css-color-4/#color-conversion-code 396 420 static _toLinearLight(rgb) 397 421 { … … 406 430 // Convert linear-light sRGB or Display-P3 to gamma corrected form. 407 431 // Inverse of `toLinearLight`. 432 // https://www.w3.org/TR/css-color-4/#color-conversion-code 408 433 static _gammaCorrect(rgb) 409 434 { … … 457 482 case WI.Color.Format.RGB: 458 483 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; 460 490 461 491 case WI.Color.Format.HSL: … … 484 514 } 485 515 486 get alpha()487 {488 return this._rgba ? this._rgba[3] : this._hsla[3];489 }490 491 516 get simple() 492 517 { … … 496 521 get rgb() 497 522 { 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; 501 530 } 502 531 503 532 get hsl() 504 533 { 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; 508 544 } 509 545 510 546 get rgba() 511 547 { 512 if (!this._rgba) 513 this._rgba = this._hslaToRGBA(this._hsla); 514 return this._rgba; 548 return [...this.rgb, this.alpha]; 515 549 } 516 550 517 551 get hsla() 518 552 { 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; 533 584 } 534 585 … … 543 594 case WI.Color.Format.Keyword: 544 595 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); 547 597 case WI.Color.Format.HSL: 548 598 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); 551 605 } 552 606 … … 590 644 return true; 591 645 592 if (this. gamut !== WI.Color.Gamut.SRGB)646 if (this._gamut !== WI.Color.Gamut.SRGB) 593 647 return false; 594 648 595 649 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); 600 670 } 601 671 602 672 canBeSerializedAsShortHEX() 603 673 { 604 let rgb a = this.rgba || this._hslaToRGBA(this._hsla);605 606 let r = this._componentToHexValue(rgb a[0]);674 let rgb = this.rgb; 675 676 let r = this._componentToHexValue(rgb[0]); 607 677 if (r[0] !== r[1]) 608 678 return false; 609 679 610 let g = this._componentToHexValue(rgb a[1]);680 let g = this._componentToHexValue(rgb[1]); 611 681 if (g[0] !== g[1]) 612 682 return false; 613 683 614 let b = this._componentToHexValue(rgb a[2]);684 let b = this._componentToHexValue(rgb[2]); 615 685 if (b[0] !== b[1]) 616 686 return false; 617 687 618 688 if (!this.simple) { 619 let a = this._componentToHexValue(Math.round( rgba[3]* 255));689 let a = this._componentToHexValue(Math.round(this.alpha * 255)); 620 690 if (a[0] !== a[1]) 621 691 return false; … … 639 709 let rgba = this.rgba; 640 710 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])) 642 712 return "transparent"; 643 713 return this._toRGBAString(); … … 662 732 return this._toRGBAString(); 663 733 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); 669 735 if (r[0] === r[1] && g[0] === g[1] && b[0] === b[1]) 670 736 return "#" + r[0] + g[0] + b[0]; 671 else 672 return "#" + r + g + b; 737 return "#" + r + g + b; 673 738 } 674 739 … … 678 743 return this._toRGBAString(); 679 744 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); 685 746 return "#" + r + g + b; 686 747 } … … 688 749 _toShortHEXAlphaString() 689 750 { 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)); 696 753 if (r[0] === r[1] && g[0] === g[1] && b[0] === b[1] && a[0] === a[1]) 697 754 return "#" + r[0] + g[0] + b[0] + a[0]; 698 else 699 return "#" + r + g + b + a; 755 return "#" + r + g + b + a; 700 756 } 701 757 702 758 _toHEXAlphaString() 703 759 { 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)); 710 762 return "#" + r + g + b + a; 711 763 } … … 716 768 return this._toRGBAString(); 717 769 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); 721 771 return `rgb(${r}, ${g}, ${b})`; 722 772 } … … 724 774 _toRGBAString() 725 775 { 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); 729 777 return `rgba(${r}, ${g}, ${b}, ${this.alpha})`; 730 778 } … … 732 780 _toFunctionString() 733 781 { 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})`; 738 786 } 739 787 … … 743 791 return this._toHSLAString(); 744 792 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)); 748 794 return `hsl(${h}, ${s}%, ${l}%)`; 749 795 } … … 751 797 _toHSLAString() 752 798 { 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)); 756 800 return `hsla(${h}, ${s}%, ${l}%, ${this.alpha})`; 757 801 } … … 763 807 hex = "0" + hex; 764 808 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;779 809 } 780 810 }; -
trunk/Source/WebInspectorUI/UserInterface/Views/ColorPicker.js
r252168 r253018 175 175 if (opacity !== 1) 176 176 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 { 178 180 components = this._colorSquare.tintedColor.rgb.concat(opacity); 179 181 if (opacity !== 1 && format === WI.Color.Format.RGB) … … 195 197 _updateOpacitySlider() 196 198 { 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; 199 203 let format = gamut === WI.Color.Gamut.DisplayP3 ? WI.Color.Format.ColorFunction : WI.Color.Format.RGBA; 200 204 let opaque = new WI.Color(format, rgb.concat(1), gamut).toString(); -
trunk/Source/WebInspectorUI/UserInterface/Views/ColorSquare.css
r252747 r253018 62 62 } 63 63 64 .color-square .svg-root {64 .color-square > .svg-root { 65 65 position: absolute; 66 66 top: 0; … … 71 71 } 72 72 73 .color-square .srgb-edge {73 .color-square > .svg-root > .srgb-edge { 74 74 fill: none; 75 75 stroke: white; … … 78 78 } 79 79 80 .color-square .srgb-label {80 .color-square > .srgb-label { 81 81 position: absolute; 82 padding-right: 3px;82 padding-right: 5px; 83 83 color: hsla(0, 0%, 100%, var(--stroke-opacity)); 84 84 font-size: 10px; 85 85 } 86 86 87 .color-square .srgb-label:hover {87 .color-square > .srgb-label:hover { 88 88 color: white; 89 89 } 90 90 91 .color-square .srgb-label:hover + .svg-root > .srgb-edge {91 .color-square > .srgb-label:hover + .svg-root > .srgb-edge { 92 92 stroke-width: 1px; 93 93 } 94 94 95 95 @media (-webkit-device-pixel-ratio: 1) { 96 .color-square .srgb-edge {96 .color-square > .srgb-edge { 97 97 stroke-width: 1px; 98 98 stroke-opacity: var(--stroke-opacity) / 2; -
trunk/Source/WebInspectorUI/UserInterface/Views/ColorSquare.js
r252747 r253018 107 107 if (tintedColor.format === WI.Color.Format.ColorFunction) { 108 108 // 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); 110 110 let x = hsv[1] / 100 * this._dimension; 111 111 let y = (1 - (hsv[2] / 100)) * this._dimension; … … 230 230 this._srgbLabelElement.className = "srgb-label"; 231 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");232 this._srgbLabelElement.title = WI.UIString("Edge of sRGB color space", "Label for a guide within the color picker"); 233 233 234 234 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")); 236 236 this._svgElement.classList.add("svg-root"); 237 this._element.append(this._svgElement);238 237 239 238 this._polylineElement = this._svgElement.appendChild(document.createElementNS(svgNamespace, "polyline")); … … 245 244 let x = 0; 246 245 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); 248 247 249 248 // Optimization: instead of starting from x = 0, we can benefit from the fact that the next point … … 253 252 let rgb = WI.Color.hsv2rgb(this._hue, saturation, value); 254 253 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)) { 256 255 // The point is outside of sRGB. 257 256 points.push({x, y}); … … 275 274 276 275 this._polylineElement.points.clear(); 277 for (let pointof points) {276 for (let {x, y} of points) { 278 277 let svgPoint = this._svgElement.createSVGPoint(); 279 svgPoint.x = point.x;280 svgPoint.y = point.y;278 svgPoint.x = x; 279 svgPoint.y = y; 281 280 this._polylineElement.points.appendItem(svgPoint); 282 281 } -
trunk/Source/WebInspectorUI/UserInterface/Views/InlineSwatch.js
r252168 r253018 47 47 switch (this._type) { 48 48 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. 50 50 break; 51 51 case WI.InlineSwatch.Type.Gradient: … … 78 78 this._value = value || this._fallbackValue(); 79 79 this._valueEditor = null; 80 this._readOnly = readOnly; 80 81 81 82 this._updateSwatch(); … … 147 148 this._swatchInnerElement.style.setProperty("background-image", `url(${value.src})`); 148 149 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 149 157 if (!dontFireEvents) 150 158 this.dispatchEventToListeners(WI.InlineSwatch.Event.ValueChanged, {value}); 151 159 } 152 160 161 _allowShiftClickColor() 162 { 163 return !this._readOnly && !this.value.isOutsideSRGB(); 164 } 165 153 166 _swatchElementClicked(event) 154 167 { … … 159 172 if (event.shiftKey && value) { 160 173 if (this._type === WI.InlineSwatch.Type.Color) { 174 if (!this._allowShiftClickColor()) { 175 InspectorFrontendHost.beep(); 176 return; 177 } 178 161 179 let nextFormat = value.nextFormat(); 162 // FIXME: <https://webkit.org/b/203534> Provide UI to convert between sRGB and p3 color spaces163 180 console.assert(nextFormat); 164 181 if (nextFormat) { … … 314 331 315 332 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; 320 388 this._updateSwatch(); 321 389 }); 322 390 } 323 391 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; 352 396 this._updateSwatch(); 353 397 });
Note: See TracChangeset
for help on using the changeset viewer.