PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | _inc/lib/class.color.php +904 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,904 @@
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 <[email protected]>
11 + * @author Matt Wiebe <[email protected]>
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 +}