@@ -2595,354 +2595,11 @@ Default Style Rules</h2>
25952595
25962596<em> This section is not normative.</em>
25972597
2598- <pre class="lang-javascript">
2599- <!-- imported 30 Oct 2019-->
2600- // Sample code for color conversions
2601- // Conversion can also be done using ICC profiles and a Color Management System
2602- // For clarity, a library is used for matrix manipulations
2603-
2604- // sRGB-related functions
2605-
2606- function lin_sRGB(RGB) {
2607- // convert an array of sRGB values in the range 0.0 - 1.0
2608- // to linear light (un-companded) form.
2609- // https://en.wikipedia.org/wiki/SRGB
2610- return RGB.map(function (val) {
2611- if (val < 0.04045) {
2612- return val / 12.92;
2613- }
2614-
2615- return Math.pow((val + 0.055) / 1.055, 2.4);
2616- });
2617- }
2618-
2619- function gam_sRGB(RGB) {
2620- // convert an array of linear-light sRGB values in the range 0.0-1.0
2621- // to gamma corrected form
2622- // https://en.wikipedia.org/wiki/SRGB
2623- return RGB.map(function (val) {
2624- if (val > 0.0031308) {
2625- return 1.055 * Math.pow(val, 1/2.4) - 0.055;
2626- }
2627-
2628- return 12.92 * val;
2629- });
2630- }
2631-
2632- function lin_sRGB_to_XYZ(rgb) {
2633- // convert an array of linear-light sRGB values to CIE XYZ
2634- // using sRGB's own white, D65 (no chromatic adaptation)
2635- // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
2636- var M = math.matrix([
2637- [0.4124564, 0.3575761, 0.1804375] ,
2638- [0.2126729, 0.7151522, 0.0721750] ,
2639- [0.0193339, 0.1191920, 0.9503041]
2640- ]);
2641-
2642- return math.multiply(M, rgb).valueOf();
2643- }
2644-
2645- function XYZ_to_lin_sRGB(XYZ) {
2646- // convert XYZ to linear-light sRGB
2647- var M = math.matrix([
2648- [ 3.2404542, -1.5371385, -0.4985314] ,
2649- [-0.9692660, 1.8760108, 0.0415560] ,
2650- [ 0.0556434, -0.2040259, 1.0572252]
2651- ]);
2652-
2653- return math.multiply(M, XYZ).valueOf();
2654- }
2655-
2656- // image-3-related functions
2657-
2658-
2659- function lin_P3(RGB) {
2660- // convert an array of image-p3 RGB values in the range 0.0 - 1.0
2661- // to linear light (un-companded) form.
2662-
2663- return lin_sRGB(RGB); // same as sRGB
2664- }
2665-
2666- function gam_P3(RGB) {
2667- // convert an array of linear-light image-p3 RGB in the range 0.0-1.0
2668- // to gamma corrected form
2669-
2670- return gam_sRGB(RGB); // same as sRGB
2671- }
2672-
2673- function lin_P3_to_XYZ(rgb) {
2674- // convert an array of linear-light image-p3 values to CIE XYZ
2675- // using D65 (no chromatic adaptation)
2676- // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
2677- var M = math.matrix([
2678- [0.4865709486482162, 0.26566769316909306, 0.1982172852343625] ,
2679- [0.2289745640697488, 0.6917385218365064, 0.079286914093745] ,
2680- [0.0000000000000000, 0.04511338185890264, 1.043944368900976]
2681- ]);
2682- // 0 was computed as -3.972075516933488e-17
2683-
2684- return math.multiply(M, rgb).valueOf();
2685- }
2686-
2687- function XYZ_to_lin_P3(XYZ) {
2688- // convert XYZ to linear-light P3
2689- var M = math.matrix([
2690- [ 2.493496911941425, -0.9313836179191239, -0.40271078445071684] ,
2691- [-0.8294889695615747, 1.7626640603183463, 0.023624685841943577] ,
2692- [ 0.03584583024378447, -0.07617238926804182, 0.9568845240076872]
2693- ]);
2694-
2695- return math.multiply(M, XYZ).valueOf();
2696- }
2697-
2698- // prophoto-rgb functions
2699-
2700- function lin_ProPhoto(RGB) {
2701- // convert an array of prophoto-rgb values in the range 0.0 - 1.0
2702- // to linear light (un-companded) form.
2703- // Transfer curve is gamma 1.8 with a small linear portion
2704- return RGB.map(function (val) {
2705- if (val < 0.031248) {
2706- return val / 16;
2707- }
2708-
2709- return Math.pow(val, 1.8);
2710- });
2711- }
2712-
2713- function gam_ProPhoto(RGB) {
2714- // convert an array of linear-light prophoto-rgb in the range 0.0-1.0
2715- // to gamma corrected form
2716- // Transfer curve is gamma 1.8 with a small linear portion
2717- return RGB.map(function (val) {
2718- if (val > 0.001953) {
2719- return Math.pow(val, 1/1.8);
2720- }
2721-
2722- return 16 * val;
2723- });
2724- }
2725-
2726- function lin_ProPhoto_to_XYZ(rgb) {
2727- // convert an array of linear-light prophoto-rgb values to CIE XYZ
2728- // using D50 (so no chromatic adaptation needed afterwards)
2729- // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
2730- var M = Math.matrix([
2731- [ 0.7977604896723027, 0.13518583717574031, 0.0313493495815248 ] ,
2732- [ 0.2880711282292934, 0.7118432178101014, 0.00008565396060525902 ] ,
2733- [ 0.0, 0.0, 0.8251046025104601 ]
2734- ]);
2735-
2736- return Math.multiply(M, rgb).valueOf();
2737- }
2738-
2739- function XYZ_to_lin_ProPhoto(XYZ) {
2740- // convert XYZ to linear-light prophoto-rgb
2741- var M = Math.matrix([
2742- [ 1.3457989731028281, -0.25558010007997534, -0.05110628506753401 ] ,
2743- [ -0.5446224939028347, 1.5082327413132781, 0.02053603239147973 ] ,
2744- [ 0.0, 0.0, 1.2119675456389454 ]
2745- ]);
2746-
2747- return Math.multiply(M, XYZ).valueOf();
2748- }
2749-
2750- // a98-rgb functions
2751-
2752- function lin_a98rgb(RGB) {
2753- // convert an array of a98-rgb values in the range 0.0 - 1.0
2754- // to linear light (un-companded) form.
2755- return RGB.map(function (val) {
2756- return Math.pow(val, 563/256);
2757- });
2758- }
2759-
2760- function gam_a98rgb(RGB) {
2761- // convert an array of linear-light a98-rgb in the range 0.0-1.0
2762- // to gamma corrected form
2763- return RGB.map(function (val) {
2764- return Math.pow(val, 256/563);
2765- });
2766- }
2767-
2768- function lin_a98rgb_to_XYZ(rgb) {
2769- // convert an array of linear-light a98-rgb values to CIE XYZ
2770- // using D50 (so no chromatic adaptation needed afterwards)
2771- // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
2772- // which has greater numerical precsion than section 4.3.5.3 of
2773- // https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
2774- var M = Math.matrix([
2775- [ 0.5766690429101305, 0.1855582379065463, 0.1882286462349947 ] ,
2776- [ 0.29734497525053605, 0.6273635662554661, 0.07529145849399788 ] ,
2777- [ 0.02703136138641234, 0.07068885253582723, 0.9913375368376388 ]
2778- ]);
2779-
2780- return Math.multiply(M, rgb).valueOf();
2781- }
2782-
2783- function XYZ_to_lin_a98rgb(XYZ) {
2784- // convert XYZ to linear-light a98-rgb
2785- var M = Math.matrix([
2786- [ 2.0415879038107465, -0.5650069742788596, -0.34473135077832956 ] ,
2787- [ -0.9692436362808795, 1.8759675015077202, 0.04155505740717557 ] ,
2788- [ 0.013444280632031142, -0.11836239223101838, 1.0151749943912054 ]
2789- ]);
2790-
2791- return Math.multiply(M, XYZ).valueOf();
2792- }
2793-
2794- //Rec. 2020-related functions
2795-
2796- function lin_2020(RGB) {
2797- // convert an array of rec-2020 RGB values in the range 0.0 - 1.0
2798- // to linear light (un-companded) form.
2799- const α = 1.09929682680944 ;
2800- const β = 0.018053968510807;
2801-
2802- return RGB.map(function (val) {
2803- if (val < β * 4.5 ) {
2804- return val / 4.5;
2805- }
2806-
2807- return Math.pow((val + α -1 ) / α, 2.4);
2808- });
2809- }
2810- //check with standard this really is 2.4 and 1/2.4, not 0.45 was wikipedia claims
2811-
2812- function gam_2020(RGB) {
2813- // convert an array of linear-light rec-2020 RGB in the range 0.0-1.0
2814- // to gamma corrected form
2815- const α = 1.09929682680944 ;
2816- const β = 0.018053968510807;
2817-
2818- return RGB.map(function (val) {
2819- if (val > β ) {
2820- return α * Math.pow(val, 1/2.4) - (α - 1);
2821- }
2822-
2823- return 4.5 * val;
2824- });
2825- }
2826-
2827- function lin_2020_to_XYZ(rgb) {
2828- // convert an array of linear-light rec-2020 values to CIE XYZ
2829- // using D65 (no chromatic adaptation)
2830- // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
2831- var M = math.matrix([
2832- [0.6369580483012914, 0.14461690358620832, 0.1688809751641721] ,
2833- [0.2627002120112671, 0.6779980715188708, 0.05930171646986196] ,
2834- [0.000000000000000, 0.028072693049087428, 1.060985057710791]
2835- ]);
2836- // 0 is actually calculated as 4.994106574466076e-17
2837-
2838- return math.multiply(M, rgb).valueOf();
2839- }
2840-
2841- function XYZ_to_lin_2020(XYZ) {
2842- // convert XYZ to linear-light rec-2020
2843- var M = math.matrix([
2844- [1.7166511879712674, -0.35567078377639233, -0.25336628137365974] ,
2845- [-0.6666843518324892, 1.6164812366349395, 0.01576854581391113] ,
2846- [0.017639857445310783, -0.042770613257808524, 0.9421031212354738]
2847- ]);
2848-
2849- return math.multiply(M, XYZ).valueOf();
2850- }
2851-
2852- // Chromatic adaptation
2853-
2854- function D65_to_D50(XYZ) {
2855- // Bradford chromatic adaptation from D65 to D50
2856- // The matrix below is the result of three operations:
2857- // - convert from XYZ to retinal cone domain
2858- // - scale components from one reference white to another
2859- // - convert back to XYZ
2860- // http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
2861- var M = math.matrix([
2862- [ 1.0478112, 0.0228866, -0.0501270] ,
2863- [ 0.0295424, 0.9904844, -0.0170491] ,
2864- [-0.0092345, 0.0150436, 0.7521316]
2865- ]);
2866-
2867- return math.multiply(M, XYZ).valueOf();
2868- }
2869-
2870- function D50_to_D65(XYZ) {
2871- // Bradford chromatic adaptation from D50 to D65
2872- var M = math.matrix([
2873- [ 0.9555766, -0.0230393, 0.0631636] ,
2874- [-0.0282895, 1.0099416, 0.0210077] ,
2875- [ 0.0122982, -0.0204830, 1.3299098]
2876- ]);
2877-
2878- return math.multiply(M, XYZ).valueOf();
2879- }
2880-
2881- // Lab and LCH
2882-
2883- function XYZ_to_Lab(XYZ) {
2884- // Assuming XYZ is relative to D50, convert to CIE Lab
2885- // from CIE standard, which now defines these as a rational fraction
2886- var ε = 216/24389; // 6^3/29^3
2887- var κ = 24389/27; // 29^3/3^3
2888- var white = [0.96422, 1.00000, 0.82521] ; // D50 reference white
2889-
2890- // compute xyz, which is XYZ scaled relative to reference white
2891- var xyz = XYZ.map((value, i) => value / white[i] );
2892-
2893- // now compute f
2894- var f = xyz.map(value => value > ε ? Math.cbrt(value) : (κ * value + 16)/116);
2895-
2896- return [
2897- (116 * f[1] ) - 16, // L
2898- 500 * (f[0] - f[1] ), // a
2899- 200 * (f[1] - f[2] ) // b
2900- ];
2901- }
2902-
2903- function Lab_to_XYZ(Lab) {
2904- // Convert Lab to D50-adapted XYZ
2905- // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
2906- var κ = 24389/27; // 29^3/3^3
2907- var ε = 216/24389; // 6^3/29^3
2908- var white = [0.96422, 1.00000, 0.82521] ; // D50 reference white
2909- var f = [];
2910-
2911- // compute f, starting with the luminance-related term
2912- f[1] = (Lab[0] + 16)/116;
2913- f[0] = Lab[1] /500 + f[1] ;
2914- f[2] = f[1] - Lab[2] /200;
2915-
2916- // compute xyz
2917- var xyz = [
2918- Math.pow(f[0] ,3) > ε ? Math.pow(f[0] ,3) : (116*f[0] -16)/κ,
2919- Lab[0] > κ * ε ? Math.pow((Lab[0] +16)/116,3) : Lab[0] /κ,
2920- Math.pow(f[2] ,3) > ε ? Math.pow(f[2] ,3) : (116*f[2] -16)/κ
2921- ];
2922-
2923- // Compute XYZ by scaling xyz by reference white
2924- return xyz.map((value, i) => value * white[i] );
2925- }
2926-
2927- function Lab_to_LCH(Lab) {
2928- // Convert to polar form
2929- var hue = Math.atan2(Lab[2] , Lab[1] ) * 180 / Math.PI;
2930- return [
2931- Lab[0] , // L is still L
2932- Math.sqrt(Math.pow(Lab[1] , 2) + Math.pow(Lab[2] , 2)), // Chroma
2933- hue >= 0 ? hue : hue + 360 // Hue, in degrees [0 to 360)
2934- ];
2935- }
2936-
2937- function LCH_to_Lab(LCH) {
2938- // Convert from polar form
2939- return [
2940- LCH[0] , // L is still L
2941- LCH[1] * Math.cos(LCH[2] * Math.PI / 180), // a
2942- LCH[1] * Math.sin(LCH[2] * Math.PI / 180) // b
2943- ];
2944- }
2598+ For clarity, a library is used for matrix multiplication.
29452599
2600+ <pre class="include-code lang-javascript">
2601+ path: conversions.js
2602+ highlight: js
29462603</pre>
29472604
29482605<h2 id="deprecated-system-colors" class="no-num">
0 commit comments