未验证 提交 41a92052 编写于 作者: F Ferhat 提交者: GitHub

Implement ColorFilter.matrix for html renderer (#25769)

上级 1019008f
repository: https://github.com/flutter/goldens.git repository: https://github.com/flutter/goldens.git
revision: 8d866a84b7ceb67412ce6767ea0c1eec2eb3fa3f revision: 4a8da9f65353bda26a73e12838797f20cfdedc40
...@@ -631,29 +631,11 @@ class BitmapCanvas extends EngineCanvas { ...@@ -631,29 +631,11 @@ class BitmapCanvas extends EngineCanvas {
paint.colorFilter as EngineColorFilter?; paint.colorFilter as EngineColorFilter?;
html.HtmlElement imgElement; html.HtmlElement imgElement;
if (colorFilter is _CkBlendModeColorFilter) { if (colorFilter is _CkBlendModeColorFilter) {
switch (colorFilter.blendMode) { imgElement = _createImageElementWithBlend(image,
case ui.BlendMode.colorBurn: colorFilter.color, colorFilter.blendMode, paint);
case ui.BlendMode.colorDodge: } else if (colorFilter is _CkMatrixColorFilter) {
case ui.BlendMode.hue: imgElement = _createImageElementWithSvgColorMatrixFilter(
case ui.BlendMode.modulate: image, colorFilter.matrix , paint);
case ui.BlendMode.overlay:
case ui.BlendMode.plus:
case ui.BlendMode.srcIn:
case ui.BlendMode.srcATop:
case ui.BlendMode.srcOut:
case ui.BlendMode.saturation:
case ui.BlendMode.color:
case ui.BlendMode.luminosity:
case ui.BlendMode.xor:
case ui.BlendMode.dstATop:
imgElement = _createImageElementWithSvgFilter(
image, colorFilter.color, colorFilter.blendMode, paint);
break;
default:
imgElement = _createBackgroundImageWithBlend(
image, colorFilter.color, colorFilter.blendMode, paint);
break;
}
} else { } else {
// No Blending, create an image by cloning original loaded image. // No Blending, create an image by cloning original loaded image.
imgElement = _reuseOrCreateImage(htmlImage); imgElement = _reuseOrCreateImage(htmlImage);
...@@ -683,6 +665,31 @@ class BitmapCanvas extends EngineCanvas { ...@@ -683,6 +665,31 @@ class BitmapCanvas extends EngineCanvas {
return imgElement; return imgElement;
} }
html.HtmlElement _createImageElementWithBlend(HtmlImage image,
ui.Color color, ui.BlendMode blendMode, SurfacePaintData paint) {
switch (blendMode) {
case ui.BlendMode.colorBurn:
case ui.BlendMode.colorDodge:
case ui.BlendMode.hue:
case ui.BlendMode.modulate:
case ui.BlendMode.overlay:
case ui.BlendMode.plus:
case ui.BlendMode.srcIn:
case ui.BlendMode.srcATop:
case ui.BlendMode.srcOut:
case ui.BlendMode.saturation:
case ui.BlendMode.color:
case ui.BlendMode.luminosity:
case ui.BlendMode.xor:
case ui.BlendMode.dstATop:
return _createImageElementWithSvgBlendFilter(
image, color, blendMode, paint);
default:
return _createBackgroundImageWithBlend(
image, color, blendMode, paint);
}
}
@override @override
void drawImageRect( void drawImageRect(
ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) {
...@@ -811,7 +818,7 @@ class BitmapCanvas extends EngineCanvas { ...@@ -811,7 +818,7 @@ class BitmapCanvas extends EngineCanvas {
} }
// Creates an image element and an svg filter to apply on the element. // Creates an image element and an svg filter to apply on the element.
html.HtmlElement _createImageElementWithSvgFilter( html.HtmlElement _createImageElementWithSvgBlendFilter(
HtmlImage image, HtmlImage image,
ui.Color? filterColor, ui.Color? filterColor,
ui.BlendMode colorFilterBlendMode, ui.BlendMode colorFilterBlendMode,
...@@ -831,6 +838,22 @@ class BitmapCanvas extends EngineCanvas { ...@@ -831,6 +838,22 @@ class BitmapCanvas extends EngineCanvas {
return imgElement; return imgElement;
} }
// Creates an image element and an svg color matrix filter to apply on the element.
html.HtmlElement _createImageElementWithSvgColorMatrixFilter(
HtmlImage image,
List<double> matrix,
SurfacePaintData paint) {
// For srcIn blendMode, we use an svg filter to apply to image element.
String? svgFilter = svgFilterFromColorMatrix(matrix);
final html.Element filterElement =
html.Element.html(svgFilter, treeSanitizer: _NullTreeSanitizer());
rootElement.append(filterElement);
_children.add(filterElement);
final html.HtmlElement imgElement = _reuseOrCreateImage(image);
imgElement.style.filter = 'url(#_fcf${_filterIdCounter})';
return imgElement;
}
// Should be called when we add new html elements into rootElement so that // Should be called when we add new html elements into rootElement so that
// paint order is preserved. // paint order is preserved.
// //
......
...@@ -59,14 +59,18 @@ class PersistedColorFilter extends PersistedContainerSurface ...@@ -59,14 +59,18 @@ class PersistedColorFilter extends PersistedContainerSurface
childContainer?.style.visibility = 'visible'; childContainer?.style.visibility = 'visible';
return; return;
} }
if (engineValue is _CkBlendModeColorFilter) {
if (engineValue is! _CkBlendModeColorFilter) { _applyBlendModeFilter(engineValue);
} else if (engineValue is _CkMatrixColorFilter) {
_applyMatrixColorFilter(engineValue);
} else {
childContainer?.style.visibility = 'visible'; childContainer?.style.visibility = 'visible';
return;
} }
}
ui.Color filterColor = engineValue.color; void _applyBlendModeFilter(_CkBlendModeColorFilter colorFilter) {
ui.BlendMode colorFilterBlendMode = engineValue.blendMode; ui.Color filterColor = colorFilter.color;
ui.BlendMode colorFilterBlendMode = colorFilter.blendMode;
html.CssStyleDeclaration style = rootElement!.style; html.CssStyleDeclaration style = rootElement!.style;
switch (colorFilterBlendMode) { switch (colorFilterBlendMode) {
case ui.BlendMode.clear: case ui.BlendMode.clear:
...@@ -123,7 +127,16 @@ class PersistedColorFilter extends PersistedContainerSurface ...@@ -123,7 +127,16 @@ class PersistedColorFilter extends PersistedContainerSurface
colorFilterBlendMode == ui.BlendMode.modulate) { colorFilterBlendMode == ui.BlendMode.modulate) {
style.backgroundColor = colorToCssString(filterColor); style.backgroundColor = colorToCssString(filterColor);
} }
return; }
}
void _applyMatrixColorFilter(_CkMatrixColorFilter colorFilter) {
String? svgFilter = svgFilterFromColorMatrix(colorFilter.matrix);
if (svgFilter != null) {
_filterElement =
html.Element.html(svgFilter, treeSanitizer: _NullTreeSanitizer());
rootElement!.append(_filterElement!);
rootElement!.style.filter = 'url(#_fcf${_filterIdCounter})';
} }
} }
...@@ -215,6 +228,23 @@ String? svgFilterFromBlendMode( ...@@ -215,6 +228,23 @@ String? svgFilterFromBlendMode(
return svgFilter; return svgFilter;
} }
String? svgFilterFromColorMatrix(List<double> matrix) {
_filterIdCounter += 1;
final StringBuffer sbMatrix = StringBuffer();
assert(matrix.length == 20);
for (int i = 0; i < 20; i++) {
if (i != 0) {
sbMatrix.write(' ');
}
sbMatrix.write(matrix[i]);
}
return '$kSvgResourceHeader'
'<filter id="_fcf$_filterIdCounter" '
'filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">'
'<feColorMatrix values="$sbMatrix" result="comp"/>'
'</filter></svg>';
}
int _filterIdCounter = 0; int _filterIdCounter = 0;
// The color matrix for feColorMatrix element changes colors based on // The color matrix for feColorMatrix element changes colors based on
......
...@@ -24,11 +24,27 @@ void testMain() async { ...@@ -24,11 +24,27 @@ void testMain() async {
// Regression test for https://github.com/flutter/flutter/issues/76966 // Regression test for https://github.com/flutter/flutter/issues/76966
test('Draws image with dstATop color filter', () async { test('Draws image with dstATop color filter', () async {
final RecordingCanvas canvas = RecordingCanvas(region); final RecordingCanvas canvas = RecordingCanvas(region);
canvas.drawImage(createFlutterLogoTestImage(), Offset(10, 10), canvas.drawImage(
makePaint() createFlutterLogoTestImage(),
..colorFilter = EngineColorFilter.mode(Color(0x40000000), Offset(10, 10),
BlendMode.dstATop)); makePaint()
await canvasScreenshot(canvas, 'image_color_fiter_dstatop', ..colorFilter =
region: region); EngineColorFilter.mode(Color(0x40000000), BlendMode.dstATop));
await canvasScreenshot(canvas, 'image_color_fiter_dstatop', region: region);
});
test('Draws image with matrix color filter', () async {
final RecordingCanvas canvas = RecordingCanvas(region);
canvas.drawImage(
createFlutterLogoTestImage(),
Offset(10, 10),
makePaint()
..colorFilter = EngineColorFilter.matrix(<double>[
0.2126, 0.7152, 0.0722, 0, 0, //
0.2126, 0.7152, 0.0722, 0, 0, //
0.2126, 0.7152, 0.0722, 0, 0, //
0, 0, 0, 1, 0, //
]));
await canvasScreenshot(canvas, 'image_matrix_color_fiter', region: region);
}); });
} }
...@@ -48,6 +48,26 @@ void testMain() async { ...@@ -48,6 +48,26 @@ void testMain() async {
maxDiffRatePercent: 12.0); maxDiffRatePercent: 12.0);
}); });
test('Should apply matrix color filter to image', () async {
final List<double> colorMatrix = <double>[
0.2126, 0.7152, 0.0722, 0, 0, //
0.2126, 0.7152, 0.0722, 0, 0, //
0.2126, 0.7152, 0.0722, 0, 0, //
0, 0, 0, 1, 0, //
];
final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
final Picture backgroundPicture = _drawBackground();
builder.addPicture(Offset.zero, backgroundPicture);
builder.pushColorFilter(
EngineColorFilter.matrix(colorMatrix));
final Picture circles1 = _drawTestPictureWithCircles(30, 30);
builder.addPicture(Offset.zero, circles1);
builder.pop();
html.document.body!.append(builder.build().webOnlyRootElement!);
await matchGoldenFile('color_filter_matrix.png', region: region,
maxDiffRatePercent: 12.0);
});
/// Regression test for https://github.com/flutter/flutter/issues/59451. /// Regression test for https://github.com/flutter/flutter/issues/59451.
/// ///
/// Picture with overlay blend inside a physical shape. Should show image /// Picture with overlay blend inside a physical shape. Should show image
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册