• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

markusn / color-diff / 5137103075

pending completion
5137103075

push

github

web-flow
Merge pull request #22 from markusn/esm-module

convert to esm module, cjs support using rollup

78 of 79 branches covered (98.73%)

41 of 41 new or added lines in 3 files covered. (100.0%)

527 of 528 relevant lines covered (99.81%)

321.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.16
/lib/diff.js
1
/**
2✔
2
 * @author Markus Ekholm
2✔
3
 * @copyright 2012-2023 (c) Markus Ekholm <markus at botten dot org >
2✔
4
 * @license Copyright (c) 2012-2023, Markus Ekholm
2✔
5
 * All rights reserved.
2✔
6
 * Redistribution and use in source and binary forms, with or without
2✔
7
 * modification, are permitted provided that the following conditions are met:
2✔
8
 *    * Redistributions of source code must retain the above copyright
2✔
9
 *      notice, this list of conditions and the following disclaimer.
2✔
10
 *    * Redistributions in binary form must reproduce the above copyright
2✔
11
 *      notice, this list of conditions and the following disclaimer in the
2✔
12
 *      documentation and/or other materials provided with the distribution.
2✔
13
 *    * Neither the name of the author nor the
2✔
14
 *      names of its contributors may be used to endorse or promote products
2✔
15
 *      derived from this software without specific prior written permission.
2✔
16
 *
2✔
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
2✔
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2✔
19
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2✔
20
 * DISCLAIMED. IN NO EVENT SHALL MARKUS EKHOLM BE LIABLE FOR ANY
2✔
21
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2✔
22
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2✔
23
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2✔
24
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2✔
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2✔
26
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2✔
27
 */
2✔
28

2✔
29
/**
2✔
30
* IMPORTS
2✔
31
*/
2✔
32
import { rgbaToLab } from "./convert.js";
2✔
33

2✔
34
/**
2✔
35
* EXPORTS
2✔
36
*/
2✔
37
export { ciede2000 };
2✔
38

2✔
39
/**
2✔
40
 * TYPES
2✔
41
 */
2✔
42

2✔
43
/**
2✔
44
 * @typedef {import("../").LabColor} LabColor
2✔
45
 * @typedef {import("../").RGBAColor} RGBAColor
2✔
46
 * @typedef {import("../").Color} Color
2✔
47
 */
2✔
48

2✔
49
/**
2✔
50
 * API FUNCTIONS
2✔
51
 */
2✔
52

2✔
53
/**
2✔
54
* Returns diff between c1 and c2 using the CIEDE2000 algorithm
2✔
55
* @param {Color} c1
2✔
56
* @param {Color} c2
2✔
57
* @param {RGBAColor} [bc] background color
2✔
58
* @return {number} Difference between c1 and c2
2✔
59
*/
2✔
60
function ciede2000(c1, c2, bc) {
870✔
61
  if ("R" in c1 || "r" in c1) {
870✔
62
    c1 = rgbaToLab(c1, bc);
312✔
63
  }
312✔
64

870✔
65
  if ("R" in c2 || "r" in c2) {
870✔
66
    c2 = rgbaToLab(c2, bc);
312✔
67
  }
312✔
68
  /**
870✔
69
   * Implemented as in "The CIEDE2000 Color-Difference Formula:
870✔
70
   * Implementation Notes, Supplementary Test Data, and Mathematical Observations"
870✔
71
   * by Gaurav Sharma, Wencheng Wu and Edul N. Dalal.
870✔
72
   */
870✔
73

870✔
74
  // Get L,a,b values for color 1
870✔
75
  const L1 = c1.L;
870✔
76
  const a1 = c1.a;
870✔
77
  const b1 = c1.b;
870✔
78

870✔
79
  // Get L,a,b values for color 2
870✔
80
  const L2 = c2.L;
870✔
81
  const a2 = c2.a;
870✔
82
  const b2 = c2.b;
870✔
83

870✔
84
  // Weight factors
870✔
85
  const kL = 1;
870✔
86
  const kC = 1;
870✔
87
  const kH = 1;
870✔
88

870✔
89
  /**
870✔
90
   * Step 1: Calculate C1p, C2p, h1p, h2p
870✔
91
   */
870✔
92
  const C1 = Math.sqrt(Math.pow(a1, 2) + Math.pow(b1, 2)); // (2)
870✔
93
  const C2 = Math.sqrt(Math.pow(a2, 2) + Math.pow(b2, 2)); // (2)
870✔
94

870✔
95
  const aC1C2 = (C1 + C2) / 2.0; // (3)
870✔
96

870✔
97
  const G = 0.5 * (1 - Math.sqrt(Math.pow(aC1C2, 7.0) /
870✔
98
                          (Math.pow(aC1C2, 7.0) + Math.pow(25.0, 7.0)))); // (4)
870✔
99

870✔
100
  const a1p = (1.0 + G) * a1; // (5)
870✔
101
  const a2p = (1.0 + G) * a2; // (5)
870✔
102

870✔
103
  const C1p = Math.sqrt(Math.pow(a1p, 2) + Math.pow(b1, 2)); // (6)
870✔
104
  const C2p = Math.sqrt(Math.pow(a2p, 2) + Math.pow(b2, 2)); // (6)
870✔
105

870✔
106
  const h1p = hpF(b1, a1p); // (7)
870✔
107
  const h2p = hpF(b2, a2p); // (7)
870✔
108

870✔
109
  /**
870✔
110
   * Step 2: Calculate dLp, dCp, dHp
870✔
111
   */
870✔
112
  const dLp = L2 - L1; // (8)
870✔
113
  const dCp = C2p - C1p; // (9)
870✔
114

870✔
115
  const dhp = dhpF(C1, C2, h1p, h2p); // (10)
870✔
116
  const dHp = 2 * Math.sqrt(C1p * C2p) * Math.sin(radians(dhp) / 2.0); // (11)
870✔
117

870✔
118
  /**
870✔
119
   * Step 3: Calculate CIEDE2000 Color-Difference
870✔
120
   */
870✔
121
  const aL = (L1 + L2) / 2.0; // (12)
870✔
122
  const aCp = (C1p + C2p) / 2.0; // (13)
870✔
123

870✔
124
  const aHp = aHpF(C1, C2, h1p, h2p); // (14)
870✔
125
  const T = 1 - 0.17 * Math.cos(radians(aHp - 30)) + 0.24 * Math.cos(radians(2 * aHp)) +
870✔
126
    0.32 * Math.cos(radians(3 * aHp + 6)) - 0.20 * Math.cos(radians(4 * aHp - 63)); // (15)
870✔
127
  const dRo = 30 * Math.exp(-(Math.pow((aHp - 275) / 25, 2))); // (16)
870✔
128
  const RC = Math.sqrt((Math.pow(aCp, 7.0)) / (Math.pow(aCp, 7.0) + Math.pow(25.0, 7.0)));// (17)
870✔
129
  const SL = 1 + ((0.015 * Math.pow(aL - 50, 2)) /
870✔
130
                Math.sqrt(20 + Math.pow(aL - 50, 2.0)));// (18)
870✔
131
  const SC = 1 + 0.045 * aCp;// (19)
870✔
132
  const SH = 1 + 0.015 * aCp * T;// (20)
870✔
133
  const RT = -2 * RC * Math.sin(radians(2 * dRo));// (21)
870✔
134
  const dE = Math.sqrt(Math.pow(dLp / (SL * kL), 2) + Math.pow(dCp / (SC * kC), 2) +
870✔
135
                Math.pow(dHp / (SH * kH), 2) + RT * (dCp / (SC * kC)) *
870✔
136
                (dHp / (SH * kH))); // (22)
870✔
137
  return dE;
870✔
138
}
870✔
139

