PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.0.3
Jetpack – WP Security, Backup, Speed, & Growth v12.0.3
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 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 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 3 years ago core-api 3 years ago debugger 3 years ago markdown 4 years ago class-jetpack-ai-helper.php 3 years ago class-jetpack-currencies.php 5 years ago class-jetpack-google-drive-helper.php 3 years ago class-jetpack-instagram-gallery-helper.php 3 years ago class-jetpack-mapbox-helper.php 3 years ago class-jetpack-podcast-feed-locator.php 5 years ago class-jetpack-podcast-helper.php 3 years ago class-jetpack-recommendations.php 4 years ago class-jetpack-tweetstorm-helper.php 3 years ago class-jetpack-wizard.php 5 years ago class.color.php 4 years ago class.core-rest-api-endpoints.php 3 years ago class.jetpack-automatic-install-skin.php 4 years ago class.jetpack-iframe-embed.php 4 years ago class.jetpack-keyring-service-helper.php 4 years ago class.jetpack-password-checker.php 3 years ago class.jetpack-photon-image-sizes.php 3 years ago class.jetpack-photon-image.php 4 years ago class.jetpack-search-performance-logger.php 4 years ago class.media-extractor.php 3 years ago class.media-summary.php 3 years ago class.media.php 3 years ago components.php 3 years ago debugger.php 4 years ago functions.wp-notify.php 4 years ago icalendar-reader.php 3 years ago markdown.php 3 years ago plans.php 4 years ago plugins.php 4 years ago tonesque.php 3 years ago widgets.php 4 years ago
class.color.php
902 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 /**
20 * Color utilities
21 */
22 class Jetpack_Color {
23 /**
24 * Color code (later array or string, depending on type)
25 *
26 * @var int|array|string
27 */
28 protected $color = 0;
29
30 /**
31 * Initialize object
32 *
33 * @param string|array $color A color of the type $type.
34 * @param string $type The type of color we will construct from.
35 * One of hex (default), rgb, hsl, int.
36 */
37 public function __construct( $color = null, $type = 'hex' ) {
38 if ( $color ) {
39 switch ( $type ) {
40 case 'hex':
41 $this->fromHex( $color );
42 break;
43 case 'rgb':
44 if ( is_array( $color ) && count( $color ) === 3 ) {
45 list( $r, $g, $b ) = array_values( $color );
46 $this->fromRgbInt( $r, $g, $b );
47 }
48 break;
49 case 'hsl':
50 if ( is_array( $color ) && count( $color ) === 3 ) {
51 list( $h, $s, $l ) = array_values( $color );
52 $this->fromHsl( $h, $s, $l );
53 }
54 break;
55 case 'int':
56 $this->fromInt( $color );
57 break;
58 default:
59 // there is no default.
60 break;
61 }
62 }
63 }
64
65 /**
66 * Init color from hex value
67 *
68 * @param string $hex_value Color hex value.
69 *
70 * @return $this
71 * @throws RangeException Invalid color code range error.
72 */
73 public function fromHex( $hex_value ) {
74 $hex_value = str_replace( '#', '', $hex_value );
75 // handle short hex codes like #fff.
76 if ( 3 === strlen( $hex_value ) ) {
77 $hex_value = $hex_value[0] . $hex_value[0] . $hex_value[1] . $hex_value[1] . $hex_value[2] . $hex_value[2];
78 }
79 return $this->fromInt( hexdec( $hex_value ) );
80 }
81
82 /**
83 * Init color from integer RGB values
84 *
85 * @param int $red Red color code.
86 * @param int $green Green color code.
87 * @param int $blue Blue color code.
88 *
89 * @return $this
90 * @throws RangeException Invalid color code range error.
91 */
92 public function fromRgbInt( $red, $green, $blue ) {
93 if ( $red < 0 || $red > 255 ) {
94 throw new RangeException( 'Red value ' . $red . ' out of valid color code range' );
95 }
96
97 if ( $green < 0 || $green > 255 ) {
98 throw new RangeException( 'Green value ' . $green . ' out of valid color code range' );
99 }
100
101 if ( $blue < 0 || $blue > 255 ) {
102 throw new RangeException( 'Blue value ' . $blue . ' out of valid color code range' );
103 }
104
105 $this->color = (int) ( ( $red << 16 ) + ( $green << 8 ) + $blue );
106
107 return $this;
108 }
109
110 /**
111 * Init color from hex RGB values
112 *
113 * @param string $red Red color code.
114 * @param string $green Green color code.
115 * @param string $blue Blue color code.
116 *
117 * @return $this
118 */
119 public function fromRgbHex( $red, $green, $blue ) {
120 return $this->fromRgbInt( hexdec( $red ), hexdec( $green ), hexdec( $blue ) );
121 }
122
123 /**
124 * Converts an HSL color value to RGB. Conversion formula
125 * adapted from https://en.wikipedia.org/wiki/HSL_color_space.
126 *
127 * @param int $h Hue. [0-360].
128 * @param int $s Saturation [0, 100].
129 * @param int $l Lightness [0, 100].
130 */
131 public function fromHsl( $h, $s, $l ) {
132 $h /= 360;
133 $s /= 100;
134 $l /= 100;
135
136 if ( 0 === $s ) {
137 // achromatic.
138 $r = $l;
139 $g = $l;
140 $b = $l;
141 } else {
142 $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s;
143 $p = 2 * $l - $q;
144 $r = $this->hue2rgb( $p, $q, $h + 1 / 3 );
145 $g = $this->hue2rgb( $p, $q, $h );
146 $b = $this->hue2rgb( $p, $q, $h - 1 / 3 );
147 }
148
149 return $this->fromRgbInt( $r * 255, $g * 255, $b * 255 );
150 }
151
152 /**
153 * Helper function for Jetpack_Color::fromHsl()
154 *
155 * @param float $p Minimum of R/G/B [0, 1].
156 * @param float $q Maximum of R/G/B [0, 1].
157 * @param float $t Adjusted hue [0, 1].
158 */
159 private function hue2rgb( $p, $q, $t ) {
160 if ( $t < 0 ) {
161 ++$t;
162 }
163 if ( $t > 1 ) {
164 --$t;
165 }
166 if ( $t < 1 / 6 ) {
167 return $p + ( $q - $p ) * 6 * $t;
168 }
169 if ( $t < 1 / 2 ) {
170 return $q;
171 }
172 if ( $t < 2 / 3 ) {
173 return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6;
174 }
175 return $p;
176 }
177
178 /**
179 * Init color from integer value
180 *
181 * @param int $int_value Color code.
182 *
183 * @return $this
184 * @throws RangeException Invalid color code range error.
185 */
186 public function fromInt( $int_value ) {
187 if ( $int_value < 0 || $int_value > 16777215 ) {
188 throw new RangeException( $int_value . ' out of valid color code range' );
189 }
190
191 $this->color = $int_value;
192
193 return $this;
194 }
195
196 /**
197 * Convert color to hex
198 *
199 * @return string
200 */
201 public function toHex() {
202 return sprintf( '%06x', $this->color );
203 }
204
205 /**
206 * Convert color to RGB array (integer values)
207 *
208 * @return array
209 */
210 public function toRgbInt() {
211 return array(
212 'red' => (int) ( 255 & ( $this->color >> 16 ) ),
213 'green' => (int) ( 255 & ( $this->color >> 8 ) ),
214 'blue' => (int) ( 255 & ( $this->color ) ),
215 );
216 }
217
218 /**
219 * Convert color to RGB array (hex values)
220 *
221 * @return array
222 */
223 public function toRgbHex() {
224 $r = array();
225 foreach ( $this->toRgbInt() as $item ) {
226 $r[] = dechex( $item );
227 }
228 return $r;
229 }
230
231 /**
232 * Get Hue/Saturation/Value for the current color
233 * (float values, slow but accurate)
234 *
235 * @return array
236 */
237 public function toHsvFloat() {
238 $rgb = $this->toRgbInt();
239
240 $rgb_min = min( $rgb );
241 $rgb_max = max( $rgb );
242
243 $hsv = array(
244 'hue' => 0,
245 'sat' => 0,
246 'val' => $rgb_max,
247 );
248
249 // If v is 0, color is black.
250 if ( 0 === $hsv['val'] ) {
251 return $hsv;
252 }
253
254 // Normalize RGB values to 1.
255 $rgb['red'] /= $hsv['val'];
256 $rgb['green'] /= $hsv['val'];
257 $rgb['blue'] /= $hsv['val'];
258 $rgb_min = min( $rgb );
259 $rgb_max = max( $rgb );
260
261 // Calculate saturation.
262 $hsv['sat'] = $rgb_max - $rgb_min;
263 if ( 0 === $hsv['sat'] ) {
264 $hsv['hue'] = 0;
265 return $hsv;
266 }
267
268 // Normalize saturation to 1.
269 $rgb['red'] = ( $rgb['red'] - $rgb_min ) / ( $rgb_max - $rgb_min );
270 $rgb['green'] = ( $rgb['green'] - $rgb_min ) / ( $rgb_max - $rgb_min );
271 $rgb['blue'] = ( $rgb['blue'] - $rgb_min ) / ( $rgb_max - $rgb_min );
272 $rgb_min = min( $rgb );
273 $rgb_max = max( $rgb );
274
275 // Calculate hue.
276 if ( $rgb_max === $rgb['red'] ) {
277 $hsv['hue'] = 0.0 + 60 * ( $rgb['green'] - $rgb['blue'] );
278 if ( $hsv['hue'] < 0 ) {
279 $hsv['hue'] += 360;
280 }
281 } elseif ( $rgb_max === $rgb['green'] ) {
282 $hsv['hue'] = 120 + ( 60 * ( $rgb['blue'] - $rgb['red'] ) );
283 } else {
284 $hsv['hue'] = 240 + ( 60 * ( $rgb['red'] - $rgb['green'] ) );
285 }
286
287 return $hsv;
288 }
289
290 /**
291 * Get HSV values for color
292 * (integer values from 0-255, fast but less accurate)
293 *
294 * @return array
295 */
296 public function toHsvInt() {
297 $rgb = $this->toRgbInt();
298
299 $rgb_min = min( $rgb );
300 $rgb_max = max( $rgb );
301
302 $hsv = array(
303 'hue' => 0,
304 'sat' => 0,
305 'val' => $rgb_max,
306 );
307
308 // If value is 0, color is black.
309 if ( 0 === $hsv['val'] ) {
310 return $hsv;
311 }
312
313 // Calculate saturation.
314 $hsv['sat'] = round( 255 * ( $rgb_max - $rgb_min ) / $hsv['val'] );
315 if ( 0 === $hsv['sat'] ) {
316 $hsv['hue'] = 0;
317 return $hsv;
318 }
319
320 // Calculate hue.
321 if ( $rgb_max === $rgb['red'] ) {
322 $hsv['hue'] = round( 0 + 43 * ( $rgb['green'] - $rgb['blue'] ) / ( $rgb_max - $rgb_min ) );
323 } elseif ( $rgb_max === $rgb['green'] ) {
324 $hsv['hue'] = round( 85 + 43 * ( $rgb['blue'] - $rgb['red'] ) / ( $rgb_max - $rgb_min ) );
325 } else {
326 $hsv['hue'] = round( 171 + 43 * ( $rgb['red'] - $rgb['green'] ) / ( $rgb_max - $rgb_min ) );
327 }
328 if ( $hsv['hue'] < 0 ) {
329 $hsv['hue'] += 255;
330 }
331
332 return $hsv;
333 }
334
335 /**
336 * Converts an RGB color value to HSL. Conversion formula
337 * adapted from https://en.wikipedia.org/wiki/HSL_color_space.
338 * Assumes r, g, and b are contained in the set [0, 255] and
339 * returns h in [0, 360], s in [0, 100], l in [0, 100]
340 *
341 * @return Array The HSL representation
342 */
343 public function toHsl() {
344 list( $r, $g, $b ) = array_values( $this->toRgbInt() );
345 $r /= 255;
346 $g /= 255;
347 $b /= 255;
348 $max = max( $r, $g, $b );
349 $min = min( $r, $g, $b );
350 $l = ( $max + $min ) / 2;
351
352 if ( $max === $min ) {
353 // achromatic.
354 $s = 0;
355 $h = 0;
356 } else {
357 $d = $max - $min;
358 $s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min );
359 switch ( $max ) {
360 case $r:
361 $h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 );
362 break;
363 case $g:
364 $h = ( $b - $r ) / $d + 2;
365 break;
366 case $b:
367 $h = ( $r - $g ) / $d + 4;
368 break;
369 }
370 $h /= 6;
371 }
372 $h = (int) round( $h * 360 );
373 $s = (int) round( $s * 100 );
374 $l = (int) round( $l * 100 );
375 return compact( 'h', 's', 'l' );
376 }
377
378 /**
379 * From a color code to a string to be used in CSS declaration.
380 *
381 * @param string $type Color code type.
382 * @param int $alpha Transparency.
383 *
384 * @return string
385 */
386 public function toCSS( $type = 'hex', $alpha = 1 ) {
387 switch ( $type ) {
388 case 'hex':
389 return $this->toString();
390 case 'rgb':
391 case 'rgba':
392 list( $r, $g, $b ) = array_values( $this->toRgbInt() );
393 if ( is_numeric( $alpha ) && $alpha < 1 ) {
394 return "rgba( {$r}, {$g}, {$b}, $alpha )";
395 } else {
396 return "rgb( {$r}, {$g}, {$b} )";
397 }
398 case 'hsl':
399 case 'hsla':
400 list( $h, $s, $l ) = array_values( $this->toHsl() );
401 if ( is_numeric( $alpha ) && $alpha < 1 ) {
402 return "hsla( {$h}, {$s}, {$l}, $alpha )";
403 } else {
404 return "hsl( {$h}, {$s}, {$l} )";
405 }
406 default:
407 return $this->toString();
408 }
409 }
410
411 /**
412 * Get current color in XYZ format
413 *
414 * @return array
415 */
416 public function toXyz() {
417 $rgb = $this->toRgbInt();
418
419 // Normalize RGB values to 1.
420 $rgb_new = array();
421 foreach ( $rgb as $item ) {
422 $rgb_new[] = $item / 255;
423 }
424 $rgb = $rgb_new;
425
426 $rgb_new = array();
427 foreach ( $rgb as $item ) {
428 if ( $item > 0.04045 ) {
429 $item = pow( ( ( $item + 0.055 ) / 1.055 ), 2.4 );
430 } else {
431 $item = $item / 12.92;
432 }
433 $rgb_new[] = $item * 100;
434 }
435 $rgb = $rgb_new;
436
437 // Observer. = 2°, Illuminant = D65.
438 $xyz = array(
439 'x' => ( $rgb['red'] * 0.4124 ) + ( $rgb['green'] * 0.3576 ) + ( $rgb['blue'] * 0.1805 ),
440 'y' => ( $rgb['red'] * 0.2126 ) + ( $rgb['green'] * 0.7152 ) + ( $rgb['blue'] * 0.0722 ),
441 'z' => ( $rgb['red'] * 0.0193 ) + ( $rgb['green'] * 0.1192 ) + ( $rgb['blue'] * 0.9505 ),
442 );
443
444 return $xyz;
445 }
446
447 /**
448 * Get color CIE-Lab values
449 *
450 * @return array
451 */
452 public function toLabCie() {
453 $xyz = $this->toXyz();
454
455 // Ovserver = 2*, Iluminant=D65.
456 $xyz['x'] /= 95.047;
457 $xyz['y'] /= 100;
458 $xyz['z'] /= 108.883;
459
460 $xyz_new = array();
461 foreach ( $xyz as $item ) {
462 if ( $item > 0.008856 ) {
463 $xyz_new[] = pow( $item, 1 / 3 );
464 } else {
465 $xyz_new[] = ( 7.787 * $item ) + ( 16 / 116 );
466 }
467 }
468 $xyz = $xyz_new;
469
470 $lab = array(
471 'l' => ( 116 * $xyz['y'] ) - 16,
472 'a' => 500 * ( $xyz['x'] - $xyz['y'] ),
473 'b' => 200 * ( $xyz['y'] - $xyz['z'] ),
474 );
475
476 return $lab;
477 }
478
479 /**
480 * Convert color to integer
481 *
482 * @return int
483 */
484 public function toInt() {
485 return $this->color;
486 }
487
488 /**
489 * Alias of toString()
490 *
491 * @return string
492 */
493 public function __toString() {
494 return $this->toString();
495 }
496
497 /**
498 * Get color as string
499 *
500 * @return string
501 */
502 public function toString() {
503 $str = $this->toHex();
504 return strtoupper( "#{$str}" );
505 }
506
507 /**
508 * Get the distance between this color and the given color
509 *
510 * @param Jetpack_Color $color Color code.
511 *
512 * @return int
513 */
514 public function getDistanceRgbFrom( Jetpack_Color $color ) {
515 $rgb1 = $this->toRgbInt();
516 $rgb2 = $color->toRgbInt();
517
518 $r_diff = abs( $rgb1['red'] - $rgb2['red'] );
519 $g_diff = abs( $rgb1['green'] - $rgb2['green'] );
520 $b_diff = abs( $rgb1['blue'] - $rgb2['blue'] );
521
522 // Sum of RGB differences.
523 $diff = $r_diff + $g_diff + $b_diff;
524 return $diff;
525 }
526
527 /**
528 * Get distance from the given color using the Delta E method
529 *
530 * @param Jetpack_Color $color Color code.
531 *
532 * @return float
533 */
534 public function getDistanceLabFrom( Jetpack_Color $color ) {
535 $lab1 = $this->toLabCie();
536 $lab2 = $color->toLabCie();
537
538 $l_diff = abs( $lab2['l'] - $lab1['l'] );
539 $a_diff = abs( $lab2['a'] - $lab1['a'] );
540 $b_diff = abs( $lab2['b'] - $lab1['b'] );
541
542 $delta = sqrt( $l_diff + $a_diff + $b_diff );
543
544 return $delta;
545 }
546
547 /**
548 * Calculate luminosity.
549 *
550 * @return float
551 */
552 public function toLuminosity() {
553 $lum = array();
554 foreach ( $this->toRgbInt() as $slot => $value ) {
555 $chan = $value / 255;
556 $lum[ $slot ] = ( $chan <= 0.03928 ) ? $chan / 12.92 : pow( ( ( $chan + 0.055 ) / 1.055 ), 2.4 );
557 }
558 return 0.2126 * $lum['red'] + 0.7152 * $lum['green'] + 0.0722 * $lum['blue'];
559 }
560
561 /**
562 * Get distance between colors using luminance.
563 * Should be more than 5 for readable contrast
564 *
565 * @param Jetpack_Color $color Another color.
566 * @return float
567 */
568 public function getDistanceLuminosityFrom( Jetpack_Color $color ) {
569 $l1 = $this->toLuminosity();
570 $l2 = $color->toLuminosity();
571 if ( $l1 > $l2 ) {
572 return ( $l1 + 0.05 ) / ( $l2 + 0.05 );
573 } else {
574 return ( $l2 + 0.05 ) / ( $l1 + 0.05 );
575 }
576 }
577
578 /**
579 * Get maximum contrast color.
580 *
581 * @return $this
582 */
583 public function getMaxContrastColor() {
584 $with_black = $this->getDistanceLuminosityFrom( new Jetpack_Color( '#000' ) );
585 $with_white = $this->getDistanceLuminosityFrom( new Jetpack_Color( '#fff' ) );
586 $color = new Jetpack_Color();
587 $hex = ( $with_black >= $with_white ) ? '#000000' : '#ffffff';
588 return $color->fromHex( $hex );
589 }
590
591 /**
592 * Get grayscale contrasting color.
593 *
594 * @param bool|int $contrast Contrast.
595 *
596 * @return $this
597 */
598 public function getGrayscaleContrastingColor( $contrast = false ) {
599 if ( ! $contrast ) {
600 return $this->getMaxContrastColor();
601 }
602 // don't allow less than 5.
603 $target_contrast = ( $contrast < 5 ) ? 5 : $contrast;
604 $color = $this->getMaxContrastColor();
605 $contrast = $color->getDistanceLuminosityFrom( $this );
606
607 // if current max contrast is less than the target contrast, we had wishful thinking.
608 if ( $contrast <= $target_contrast ) {
609 return $color;
610 }
611
612 $incr = ( '#000000' === $color->toString() ) ? 1 : -1;
613 while ( $contrast > $target_contrast ) {
614 $color = $color->incrementLightness( $incr );
615 $contrast = $color->getDistanceLuminosityFrom( $this );
616 }
617
618 return $color;
619 }
620
621 /**
622 * Gets a readable contrasting color. $this is assumed to be the text and $color the background color.
623 *
624 * @param object $bg_color A Color object that will be compared against $this.
625 * @param integer $min_contrast The minimum contrast to achieve, if possible.
626 * @return object A Color object, an increased contrast $this compared against $bg_color
627 */
628 public function getReadableContrastingColor( $bg_color = false, $min_contrast = 5 ) {
629 if ( ! $bg_color || ! is_a( $bg_color, 'Jetpack_Color' ) ) {
630 return $this;
631 }
632 // you shouldn't use less than 5, but you might want to.
633 $target_contrast = $min_contrast;
634 // working things.
635 $contrast = $bg_color->getDistanceLuminosityFrom( $this );
636 $max_contrast_color = $bg_color->getMaxContrastColor();
637 $max_contrast = $max_contrast_color->getDistanceLuminosityFrom( $bg_color );
638
639 // if current max contrast is less than the target contrast, we had wishful thinking.
640 // still, go max.
641 if ( $max_contrast <= $target_contrast ) {
642 return $max_contrast_color;
643 }
644 // or, we might already have sufficient contrast.
645 if ( $contrast >= $target_contrast ) {
646 return $this;
647 }
648
649 $incr = ( 0 === $max_contrast_color->toInt() ) ? -1 : 1;
650 while ( $contrast < $target_contrast ) {
651 $this->incrementLightness( $incr );
652 $contrast = $bg_color->getDistanceLuminosityFrom( $this );
653 // infininite loop prevention: you never know.
654 if ( 0 === $this->color || 16777215 === $this->color ) {
655 break;
656 }
657 }
658
659 return $this;
660 }
661
662 /**
663 * Detect if color is grayscale
664 *
665 * @param int $threshold Max difference between colors.
666 *
667 * @return bool
668 */
669 public function isGrayscale( $threshold = 16 ) {
670 $rgb = $this->toRgbInt();
671
672 // Get min and max rgb values, then difference between them.
673 $rgb_min = min( $rgb );
674 $rgb_max = max( $rgb );
675 $diff = $rgb_max - $rgb_min;
676
677 return $diff < $threshold;
678 }
679
680 /**
681 * Get the closest matching color from the given array of colors
682 *
683 * @param array $colors array of integers or Jetpack_Color objects.
684 *
685 * @return mixed the array key of the matched color
686 */
687 public function getClosestMatch( array $colors ) {
688 $match_dist = 10000;
689 $match_key = null;
690 foreach ( $colors as $key => $color ) {
691 if ( false === ( $color instanceof Jetpack_Color ) ) {
692 $c = new Jetpack_Color( $color );
693 }
694 $dist = $this->getDistanceLabFrom( $c );
695 if ( $dist < $match_dist ) {
696 $match_dist = $dist;
697 $match_key = $key;
698 }
699 }
700
701 return $match_key;
702 }
703
704 /* TRANSFORMS */
705
706 /**
707 * Transform -- Darken color.
708 *
709 * @param int $amount Amount. Default to 5.
710 *
711 * @return $this
712 */
713 public function darken( $amount = 5 ) {
714 return $this->incrementLightness( - $amount );
715 }
716
717 /**
718 * Transform -- Lighten color.
719 *
720 * @param int $amount Amount. Default to 5.
721 *
722 * @return $this
723 */
724 public function lighten( $amount = 5 ) {
725 return $this->incrementLightness( $amount );
726 }
727
728 /**
729 * Transform -- Increment lightness.
730 *
731 * @param int $amount Amount.
732 *
733 * @return $this
734 */
735 public function incrementLightness( $amount ) {
736 $hsl = $this->toHsl();
737
738 $h = isset( $hsl['h'] ) ? $hsl['h'] : 0;
739 $s = isset( $hsl['s'] ) ? $hsl['s'] : 0;
740 $l = isset( $hsl['l'] ) ? $hsl['l'] : 0;
741
742 $l += $amount;
743 if ( $l < 0 ) {
744 $l = 0;
745 }
746 if ( $l > 100 ) {
747 $l = 100;
748 }
749 return $this->fromHsl( $h, $s, $l );
750 }
751
752 /**
753 * Transform -- Saturate color.
754 *
755 * @param int $amount Amount. Default to 15.
756 *
757 * @return $this
758 */
759 public function saturate( $amount = 15 ) {
760 return $this->incrementSaturation( $amount );
761 }
762
763 /**
764 * Transform -- Desaturate color.
765 *
766 * @param int $amount Amount. Default to 15.
767 *
768 * @return $this
769 */
770 public function desaturate( $amount = 15 ) {
771 return $this->incrementSaturation( - $amount );
772 }
773
774 /**
775 * Transform -- Increment saturation.
776 *
777 * @param int $amount Amount.
778 *
779 * @return $this
780 */
781 public function incrementSaturation( $amount ) {
782 $hsl = $this->toHsl();
783
784 $h = isset( $hsl['h'] ) ? $hsl['h'] : 0;
785 $s = isset( $hsl['s'] ) ? $hsl['s'] : 0;
786 $l = isset( $hsl['l'] ) ? $hsl['l'] : 0;
787
788 $s += $amount;
789 if ( $s < 0 ) {
790 $s = 0;
791 }
792 if ( $s > 100 ) {
793 $s = 100;
794 }
795 return $this->fromHsl( $h, $s, $l );
796 }
797
798 /**
799 * Transform -- To grayscale.
800 *
801 * @return $this
802 */
803 public function toGrayscale() {
804 $hsl = $this->toHsl();
805
806 $h = isset( $hsl['h'] ) ? $hsl['h'] : 0;
807 $s = 0;
808 $l = isset( $hsl['l'] ) ? $hsl['l'] : 0;
809
810 return $this->fromHsl( $h, $s, $l );
811 }
812
813 /**
814 * Transform -- To the complementary color.
815 *
816 * The complement is the color on the opposite side of the color wheel, 180° away.
817 *
818 * @return $this
819 */
820 public function getComplement() {
821 return $this->incrementHue( 180 );
822 }
823
824 /**
825 * Transform -- To an analogous color of the complement.
826 *
827 * @param int $step Pass `1` or `-1` to choose which direction around the color wheel.
828 *
829 * @return $this
830 */
831 public function getSplitComplement( $step = 1 ) {
832 $incr = 180 + ( $step * 30 );
833 return $this->incrementHue( $incr );
834 }
835
836 /**
837 * Transform -- To an analogous color.
838 *
839 * Analogous colors are those adjacent on the color wheel, separated by 30°.
840 *
841 * @param int $step Pass `1` or `-1` to choose which direction around the color wheel.
842 *
843 * @return $this
844 */
845 public function getAnalog( $step = 1 ) {
846 $incr = $step * 30;
847 return $this->incrementHue( $incr );
848 }
849
850 /**
851 * Transform -- To a tetradic (rectangular) color.
852 *
853 * A rectangular color scheme uses a color, its complement, and the colors 60° from each.
854 * This transforms the color to its 60° "tetrad".
855 *
856 * @param int $step Pass `1` or `-1` to choose which direction around the color wheel.
857 *
858 * @return $this
859 */
860 public function getTetrad( $step = 1 ) {
861 $incr = $step * 60;
862 return $this->incrementHue( $incr );
863 }
864
865 /**
866 * Transform -- To a triadic color.
867 *
868 * A triadic color scheme uses three colors evenly spaced (120°) around the color wheel.
869 * This transforms the color to one of its triadic colors.
870 *
871 * @param int $step Pass `1` or `-1` to choose which direction around the color wheel.
872 *
873 * @return $this
874 */
875 public function getTriad( $step = 1 ) {
876 $incr = $step * 120;
877 return $this->incrementHue( $incr );
878 }
879
880 /**
881 * Transform -- Increment hue.
882 *
883 * @param int $amount Amount.
884 *
885 * @return $this
886 */
887 public function incrementHue( $amount ) {
888 $hsl = $this->toHsl();
889
890 $h = isset( $hsl['h'] ) ? $hsl['h'] : 0;
891 $s = isset( $hsl['s'] ) ? $hsl['s'] : 0;
892 $l = isset( $hsl['l'] ) ? $hsl['l'] : 0;
893
894 $h = ( $h + $amount ) % 360;
895 if ( $h < 0 ) {
896 $h += 360;
897 }
898 return $this->fromHsl( $h, $s, $l );
899 }
900
901 }
902