@@ -69,6 +69,28 @@ function wp_tinycolor_bound01( $n, $max ) {
69
69
return ( $ n % $ max ) / (float ) $ max ;
70
70
}
71
71
72
+ /**
73
+ * Direct port of tinycolor's boundAlpha function to maintain consistency with
74
+ * how tinycolor works.
75
+ *
76
+ * @see https://github.com/bgrins/TinyColor
77
+ *
78
+ * @since 5.9.0
79
+ * @access private
80
+ *
81
+ * @param mixed $n Number of unknown type.
82
+ * @return float Value in the range [0,1].
83
+ */
84
+ function _wp_tinycolor_bound_alpha ( $ n ) {
85
+ if ( is_numeric ( $ n ) ) {
86
+ $ n = (float ) $ n ;
87
+ if ( $ n >= 0 && $ n <= 1 ) {
88
+ return $ n ;
89
+ }
90
+ }
91
+ return 1 ;
92
+ }
93
+
72
94
/**
73
95
* Round and convert values of an RGB object.
74
96
*
@@ -170,8 +192,7 @@ function wp_tinycolor_hsl_to_rgb( $hsl_color ) {
170
192
171
193
/**
172
194
* Parses hex, hsl, and rgb CSS strings using the same regex as TinyColor v1.4.2
173
- * used in the JavaScript. Only colors output from react-color are implemented
174
- * and the alpha value is ignored as it is not used in duotone.
195
+ * used in the JavaScript. Only colors output from react-color are implemented.
175
196
*
176
197
* Direct port of TinyColor's function, lightly simplified to maintain
177
198
* consistency with TinyColor.
@@ -180,6 +201,7 @@ function wp_tinycolor_hsl_to_rgb( $hsl_color ) {
180
201
* @see https://github.com/casesandberg/react-color/
181
202
*
182
203
* @since 5.8.0
204
+ * @since 5.9.0 Added alpha processing.
183
205
* @access private
184
206
*
185
207
* @param string $color_str CSS color string.
@@ -199,35 +221,47 @@ function wp_tinycolor_string_to_rgb( $color_str ) {
199
221
200
222
$ rgb_regexp = '/^rgb ' . $ permissive_match3 . '$/ ' ;
201
223
if ( preg_match ( $ rgb_regexp , $ color_str , $ match ) ) {
202
- return wp_tinycolor_rgb_to_rgb (
224
+ $ rgb = wp_tinycolor_rgb_to_rgb (
203
225
array (
204
226
'r ' => $ match [1 ],
205
227
'g ' => $ match [2 ],
206
228
'b ' => $ match [3 ],
207
229
)
208
230
);
231
+
232
+ $ rgb ['a ' ] = 1 ;
233
+
234
+ return $ rgb ;
209
235
}
210
236
211
237
$ rgba_regexp = '/^rgba ' . $ permissive_match4 . '$/ ' ;
212
238
if ( preg_match ( $ rgba_regexp , $ color_str , $ match ) ) {
213
- return wp_tinycolor_rgb_to_rgb (
239
+ $ rgb = wp_tinycolor_rgb_to_rgb (
214
240
array (
215
241
'r ' => $ match [1 ],
216
242
'g ' => $ match [2 ],
217
243
'b ' => $ match [3 ],
218
244
)
219
245
);
246
+
247
+ $ rgb ['a ' ] = _wp_tinycolor_bound_alpha ( $ match [4 ] );
248
+
249
+ return $ rgb ;
220
250
}
221
251
222
252
$ hsl_regexp = '/^hsl ' . $ permissive_match3 . '$/ ' ;
223
253
if ( preg_match ( $ hsl_regexp , $ color_str , $ match ) ) {
224
- return wp_tinycolor_hsl_to_rgb (
254
+ $ rgb = wp_tinycolor_hsl_to_rgb (
225
255
array (
226
256
'h ' => $ match [1 ],
227
257
's ' => $ match [2 ],
228
258
'l ' => $ match [3 ],
229
259
)
230
260
);
261
+
262
+ $ rgb ['a ' ] = 1 ;
263
+
264
+ return $ rgb ;
231
265
}
232
266
233
267
$ hsla_regexp = '/^hsla ' . $ permissive_match4 . '$/ ' ;
@@ -239,50 +273,87 @@ function wp_tinycolor_string_to_rgb( $color_str ) {
239
273
'l ' => $ match [3 ],
240
274
)
241
275
);
276
+
277
+ $ rgb ['a ' ] = _wp_tinycolor_bound_alpha ( $ match [4 ] );
278
+
279
+ return $ rgb ;
242
280
}
243
281
244
282
$ hex8_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ ' ;
245
283
if ( preg_match ( $ hex8_regexp , $ color_str , $ match ) ) {
246
- return wp_tinycolor_rgb_to_rgb (
284
+ $ rgb = wp_tinycolor_rgb_to_rgb (
247
285
array (
248
286
'r ' => base_convert ( $ match [1 ], 16 , 10 ),
249
287
'g ' => base_convert ( $ match [2 ], 16 , 10 ),
250
288
'b ' => base_convert ( $ match [3 ], 16 , 10 ),
251
289
)
252
290
);
291
+
292
+ $ rgb ['a ' ] = _wp_tinycolor_bound_alpha (
293
+ base_convert ( $ match [4 ], 16 , 10 ) / 255
294
+ );
295
+
296
+ return $ rgb ;
253
297
}
254
298
255
299
$ hex6_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ ' ;
256
300
if ( preg_match ( $ hex6_regexp , $ color_str , $ match ) ) {
257
- return wp_tinycolor_rgb_to_rgb (
301
+ $ rgb = wp_tinycolor_rgb_to_rgb (
258
302
array (
259
303
'r ' => base_convert ( $ match [1 ], 16 , 10 ),
260
304
'g ' => base_convert ( $ match [2 ], 16 , 10 ),
261
305
'b ' => base_convert ( $ match [3 ], 16 , 10 ),
262
306
)
263
307
);
308
+
309
+ $ rgb ['a ' ] = 1 ;
310
+
311
+ return $ rgb ;
264
312
}
265
313
266
314
$ hex4_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/ ' ;
267
315
if ( preg_match ( $ hex4_regexp , $ color_str , $ match ) ) {
268
- return wp_tinycolor_rgb_to_rgb (
316
+ $ rgb = wp_tinycolor_rgb_to_rgb (
269
317
array (
270
318
'r ' => base_convert ( $ match [1 ] . $ match [1 ], 16 , 10 ),
271
319
'g ' => base_convert ( $ match [2 ] . $ match [2 ], 16 , 10 ),
272
320
'b ' => base_convert ( $ match [3 ] . $ match [3 ], 16 , 10 ),
273
321
)
274
322
);
323
+
324
+ $ rgb ['a ' ] = _wp_tinycolor_bound_alpha (
325
+ base_convert ( $ match [4 ] . $ match [4 ], 16 , 10 ) / 255
326
+ );
327
+
328
+ return $ rgb ;
275
329
}
276
330
277
331
$ hex3_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/ ' ;
278
332
if ( preg_match ( $ hex3_regexp , $ color_str , $ match ) ) {
279
- return wp_tinycolor_rgb_to_rgb (
333
+ $ rgb = wp_tinycolor_rgb_to_rgb (
280
334
array (
281
335
'r ' => base_convert ( $ match [1 ] . $ match [1 ], 16 , 10 ),
282
336
'g ' => base_convert ( $ match [2 ] . $ match [2 ], 16 , 10 ),
283
337
'b ' => base_convert ( $ match [3 ] . $ match [3 ], 16 , 10 ),
284
338
)
285
339
);
340
+
341
+ $ rgb ['a ' ] = 1 ;
342
+
343
+ return $ rgb ;
344
+ }
345
+
346
+ /*
347
+ * The JS color picker considers the string "transparent" to be a hex value,
348
+ * so we need to handle it here as a special case.
349
+ */
350
+ if ( 'transparent ' === $ color_str ) {
351
+ return array (
352
+ 'r ' => 0 ,
353
+ 'g ' => 0 ,
354
+ 'b ' => 0 ,
355
+ 'a ' => 0 ,
356
+ );
286
357
}
287
358
}
288
359
@@ -313,6 +384,95 @@ function wp_register_duotone_support( $block_type ) {
313
384
}
314
385
}
315
386
}
387
+ /**
388
+ * Renders the duotone filter SVG and returns the CSS filter property to
389
+ * reference the rendered SVG.
390
+ *
391
+ * @since 5.9.0
392
+ *
393
+ * @param array $preset Duotone preset value as seen in theme.json.
394
+ * @return string Duotone CSS filter property.
395
+ */
396
+ function wp_render_duotone_filter_preset ( $ preset ) {
397
+ $ duotone_id = $ preset ['slug ' ];
398
+ $ duotone_colors = $ preset ['colors ' ];
399
+ $ filter_id = 'wp-duotone- ' . $ duotone_id ;
400
+ $ duotone_values = array (
401
+ 'r ' => array (),
402
+ 'g ' => array (),
403
+ 'b ' => array (),
404
+ 'a ' => array (),
405
+ );
406
+ foreach ( $ duotone_colors as $ color_str ) {
407
+ $ color = wp_tinycolor_string_to_rgb ( $ color_str );
408
+
409
+ $ duotone_values ['r ' ][] = $ color ['r ' ] / 255 ;
410
+ $ duotone_values ['g ' ][] = $ color ['g ' ] / 255 ;
411
+ $ duotone_values ['b ' ][] = $ color ['b ' ] / 255 ;
412
+ $ duotone_values ['a ' ][] = $ color ['a ' ];
413
+ }
414
+
415
+ ob_start ();
416
+
417
+ ?>
418
+
419
+ <svg
420
+ xmlns="http://www.w3.org/2000/svg"
421
+ viewBox="0 0 0 0"
422
+ width="0"
423
+ height="0"
424
+ focusable="false"
425
+ role="none"
426
+ style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;"
427
+ >
428
+ <defs>
429
+ <filter id="<?php echo esc_attr ( $ filter_id ); ?> ">
430
+ <feColorMatrix
431
+ color-interpolation-filters="sRGB"
432
+ type="matrix"
433
+ values="
434
+ .299 .587 .114 0 0
435
+ .299 .587 .114 0 0
436
+ .299 .587 .114 0 0
437
+ .299 .587 .114 0 0
438
+ "
439
+ />
440
+ <feComponentTransfer color-interpolation-filters="sRGB" >
441
+ <feFuncR type="table" tableValues="<?php echo esc_attr ( implode ( ' ' , $ duotone_values ['r ' ] ) ); ?> " />
442
+ <feFuncG type="table" tableValues="<?php echo esc_attr ( implode ( ' ' , $ duotone_values ['g ' ] ) ); ?> " />
443
+ <feFuncB type="table" tableValues="<?php echo esc_attr ( implode ( ' ' , $ duotone_values ['b ' ] ) ); ?> " />
444
+ <feFuncA type="table" tableValues="<?php echo esc_attr ( implode ( ' ' , $ duotone_values ['a ' ] ) ); ?> " />
445
+ </feComponentTransfer>
446
+ <feComposite in2="SourceGraphic" operator="in" />
447
+ </filter>
448
+ </defs>
449
+ </svg>
450
+
451
+ <?php
452
+
453
+ $ svg = ob_get_clean ();
454
+
455
+ if ( ! defined ( 'SCRIPT_DEBUG ' ) || ! SCRIPT_DEBUG ) {
456
+ // Clean up the whitespace.
457
+ $ svg = preg_replace ( "/[ \r\n\t ]+/ " , ' ' , $ svg );
458
+ $ svg = preg_replace ( '/> </ ' , '>< ' , $ svg );
459
+ $ svg = trim ( $ svg );
460
+ }
461
+
462
+ add_action (
463
+ /*
464
+ * Safari doesn't render SVG filters defined in data URIs,
465
+ * and SVG filters won't render in the head of a document,
466
+ * so the next best place to put the SVG is in the footer.
467
+ */
468
+ is_admin () ? 'admin_footer ' : 'wp_footer ' ,
469
+ static function () use ( $ svg ) {
470
+ echo $ svg ;
471
+ }
472
+ );
473
+
474
+ return "url('# " . $ filter_id . "') " ;
475
+ }
316
476
317
477
/**
318
478
* Render out the duotone stylesheet and SVG.
0 commit comments