PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / _inc / lib / class.color.php
jetpack / _inc / lib Last commit date
admin-pages 1 week ago core-api 2 days ago debugger 1 month ago markdown 7 months ago class-jetpack-ai-helper.php 4 months ago class-jetpack-application-password-extras.php 3 months ago class-jetpack-blog-stats-helper.php 7 months ago class-jetpack-currencies.php 1 year ago class-jetpack-instagram-gallery-helper.php 1 month ago class-jetpack-mapbox-helper.php 3 years ago class-jetpack-newsletter-category-helper.php 7 months ago class-jetpack-podcast-feed-locator.php 1 month ago class-jetpack-podcast-helper.php 5 months ago class-jetpack-recommendations.php 1 month ago class-jetpack-spinner.php 2 months ago class-jetpack-top-posts-helper.php 1 week ago class.color.php 1 year ago class.core-rest-api-endpoints.php 1 week ago class.jetpack-automatic-install-skin.php 2 years ago class.jetpack-iframe-embed.php 3 years ago class.jetpack-password-checker.php 7 months ago class.jetpack-search-performance-logger.php 4 years ago class.media-extractor.php 1 month ago class.media-summary.php 1 week ago class.media.php 7 months ago components.php 7 months ago debugger.php 1 month ago icalendar-reader.php 1 month ago markdown.php 7 months ago plans.php 4 years ago plugins.php 4 years ago tonesque.php 7 months ago widgets.php 3 months ago
class.color.php
905 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Color utility and conversion
4 *
5 * Represents a color value, and converts between RGB/HSV/XYZ/Lab/HSL
6 *
7 * Example:
8 * $color = new Jetpack_Color(0xFFFFFF);
9 *
10 * @author Harold Asbridge <hasbridge@gmail.com>
11 * @author Matt Wiebe <wiebe@automattic.com>
12 * @license https://www.opensource.org/licenses/MIT
13 *
14 * @package automattic/jetpack
15 */
16
17 // phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
18
19 if ( ! class_exists( 'Jetpack_Color' ) ) {
20 /**
21 * Color utilities
22 */
23 class Jetpack_Color {
24 /**
25 * Color code (later array or string, depending on type)
26 *
27 * @var int|array|string
28 */
29 protected $color = 0;
30
31 /**
32 * Initialize object
33 *
34 * @param string|array $color A color of the type $type.
35 * @param string $type The type of color we will construct from.
36 * One of hex (default), rgb, hsl, int.
37 */
38 public function __construct( $color = null, $type = 'hex' ) {
39 _deprecated_function( 'Jetpack_Color::__construct', 'jetpack-13.8' );
40
41 if ( $color ) {
42 switch ( $type ) {
43 case 'hex':
44 $this->fromHex( $color );
45 break;
46 case 'rgb':
47 if ( is_array( $color ) && count( $color ) === 3 ) {
48 list( $r, $g, $b ) = array_values( $color );
49 $this->fromRgbInt( $r, $g, $b );
50 }
51 break;
52 case 'hsl':
53 if ( is_array( $color ) && count( $color ) === 3 ) {
54 list( $h, $s, $l ) = array_values( $color );
55 $this->fromHsl( $h, $s, $l );
56 }
57 break;
58 case 'int':
59 $this->fromInt( $color );
60 break;
61 default:
62 // there is no default.
63 break;
64 }
65 }
66 }
67
68 /**
69 * Init color from hex value
70 *
71 * @param string $hex_value Color hex value.
72 *
73 * @return $this
74 * @throws RangeException Invalid color code range error.
75 */
76 public function fromHex( $hex_value ) {
77 $hex_value = str_replace( '#', '', $hex_value );
78 // handle short hex codes like #fff.
79 if ( 3 === strlen( $hex_value ) ) {
80 $hex_value = $hex_value[0] . $hex_value[0] . $hex_value[1] . $hex_value[1] . $hex_value[2] . $hex_value[2];
81 }
82 return $this->fromInt( hexdec( $hex_value ) );
83 }
84
85 /**
86 * Init color from integer RGB values
87 *
88 * @param int $red Red color code.
89 * @param int $green Green color code.
90 * @param int $blue Blue color code.
91 *
92 * @return $this
93 * @throws RangeException Invalid color code range error.
94 */
95 public function fromRgbInt( $red, $green, $blue ) {
96 if ( $red < 0 || $red > 255 ) {
97 throw new RangeException( 'Red value ' . $red . ' out of valid color code range' );
98 }
99
100 if ( $green < 0 || $green > 255 ) {
101 throw new RangeException( 'Green value ' . $green . ' out of valid color code range' );
102 }
103
104 if ( $blue < 0 || $blue > 255 ) {
105 throw new RangeException( 'Blue value ' . $blue . ' out of valid color code range' );
106 }
107
108 $this->color = ( intval( $red ) << 16 ) + ( intval( $green ) << 8 ) + intval( $blue );
109
110 return $this;
111 }
112
113 /**
114 * Init color from hex RGB values
115 *
116 * @param string $red Red color code.
117 * @param string $green Green color code.
118 * @param string $blue Blue color code.
119 *
120 * @return $this
121 */
122 public function fromRgbHex( $red, $green, $blue ) {
123 return $this->fromRgbInt( hexdec( $red ), hexdec( $green ), hexdec( $blue ) );
124 }
125
126 /**
127 * Converts an HSL color value to RGB. Conversion formula
128 * adapted from https://en.wikipedia.org/wiki/HSL_color_space.
129 *
130 * @param int $h Hue. [0-360].
131 * @param int $s Saturation [0, 100].
132 * @param int $l Lightness [0, 100].
133 */
134 public function fromHsl( $h, $s, $l ) {
135 $h /= 360;
136 $s /= 100;
137 $l /= 100;
138
139 if ( 0 === $s ) {
140 // achromatic.
141 $r = $l;
142 $g = $l;
143 $b = $l;
144 } else {
145 $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s;
146 $p = 2 * $l - $q;
147 $r = $this->hue2rgb( $p, $q, $h + 1 / 3 );
148 $g = $this->hue2rgb( $p, $q, $h );
149 $b = $this->hue2rgb( $p, $q, $h - 1 / 3 );
150 }
151
152 return $this->fromRgbInt( $r * 255, $g * 255, $b * 255 );
153 }
154
155 /**
156 * Helper function for Jetpack_Color::fromHsl()
157 *
158 * @param float $p Minimum of R/G/B [0, 1].
159 * @param float $q Maximum of R/G/B [0, 1].
160 * @param float $t Adjusted hue [0, 1].
161 */
162 private function hue2rgb( $p, $q, $t ) {
163 if ( $t < 0 ) {
164 ++$t;
165 }
166 if ( $t > 1 ) {
167 --$t;
168 }
169 if ( $t < 1 / 6 ) {
170 return $p + ( $q - $p ) * 6 * $t;
171 }
172 if ( $t < 1 / 2 ) {
173 return $q;
174 }
175 if ( $t < 2 / 3 ) {
176 return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6;
177 }
178 return $p;
179 }
180
181 /**
182 * Init color from integer value
183 *
184 * @param int $int_value Color code.
185 *
186 * @return $this
187 * @throws RangeException Invalid color code range error.
188 */
189 public function fromInt( $int_value ) {
190 if ( $int_value < 0 || $int_value > 16777215 ) {
191 throw new RangeException( $int_value . ' out of valid color code range' );
192 }
193
194 $this->color = $int_value;
195
196 return $this;
197 }
198
199 /**
200 * Convert color to hex
201 *
202 * @return string
203 */
204 public function toHex() {
205 return sprintf( '%06x', $this->color );
206 }
207
208 /**
209 * Convert color to RGB array (integer values)
210 *
211 * @return array
212 */
213 public function toRgbInt() {
214 return array(
215 'red' => (int) ( 255 & ( $this->color >> 16 ) ),
216 'green' => (int) ( 255 & ( $this->color >> 8 ) ),
217 'blue' => (int) ( 255 & ( $this->color ) ),
218 );
219 }
220
221 /**
222 * Convert color to RGB array (hex values)
223 *
224 * @return array
225 */
226 public function toRgbHex() {
227 $r = array();
228 foreach ( $this->toRgbInt() as $item ) {
229 $r[] = dechex( $item );
230 }
231 return $r;
232 }
233
234 /**
235 * Get Hue/Saturation/Value for the current color
236 * (float values, slow but accurate)
237 *
238 * @return array
239 */
240 public function toHsvFloat() {
241 $rgb = $this->toRgbInt();
242
243 $rgb_min = min( $rgb );
244 $rgb_max = max( $rgb );
245
246 $hsv = array(
247 'hue' => 0,
248 'sat' => 0,
249 'val' => $rgb_max,
250 );
251
252 // If v is 0, color is black.
253 if ( 0 === $hsv['val'] ) {
254 return $hsv;
255 }
256
257 // Normalize RGB values to 1.
258 $rgb['red'] /= $hsv['val'];
259 $rgb['green'] /= $hsv['val'];
260 $rgb['blue'] /= $hsv['val'];
261 $rgb_min = min( $rgb );
262 $rgb_max = max( $rgb );
263
264 // Calculate saturation.
265 $hsv['sat'] = $rgb_max - $rgb_min;
266 if ( 0 === $hsv['sat'] ) {
267 $hsv['hue'] = 0;
268 return $hsv;
269 }
270
271 // Normalize saturation to 1.
272 $rgb['red'] = ( $rgb['red'] - $rgb_min ) / ( $rgb_max - $rgb_min );
273 $rgb['green'] = ( $rgb['green'] - $rgb_min ) / ( $rgb_max - $rgb_min );
274 $rgb['blue'] = ( $rgb['blue'] - $rgb_min ) / ( $rgb_max - $rgb_min );
275 $rgb_min = min( $rgb );
276 $rgb_max = max( $rgb );
277
278 // Calculate hue.
279 if ( $rgb_max === $rgb['red'] ) {
280 $hsv['hue'] = 0.0 + 60 * ( $rgb['green'] - $rgb['blue'] );
281 if ( $hsv['hue'] < 0 ) {
282 $hsv['hue'] += 360;
283 }
284 } elseif ( $rgb_max === $rgb['green'] ) {
285 $hsv['hue'] = 120 + ( 60 * ( $rgb['blue'] - $rgb['red'] ) );
286 } else {
287 $hsv['hue'] = 240 + ( 60 * ( $rgb['red'] - $rgb['green'] ) );
288 }
289
290 return $hsv;
291 }
292
293 /**
294 * Get HSV values for color
295 * (integer values from 0-255, fast but less accurate)
296 *
297 * @return array
298 */
299 public function toHsvInt() {
300 $rgb = $this->toRgbInt();
301
302 $rgb_min = min( $rgb );
303 $rgb_max = max( $rgb );
304
305 $hsv = array(
306 'hue' => 0,
307 'sat' => 0,
308 'val' => $rgb_max,
309 );
310
311 // If value is 0, color is black.
312 if ( 0 === $hsv['val'] ) {
313 return $hsv;
314 }
315
316 // Calculate saturation.
317 $hsv['sat'] = round( 255 * ( $rgb_max - $rgb_min ) / $hsv['val'] );
318 if ( 0 === $hsv['sat'] ) {
319 $hsv['hue'] = 0;
320 return $hsv;
321 }
322
323 // Calculate hue.
324 if ( $rgb_max === $rgb['red'] ) {
325 $hsv['hue'] = round( 0 + 43 * ( $rgb['green'] - $rgb['blue'] ) / ( $rgb_max - $rgb_min ) );
326 } elseif ( $rgb_max === $rgb['green'] ) {
327 $hsv['hue'] = round( 85 + 43 * ( $rgb['blue'] - $rgb['red'] ) / ( $rgb_max - $rgb_min ) );
328 } else {
329 $hsv['hue'] = round( 171 + 43 * ( $rgb['red'] - $rgb['green'] ) / ( $rgb_max - $rgb_min ) );
330 }
331 if ( $hsv['hue'] < 0 ) {
332 $hsv['hue'] += 255;
333 }
334
335 return $hsv;
336 }
337
338 /**
339 * Converts an RGB color value to HSL. Conversion formula
340 * adapted from https://en.wikipedia.org/wiki/HSL_color_space.
341 * Assumes r, g, and b are contained in the set [0, 255] and
342 * returns h in [0, 360], s in [0, 100], l in [0, 100]
343 *
344 * @return Array The HSL representation
345 */
346 public function toHsl() {
347 list( $r, $g, $b ) = array_values( $this->toRgbInt() );
348 $r /= 255;
349 $g /= 255;
350 $b /= 255;
351 $max = max( $r, $g, $b );
352 $min = min( $r, $g, $b );
353 $l = ( $max + $min ) / 2;
354
355 if ( $max === $min ) {
356 // achromatic.
357 $s = 0;
358 $h = 0;
359 } else {
360 $d = $max - $min;
361 $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min );
362 switch ( $max ) {
363 case $r:
364 $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 );
365 break;
366 case $g:
367 $h = ( $b - $r ) / $d + 2;
368 break;
369 case $b:
370 $h = ( $r - $g ) / $d + 4;
371 break;
372 }
373 $h /= 6;
374 }
375 $h = (int) round( $h * 360 );
376 $s = (int) round( $s * 100 );
377 $l = (int) round( $l * 100 );
378 return compact( 'h', 's', 'l' );
379 }
380
381 /**
382 * From a color code to a string to be used in CSS declaration.
383 *
384 * @param string $type Color code type.
385 * @param int $alpha Transparency.
386 *
387 * @return string
388 */
389 public function toCSS( $type = 'hex', $alpha = 1 ) {
390 switch ( $type ) {
391 case 'hex':
392 return $this->toString();
393 case 'rgb':
394 case 'rgba':
395 list( $r, $g, $b ) = array_values( $this->toRgbInt() );
396 if ( is_numeric( $alpha ) && $alpha < 1 ) {
397 return "rgba( {$r}, {$g}, {$b}, $alpha )";
398 } else {
399 return "rgb( {$r}, {$g}, {$b} )";
400 }
401 case 'hsl':
402 case 'hsla':
403 list( $h, $s, $l ) = array_values( $this->toHsl() );
404 if ( is_numeric( $alpha ) && $alpha < 1 ) {
405 return "hsla( {$h}, {$s}, {$l}, $alpha )";
406 } else {
407 return "hsl( {$h}, {$s}, {$l} )";
408 }
409 default:
410 return $this->toString();
411 }
412 }
413
414 /**
415 * Get current color in XYZ format
416 *
417 * @return array
418 */
419 public function toXyz() {
420 $rgb = $this->toRgbInt();
421
422 // Normalize RGB values to 1.
423 $rgb_new = array();
424 foreach ( $rgb as $item ) {
425 $rgb_new[] = $item / 255;
426 }
427 $rgb = $rgb_new;
428
429 $rgb_new = array();
430 foreach ( $rgb as $item ) {
431 if ( $item > 0.04045 ) {
432 $item = pow( ( ( $item + 0.055 ) / 1.055 ), 2.4 );
433 } else {
434 $item = $item / 12.92;
435 }
436 $rgb_new[] = $item * 100;
437 }
438 $rgb = $rgb_new;
439
440 // Observer. = 2°, Illuminant = D65.
441 $xyz = array(
442 'x' => ( $rgb['red'] * 0.4124 ) + ( $rgb['green'] * 0.3576 ) + ( $rgb['blue'] * 0.1805 ),
443 'y' => ( $rgb['red'] * 0.2126 ) + ( $rgb['green'] * 0.7152 ) + ( $rgb['blue'] * 0.0722 ),
444 'z' => ( $rgb['red'] * 0.0193 ) + ( $rgb['green'] * 0.1192 ) + ( $rgb['blue'] * 0.9505 ),
445 );
446
447 return $xyz;
448 }
449
450 /**
451 * Get color CIE-Lab values
452 *
453 * @return array
454 */
455 public function toLabCie() {
456 $xyz = $this->toXyz();
457
458 // Ovserver = 2*, Iluminant=D65.
459 $xyz['x'] /= 95.047;
460 $xyz['y'] /= 100;
461 $xyz['z'] /= 108.883;
462
463 $xyz_new = array();
464 foreach ( $xyz as $item ) {
465 if ( $item > 0.008856 ) {
466 $xyz_new[] = pow( $item, 1 / 3 );
467 } else {
468 $xyz_new[] = ( 7.787 * $item ) + ( 16 / 116 );
469 }
470 }
471 $xyz = $xyz_new;
472
473 $lab = array(
474 'l' => ( 116 * $xyz['y'] ) - 16,
475 'a' => 500 * ( $xyz['x'] - $xyz['y'] ),
476 'b' => 200 * ( $xyz['y'] - $xyz['z'] ),
477 );
478
479 return $lab;
480 }
481
482 /**
483 * Convert color to integer
484 *
485 * @return int
486 */
487 public function toInt() {
488 return $this->color;
489 }
490
491 /**
492 * Alias of toString()
493 *
494 * @return string
495 */
496 public function __toString() {
497 return $this->toString();
498 }
499
500 /**
501 * Get color as string
502 *
503 * @return string
504 */
505 public function toString() {
506 $str = $this->toHex();
507 return strtoupper( "#{$str}" );
508 }
509
510 /**
511 * Get the distance between this color and the given color
512 *
513 * @param Jetpack_Color $color Color code.
514 *
515 * @return int
516 */
517 public function getDistanceRgbFrom( Jetpack_Color $color ) {
518 $rgb1 = $this->toRgbInt();
519 $rgb2 = $color->toRgbInt();
520
521 $r_diff = abs( $rgb1['red'] - $rgb2['red'] );
522 $g_diff = abs( $rgb1['green'] - $rgb2['green'] );
523 $b_diff = abs( $rgb1['blue'] - $rgb2['blue'] );
524
525 // Sum of RGB differences.
526 $diff = $r_diff + $g_diff + $b_diff;
527 return $diff;
528 }
529
530 /**
531 * Get distance from the given color using the Delta E method
532 *
533 * @param Jetpack_Color $color Color code.
534 *
535 * @return float
536 */
537 public function getDistanceLabFrom( Jetpack_Color $color ) {
538 $lab1 = $this->toLabCie();
539 $lab2 = $color->toLabCie();
540
541 $l_diff = abs( $lab2['l'] - $lab1['l'] );
542 $a_diff = abs( $lab2['a'] - $lab1['a'] );
543 $b_diff = abs( $lab2['b'] - $lab1['b'] );
544
545 $delta = sqrt( $l_diff + $a_diff + $b_diff );
546
547 return $delta;
548 }
549
550 /**
551 * Calculate luminosity.
552 *
553 * @return float
554 */
555 public function toLuminosity() {
556 $lum = array();
557 foreach ( $this->toRgbInt() as $slot => $value ) {
558 $chan = $value / 255;
559 $lum[ $slot ] = ( $chan <= 0.03928 ) ? $chan / 12.92 : pow( ( ( $chan + 0.055 ) / 1.055 ), 2.4 );
560 }
561 return 0.2126 * $lum['red'] + 0.7152 * $lum['green'] + 0.0722 * $lum['blue'];
562 }
563
564 /**
565 * Get distance between colors using luminance.
566 * Should be more than 5 for readable contrast
567 *
568 * @param Jetpack_Color $color Another color.
569 * @return float
570 */
571 public function getDistanceLuminosityFrom( Jetpack_Color $color ) {
572 $l1 = $this->toLuminosity();
573 $l2 = $color->toLuminosity();
574 if ( $l1 > $l2 ) {
575 return ( $l1 + 0.05 ) / ( $l2 + 0.05 );
576 } else {
577 return ( $l2 + 0.05 ) / ( $l1 + 0.05 );
578 }
579 }
580
581 /**
582 * Get maximum contrast color.
583 *
584 * @return $this
585 */
586 public function getMaxContrastColor() {
587 $with_black = $this->getDistanceLuminosityFrom( new Jetpack_Color( '#000' ) );
588 $with_white = $this->getDistanceLuminosityFrom( new Jetpack_Color( '#fff' ) );
589 $color = new Jetpack_Color();
590 $hex = ( $with_black >= $with_white ) ? '#000000' : '#ffffff';
591 return $color->fromHex( $hex );
592 }
593
594 /**
595 * Get grayscale contrasting color.
596 *
597 * @param bool|int $contrast Contrast.
598 *
599 * @return $this
600 */
601 public function getGrayscaleContrastingColor( $contrast = false ) {
602 if ( ! $contrast ) {
603 return $this->getMaxContrastColor();
604 }
605 // don't allow less than 5.
606 $target_contrast = ( $contrast < 5 ) ? 5 : $contrast;
607 $color = $this->getMaxContrastColor();
608 $contrast = $color->getDistanceLuminosityFrom( $this );
609
610 // if current max contrast is less than the target contrast, we had wishful thinking.
611 if ( $contrast <= $target_contrast ) {
612 return $color;
613 }
614
615 $incr = ( '#000000' === $color->toString() ) ? 1 : -1;
616 while ( $contrast > $target_contrast ) {
617 $color = $color->incrementLightness( $incr );
618 $contrast = $color->getDistanceLuminosityFrom( $this );
619 }
620
621 return $color;
622 }
623
624 /**
625 * Gets a readable contrasting color. $this is assumed to be the text and $color the background color.
626 *
627 * @param object $bg_color A Color object that will be compared against $this.
628 * @param integer $min_contrast The minimum contrast to achieve, if possible.
629 * @return object A Color object, an increased contrast $this compared against $bg_color
630 */
631 public function getReadableContrastingColor( $bg_color = false, $min_contrast = 5 ) {
632 if ( ! $bg_color || ! is_a( $bg_color, 'Jetpack_Color' ) ) {
633 return $this;
634 }
635 // you shouldn't use less than 5, but you might want to.
636 $target_contrast = $min_contrast;
637 // working things.
638 $contrast = $bg_color->getDistanceLuminosityFrom( $this );
639 $max_contrast_color = $bg_color->getMaxContrastColor();
640 $max_contrast = $max_contrast_color->getDistanceLuminosityFrom( $bg_color );
641
642 // if current max contrast is less than the target contrast, we had wishful thinking.
643 // still, go max.
644 if ( $max_contrast <= $target_contrast ) {
645 return $max_contrast_color;
646 }
647 // or, we might already have sufficient contrast.
648 if ( $contrast >= $target_contrast ) {
649 return $this;
650 }
651
652 $incr = ( 0 === $max_contrast_color->toInt() ) ? -1 : 1;
653 while ( $contrast < $target_contrast ) {
654 $this->incrementLightness( $incr );
655 $contrast = $bg_color->getDistanceLuminosityFrom( $this );
656 // infininite loop prevention: you never know.
657 if ( 0 === $this->color || 16777215 === $this->color ) {
658 break;
659 }
660 }
661
662 return $this;
663 }
664
665 /**
666 * Detect if color is grayscale
667 *
668 * @param int $threshold Max difference between colors.
669 *
670 * @return bool
671 */
672 public function isGrayscale( $threshold = 16 ) {
673 $rgb = $this->toRgbInt();
674
675 // Get min and max rgb values, then difference between them.
676 $rgb_min = min( $rgb );
677 $rgb_max = max( $rgb );
678 $diff = $rgb_max - $rgb_min;
679
680 return $diff < $threshold;
681 }
682
683 /**
684 * Get the closest matching color from the given array of colors
685 *
686 * @param array $colors array of integers or Jetpack_Color objects.
687 *
688 * @return mixed the array key of the matched color
689 */
690 public function getClosestMatch( array $colors ) {
691 $match_dist = 10000;
692 $match_key = null;
693 foreach ( $colors as $key => $color ) {
694 if ( false === ( $color instanceof Jetpack_Color ) ) {
695 $c = new Jetpack_Color( $color );
696 }
697 $dist = $this->getDistanceLabFrom( $c );
698 if ( $dist < $match_dist ) {
699 $match_dist = $dist;
700 $match_key = $key;
701 }
702 }
703
704 return $match_key;
705 }
706
707 /* TRANSFORMS */
708
709 /**
710 * Transform -- Darken color.
711 *
712 * @param int $amount Amount. Default to 5.
713 *
714 * @return $this
715 */
716 public function darken( $amount = 5 ) {
717 return $this->incrementLightness( - $amount );
718 }
719
720 /**
721 * Transform -- Lighten color.
722 *
723 * @param int $amount Amount. Default to 5.
724 *
725 * @return $this
726 */
727 public function lighten( $amount = 5 ) {
728 return $this->incrementLightness( $amount );
729 }
730
731 /**
732 * Transform -- Increment lightness.
733 *
734 * @param int $amount Amount.
735 *
736 * @return $this
737 */
738 public function incrementLightness( $amount ) {
739 $hsl = $this->toHsl();
740
741 $h = isset( $hsl['h'] ) ? $hsl['h'] : 0;
742 $s = isset( $hsl['s'] ) ? $hsl['s'] : 0;
743 $l = isset( $hsl['l'] ) ? $hsl['l'] : 0;
744
745 $l += $amount;
746 if ( $l < 0 ) {
747 $l = 0;
748 }
749 if ( $l > 100 ) {
750 $l = 100;
751 }
752 return $this->fromHsl( $h, $s, $l );
753 }
754
755 /**
756 * Transform -- Saturate color.
757 *
758 * @param int $amount Amount. Default to 15.
759 *
760 * @return $this
761 */
762 public function saturate( $amount = 15 ) {
763 return $this->incrementSaturation( $amount );
764 }
765
766 /**
767 * Transform -- Desaturate color.
768 *
769 * @param int $amount Amount. Default to 15.
770 *
771 * @return $this
772 */
773 public function desaturate( $amount = 15 ) {
774 return $this->incrementSaturation( - $amount );
775 }
776
777 /**
778 * Transform -- Increment saturation.
779 *
780 * @param int $amount Amount.
781 *
782 * @return $this
783 */
784 public function incrementSaturation( $amount ) {
785 $hsl = $this->toHsl();
786
787 $h = isset( $hsl['h'] ) ? $hsl['h'] : 0;
788 $s = isset( $hsl['s'] ) ? $hsl['s'] : 0;
789 $l = isset( $hsl['l'] ) ? $hsl['l'] : 0;
790
791 $s += $amount;
792 if ( $s < 0 ) {
793 $s = 0;
794 }
795 if ( $s > 100 ) {
796 $s = 100;
797 }
798 return $this->fromHsl( $h, $s, $l );
799 }
800
801 /**
802 * Transform -- To grayscale.
803 *
804 * @return $this
805 */
806 public function toGrayscale() {
807 $hsl = $this->toHsl();
808
809 $h = isset( $hsl['h'] ) ? $hsl['h'] : 0;
810 $s = 0;
811 $l = isset( $hsl['l'] ) ? $hsl['l'] : 0;
812
813 return $this->fromHsl( $h, $s, $l );
814 }
815
816 /**
817 * Transform -- To the complementary color.
818 *
819 * The complement is the color on the opposite side of the color wheel, 180° away.
820 *
821 * @return $this
822 */
823 public function getComplement() {
824 return $this->incrementHue( 180 );
825 }
826
827 /**
828 * Transform -- To an analogous color of the complement.
829 *
830 * @param int $step Pass `1` or `-1` to choose which direction around the color wheel.
831 *
832 * @return $this
833 */
834 public function getSplitComplement( $step = 1 ) {
835 $incr = 180 + ( $step * 30 );
836 return $this->incrementHue( $incr );
837 }
838
839 /**
840 * Transform -- To an analogous color.
841 *
842 * Analogous colors are those adjacent on the color wheel, separated by 30°.
843 *
844 * @param int $step Pass `1` or `-1` to choose which direction around the color wheel.
845 *
846 * @return $this
847 */
848 public function getAnalog( $step = 1 ) {
849 $incr = $step * 30;
850 return $this->incrementHue( $incr );
851 }
852
853 /**
854 * Transform -- To a tetradic (rectangular) color.
855 *
856 * A rectangular color scheme uses a color, its complement, and the colors 60° from each.
857 * This transforms the color to its 60° "tetrad".
858 *
859 * @param int $step Pass `1` or `-1` to choose which direction around the color wheel.
860 *
861 * @return $this
862 */
863 public function getTetrad( $step = 1 ) {
864 $incr = $step * 60;
865 return $this->incrementHue( $incr );
866 }
867
868 /**
869 * Transform -- To a triadic color.
870 *
871 * A triadic color scheme uses three colors evenly spaced (120°) around the color wheel.
872 * This transforms the color to one of its triadic colors.
873 *
874 * @param int $step Pass `1` or `-1` to choose which direction around the color wheel.
875 *
876 * @return $this
877 */
878 public function getTriad( $step = 1 ) {
879 $incr = $step * 120;
880 return $this->incrementHue( $incr );
881 }
882
883 /**
884 * Transform -- Increment hue.
885 *
886 * @param int $amount Amount.
887 *
888 * @return $this
889 */
890 public function incrementHue( $amount ) {
891 $hsl = $this->toHsl();
892
893 $h = isset( $hsl['h'] ) ? $hsl['h'] : 0;
894 $s = isset( $hsl['s'] ) ? $hsl['s'] : 0;
895 $l = isset( $hsl['l'] ) ? $hsl['l'] : 0;
896
897 $h = ( $h + $amount ) % 360;
898 if ( $h < 0 ) {
899 $h += 360;
900 }
901 return $this->fromHsl( $h, $s, $l );
902 }
903 }
904 }
905