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