2✔
140
/**
2✔
141
 * INTERNAL FUNCTIONS
2✔
142
 */
2✔
143

2✔
144
/**
2✔
145
 *
2✔
146
 * @param {number} n
2✔
147
 * @returns {number}
2✔
148
 */
2✔
149
function degrees(n) {
1,456✔
150
  return n * (180 / Math.PI);
1,456✔
151
}
1,456✔
152

2✔
153
/**
2✔
154
 *
2✔
155
 * @param {number} n
2✔
156
 * @returns number
2✔
157
 */
2✔
158
function radians(n) {
5,208✔
159
  return n * (Math.PI / 180);
5,208✔
160
}
5,208✔
161

2✔
162
/**
2✔
163
 *
2✔
164
 * @param {number} x
2✔
165
 * @param {number} y
2✔
166
 * @returns {number}
2✔
167
 */
2✔
168
function hpF(x, y) { // (7)
1,740✔
169
  if (x === 0 && y === 0) return 0;
1,740✔
170
  else {
1,456✔
171
    const tmphp = degrees(Math.atan2(x, y));
1,456✔
172
    if (tmphp >= 0) return tmphp;
1,456✔
173
    else return tmphp + 360;
872✔
174
  }
1,456✔
175
}
1,740✔
176

2✔
177
/**
2✔
178
 *
2✔
179
 * @param {number} C1
2✔
180
 * @param {number} C2
2✔
181
 * @param {number} h1p
2✔
182
 * @param {number} h2p
2✔
183
 * @returns {number}
2✔
184
 */
2✔
185
function dhpF(C1, C2, h1p, h2p) { // (10)
870✔
186
  if (C1 * C2 === 0) return 0;
870✔
187
  else if (Math.abs(h2p - h1p) <= 180) return h2p - h1p;
614✔
188
  else if ((h2p - h1p) > 180) return (h2p - h1p) - 360;
254✔
189
  else if ((h2p - h1p) < -180) return (h2p - h1p) + 360;
122✔
190
  else throw (new Error());
2✔
191
}
870✔
192

2✔
193
/**
2✔
194
 *
2✔
195
 * @param {number} C1
2✔
196
 * @param {number} C2
2✔
197
 * @param {number} h1p
2✔
198
 * @param {number} h2p
2✔
199
 * @returns {number}
2✔
200
 */
2✔
201
function aHpF(C1, C2, h1p, h2p) { // (14)
868✔
202
  if (C1 * C2 === 0) return h1p + h2p;
868✔
203
  else if (Math.abs(h1p - h2p) <= 180) return (h1p + h2p) / 2.0;
612✔
204
  else if ((Math.abs(h1p - h2p) > 180) && ((h1p + h2p) < 360)) return (h1p + h2p + 360) / 2.0;
252✔
205
  else if ((Math.abs(h1p - h2p) > 180) && ((h1p + h2p) >= 360)) return (h1p + h2p - 360) / 2.0;
240✔
206
  else throw (new Error());
×
207
}
868✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc