Skip to content

Commit

Permalink
Enable transparency for duotone (#34130)
Browse files Browse the repository at this point in the history
* Enable transparency for duotone
  • Loading branch information
ajlende authored Oct 29, 2021
1 parent 26ee93c commit 56b95cd
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 19 deletions.
92 changes: 81 additions & 11 deletions lib/block-supports/duotone.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,25 @@ function gutenberg_tinycolor_bound01( $n, $max ) {
return ( $n % $max ) / (float) $max;
}

/**
* Direct port of tinycolor's boundAlpha function to maintain consistency with
* how tinycolor works.
*
* @see https://github.com/bgrins/TinyColor
*
* @param mixed $n Number of unknown type.
* @return float Value in the range [0,1].
*/
function gutenberg_tinycolor_bound_alpha( $n ) {
if ( is_numeric( $n ) ) {
$n = (float) $n;
if ( $n >= 0 && $n <= 1 ) {
return $n;
}
}
return 1;
}

/**
* Round and convert values of an RGB object.
*
Expand Down Expand Up @@ -116,8 +135,7 @@ function gutenberg_tinycolor_hsl_to_rgb( $hsl_color ) {

/**
* Parses hex, hsl, and rgb CSS strings using the same regex as tinycolor v1.4.2
* used in the JavaScript. Only colors output from react-color are implemented
* and the alpha value is ignored as it is not used in duotone.
* used in the JavaScript. Only colors output from react-color are implemented.
*
* @see https://github.com/bgrins/TinyColor
* @see https://github.com/casesandberg/react-color/
Expand All @@ -138,90 +156,137 @@ function gutenberg_tinycolor_string_to_rgb( $color_str ) {

$rgb_regexp = '/^rgb' . $permissive_match3 . '$/';
if ( preg_match( $rgb_regexp, $color_str, $match ) ) {
return gutenberg_tinycolor_rgb_to_rgb(
$rgb = gutenberg_tinycolor_rgb_to_rgb(
array(
'r' => $match[1],
'g' => $match[2],
'b' => $match[3],
)
);

$rgb['a'] = 1;

return $rgb;
}

$rgba_regexp = '/^rgba' . $permissive_match4 . '$/';
if ( preg_match( $rgba_regexp, $color_str, $match ) ) {
return gutenberg_tinycolor_rgb_to_rgb(
$rgb = gutenberg_tinycolor_rgb_to_rgb(
array(
'r' => $match[1],
'g' => $match[2],
'b' => $match[3],
)
);

$rgb['a'] = gutenberg_tinycolor_bound_alpha( $match[4] );

return $rgb;
}

$hsl_regexp = '/^hsl' . $permissive_match3 . '$/';
if ( preg_match( $hsl_regexp, $color_str, $match ) ) {
return gutenberg_tinycolor_hsl_to_rgb(
$rgb = gutenberg_tinycolor_hsl_to_rgb(
array(
'h' => $match[1],
's' => $match[2],
'l' => $match[3],
)
);

$rgb['a'] = 1;

return $rgb;
}

$hsla_regexp = '/^hsla' . $permissive_match4 . '$/';
if ( preg_match( $hsla_regexp, $color_str, $match ) ) {
return gutenberg_tinycolor_hsl_to_rgb(
$rgb = gutenberg_tinycolor_hsl_to_rgb(
array(
'h' => $match[1],
's' => $match[2],
'l' => $match[3],
)
);

$rgb['a'] = gutenberg_tinycolor_bound_alpha( $match[4] );

return $rgb;
}

$hex8_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/';
if ( preg_match( $hex8_regexp, $color_str, $match ) ) {
return gutenberg_tinycolor_rgb_to_rgb(
$rgb = gutenberg_tinycolor_rgb_to_rgb(
array(
'r' => base_convert( $match[1], 16, 10 ),
'g' => base_convert( $match[2], 16, 10 ),
'b' => base_convert( $match[3], 16, 10 ),
)
);

$rgb['a'] = gutenberg_tinycolor_bound_alpha(
base_convert( $match[4], 16, 10 ) / 255
);

return $rgb;
}

$hex6_regexp = '/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/';
if ( preg_match( $hex6_regexp, $color_str, $match ) ) {
return gutenberg_tinycolor_rgb_to_rgb(
$rgb = gutenberg_tinycolor_rgb_to_rgb(
array(
'r' => base_convert( $match[1], 16, 10 ),
'g' => base_convert( $match[2], 16, 10 ),
'b' => base_convert( $match[3], 16, 10 ),
)
);

$rgb['a'] = 1;

return $rgb;
}

$hex4_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/';
if ( preg_match( $hex4_regexp, $color_str, $match ) ) {
return gutenberg_tinycolor_rgb_to_rgb(
$rgb = gutenberg_tinycolor_rgb_to_rgb(
array(
'r' => base_convert( $match[1] . $match[1], 16, 10 ),
'g' => base_convert( $match[2] . $match[2], 16, 10 ),
'b' => base_convert( $match[3] . $match[3], 16, 10 ),
)
);

$rgb['a'] = gutenberg_tinycolor_bound_alpha(
base_convert( $match[4] . $match[4], 16, 10 ) / 255
);

return $rgb;
}

$hex3_regexp = '/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/';
if ( preg_match( $hex3_regexp, $color_str, $match ) ) {
return gutenberg_tinycolor_rgb_to_rgb(
$rgb = gutenberg_tinycolor_rgb_to_rgb(
array(
'r' => base_convert( $match[1] . $match[1], 16, 10 ),
'g' => base_convert( $match[2] . $match[2], 16, 10 ),
'b' => base_convert( $match[3] . $match[3], 16, 10 ),
)
);

$rgb['a'] = 1;

return $rgb;
}

// The JS color picker considers the string "transparent" to be a hex value,
// so we need to handle it here as a special case.
if ( 'transparent' === $color_str ) {
return array(
'r' => 0,
'g' => 0,
'b' => 0,
'a' => 0,
);
}
}

Expand Down Expand Up @@ -266,13 +331,15 @@ function gutenberg_render_duotone_filter_preset( $preset ) {
'r' => array(),
'g' => array(),
'b' => array(),
'a' => array(),
);
foreach ( $duotone_colors as $color_str ) {
$color = gutenberg_tinycolor_string_to_rgb( $color_str );

$duotone_values['r'][] = $color['r'] / 255;
$duotone_values['g'][] = $color['g'] / 255;
$duotone_values['b'][] = $color['b'] / 255;
$duotone_values['a'][] = $color['a'];
}

ob_start();
Expand All @@ -291,19 +358,22 @@ function gutenberg_render_duotone_filter_preset( $preset ) {
<defs>
<filter id="<?php echo esc_attr( $filter_id ); ?>">
<feColorMatrix
color-interpolation-filters="sRGB"
type="matrix"
values="
.299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
0 0 0 1 0
.299 .587 .114 0 0
"
/>
<feComponentTransfer color-interpolation-filters="sRGB" >
<feFuncR type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['r'] ) ); ?>" />
<feFuncG type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['g'] ) ); ?>" />
<feFuncB type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['b'] ) ); ?>" />
<feFuncA type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['a'] ) ); ?>" />
</feComponentTransfer>
<feComposite in2="SourceGraphic" operator="in" />
</filter>
</defs>
</svg>
Expand Down
28 changes: 21 additions & 7 deletions packages/block-editor/src/hooks/duotone.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ extend( [ namesPlugin ] );
* @return {Object} R, G, and B values.
*/
export function getValuesFromColors( colors = [] ) {
const values = { r: [], g: [], b: [] };
const values = { r: [], g: [], b: [], a: [] };

colors.forEach( ( color ) => {
const rgbColor = colord( color ).toRgb();
values.r.push( rgbColor.r / 255 );
values.g.push( rgbColor.g / 255 );
values.b.push( rgbColor.b / 255 );
values.a.push( rgbColor.a );
} );

return values;
Expand All @@ -55,6 +56,7 @@ export function getValuesFromColors( colors = [] ) {
* @property {number[]} r Red values.
* @property {number[]} g Green values.
* @property {number[]} b Blue values.
* @property {number[]} a Alpha values.
*/

/**
Expand All @@ -63,7 +65,7 @@ export function getValuesFromColors( colors = [] ) {
* @param {Object} props Duotone props.
* @param {string} props.selector Selector to apply the filter to.
* @param {string} props.id Unique id for this duotone filter.
* @param {Values} props.values R, G, and B values to filter with.
* @param {Values} props.values R, G, B, and A values to filter with.
*
* @return {WPElement} Duotone element.
*/
Expand Down Expand Up @@ -93,13 +95,16 @@ ${ selector } {
<defs>
<filter id={ id }>
<feColorMatrix
// Use sRGB instead of linearRGB so transparency looks correct.
colorInterpolationFilters="sRGB"
type="matrix"
// Use perceptual brightness to convert to grayscale.
// prettier-ignore
values=".299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
0 0 0 1 0"
values="
.299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
"
/>
<feComponentTransfer
// Use sRGB instead of linearRGB to be consistent with how CSS gradients work.
Expand All @@ -117,7 +122,16 @@ ${ selector } {
type="table"
tableValues={ values.b.join( ' ' ) }
/>
<feFuncA
type="table"
tableValues={ values.a.join( ' ' ) }
/>
</feComponentTransfer>
<feComposite
// Re-mask the image with the original transparency since the feColorMatrix above loses that information.
in2="SourceGraphic"
operator="in"
/>
</filter>
</defs>
</SVG>
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/color-list-picker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function ColorOption( {
value,
colors,
disableCustomColors,
enableAlpha,
onChange,
} ) {
const [ isOpen, setIsOpen ] = useState( false );
Expand All @@ -35,6 +36,7 @@ function ColorOption( {
clearable={ false }
onChange={ onChange }
disableCustomColors={ disableCustomColors }
enableAlpha={ enableAlpha }
/>
) }
</>
Expand All @@ -46,6 +48,7 @@ function ColorListPicker( {
labels,
value = [],
disableCustomColors,
enableAlpha,
onChange,
} ) {
return (
Expand All @@ -57,6 +60,7 @@ function ColorListPicker( {
value={ value[ index ] }
colors={ colors }
disableCustomColors={ disableCustomColors }
enableAlpha={ enableAlpha }
onChange={ ( newColor ) => {
const newColors = value.slice();
newColors[ index ] = newColor;
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/color-palette/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default function ColorPalette( {
className,
colors,
disableCustomColors = false,
enableAlpha,
onChange,
value,
} ) {
Expand Down Expand Up @@ -73,6 +74,7 @@ export default function ColorPalette( {
<ColorPicker
color={ value }
onChange={ ( color ) => onChange( color ) }
enableAlpha={ enableAlpha }
/>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export default function CustomDuotoneBar( { value, onChange } ) {
return (
<CustomGradientBar
disableInserter
disableAlpha
background={ background }
hasGradient={ hasGradient }
value={ controlPoints }
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/duotone-picker/duotone-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ function DuotonePicker( {
colors={ colorPalette }
value={ value }
disableCustomColors={ disableCustomColors }
enableAlpha
onChange={ ( newColors ) => {
if ( ! newColors[ 0 ] ) {
newColors[ 0 ] = defaultDark;
Expand Down

0 comments on commit 56b95cd

Please sign in to comment.