Skip to content

Commit

Permalink
[web] implement pushImageFilter (#14599)
Browse files Browse the repository at this point in the history
implement pushImageFilter in the HTML and CanvasKit backends
  • Loading branch information
yjbanov committed Dec 20, 2019
1 parent 9b299f2 commit 3381392
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 32 deletions.
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Expand Up @@ -452,6 +452,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/shadow.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/surface/backdrop_filter.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/surface/clip.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/surface/debug_canvas_reuse_overlay.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/surface/image_filter.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/surface/offset.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/surface/opacity.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/surface/painting.dart
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/dev/goldens_lock.yaml
@@ -1,2 +1,2 @@
repository: https://github.com/flutter/goldens.git
revision: ab1f2da642d6c5188b312965759ce7157fd073b9
revision: a121ff1169a1b478274a3e34c95d0a1d2d685948
1 change: 1 addition & 0 deletions lib/web_ui/lib/src/engine.dart
Expand Up @@ -88,6 +88,7 @@ part 'engine/shadow.dart';
part 'engine/surface/backdrop_filter.dart';
part 'engine/surface/clip.dart';
part 'engine/surface/debug_canvas_reuse_overlay.dart';
part 'engine/surface/image_filter.dart';
part 'engine/surface/offset.dart';
part 'engine/surface/opacity.dart';
part 'engine/surface/painting.dart';
Expand Down
17 changes: 17 additions & 0 deletions lib/web_ui/lib/src/engine/compositor/layer.dart
Expand Up @@ -343,6 +343,23 @@ class TransformLayer extends ContainerLayer
}
}

/// A layer that applies an [ui.ImageFilter] to its children.
class ImageFilterLayer extends ContainerLayer implements ui.OpacityEngineLayer {
ImageFilterLayer(this._filter);

final ui.ImageFilter _filter;

@override
void paint(PaintContext paintContext) {
assert(needsPainting);
final ui.Paint paint = ui.Paint();
paint.imageFilter = _filter;
paintContext.internalNodesCanvas.saveLayer(paintBounds, paint);
paintChildren(paintContext);
paintContext.internalNodesCanvas.restore();
}
}

/// A layer containing a [Picture].
class PictureLayer extends Layer {
/// The picture to paint into the canvas.
Expand Down
Expand Up @@ -147,7 +147,8 @@ class LayerSceneBuilder implements ui.SceneBuilder {
ui.ImageFilterEngineLayer oldLayer,
}) {
assert(filter != null);
throw UnimplementedError();
pushLayer(ImageFilterLayer(filter));
return null;
}

@override
Expand Down
10 changes: 2 additions & 8 deletions lib/web_ui/lib/src/engine/surface/backdrop_filter.dart
Expand Up @@ -10,7 +10,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface
PersistedBackdropFilter(PersistedBackdropFilter oldLayer, this.filter)
: super(oldLayer);

final ui.ImageFilter filter;
final EngineImageFilter filter;

/// The dedicated child container element that's separate from the
/// [rootElement] is used to host child in front of [filterElement] that
Expand All @@ -24,10 +24,6 @@ class PersistedBackdropFilter extends PersistedContainerSurface
// Reference to transform last used to cache [_invertedTransform].
Matrix4 _previousTransform;

@override
Matrix4 get localTransformInverse =>
_localTransformInverse ??= Matrix4.identity();

@override
void adoptElements(PersistedBackdropFilter oldSurface) {
super.adoptElements(oldSurface);
Expand Down Expand Up @@ -85,12 +81,10 @@ class PersistedBackdropFilter extends PersistedContainerSurface
..backgroundColor = '#000'
..opacity = '0.2';
} else {
final EngineImageFilter engineFilter = filter;
// CSS uses pixel radius for blur. Flutter & SVG use sigma parameters. For
// Gaussian blur with standard deviation (normal distribution),
// the blur will fall within 2 * sigma pixels.
domRenderer.setElementStyle(_filterElement, 'backdrop-filter',
'blur(${math.max(engineFilter.sigmaX, engineFilter.sigmaY) * 2}px)');
domRenderer.setElementStyle(_filterElement, 'backdrop-filter', _imageFilterToCss(filter));
}
}

Expand Down
16 changes: 0 additions & 16 deletions lib/web_ui/lib/src/engine/surface/clip.dart
Expand Up @@ -71,10 +71,6 @@ class PersistedClipRect extends PersistedContainerSurface
_projectedClip = null;
}

@override
Matrix4 get localTransformInverse =>
_localTransformInverse ??= Matrix4.identity();

@override
html.Element createElement() {
return super.createElement()..setAttribute('clip-type', 'rect');
Expand Down Expand Up @@ -122,10 +118,6 @@ class PersistedClipRRect extends PersistedContainerSurface
_projectedClip = null;
}

@override
Matrix4 get localTransformInverse =>
_localTransformInverse ??= Matrix4.identity();

@override
html.Element createElement() {
return super.createElement()..setAttribute('clip-type', 'rrect');
Expand Down Expand Up @@ -193,10 +185,6 @@ class PersistedPhysicalShape extends PersistedContainerSurface
_projectedClip = null;
}

@override
Matrix4 get localTransformInverse =>
_localTransformInverse ??= Matrix4.identity();

void _applyColor() {
rootElement.style.backgroundColor = color.toCssString();
}
Expand Down Expand Up @@ -350,10 +338,6 @@ class PersistedClipPath extends PersistedContainerSurface
_localClipBounds ??= clipPath.getBounds();
}

@override
Matrix4 get localTransformInverse =>
_localTransformInverse ??= Matrix4.identity();

@override
void apply() {
if (clipPath == null) {
Expand Down
32 changes: 32 additions & 0 deletions lib/web_ui/lib/src/engine/surface/image_filter.dart
@@ -0,0 +1,32 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

part of engine;

/// A surface that applies an [imageFilter] to its children.
class PersistedImageFilter extends PersistedContainerSurface
implements ui.ImageFilterEngineLayer {
PersistedImageFilter(PersistedImageFilter oldLayer, this.filter) : super(oldLayer);

final ui.ImageFilter filter;

@override
html.Element createElement() {
return defaultCreateElement('flt-image-filter');
}

@override
void apply() {
rootElement.style.filter = _imageFilterToCss(filter);
}

@override
void update(PersistedImageFilter oldSurface) {
super.update(oldSurface);

if (oldSurface.filter != filter) {
apply();
}
}
}
4 changes: 0 additions & 4 deletions lib/web_ui/lib/src/engine/surface/picture.dart
Expand Up @@ -100,10 +100,6 @@ class PersistedHoudiniPicture extends PersistedPicture {
return existingSurface.picture == picture ? 0.0 : 1.0;
}

@override
Matrix4 get localTransformInverse =>
_localTransformInverse ??= Matrix4.identity();

static void _registerCssPainter() {
_cssPainterRegistered = true;
final dynamic css = js_util.getProperty(html.window, 'CSS');
Expand Down
9 changes: 8 additions & 1 deletion lib/web_ui/lib/src/engine/surface/scene_builder.dart
Expand Up @@ -189,7 +189,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
ui.ImageFilterEngineLayer oldLayer,
}) {
assert(filter != null);
throw UnimplementedError();
return _pushSurface(PersistedImageFilter(oldLayer, filter));
}

/// Pushes a backdrop filter operation onto the operation stack.
Expand Down Expand Up @@ -537,3 +537,10 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
throw UnimplementedError();
}
}

// TODO(yjbanov): in HTML the blur looks too aggressive. The current
// implementation was copied from the existing backdrop-filter
// but probably needs a revision.
String _imageFilterToCss(EngineImageFilter filter) {
return 'blur(${math.max(filter.sigmaX, filter.sigmaY) * 2}px)';
}
7 changes: 6 additions & 1 deletion lib/web_ui/lib/src/engine/surface/surface.dart
Expand Up @@ -804,7 +804,12 @@ abstract class PersistedSurface implements ui.EngineLayer {
// Matrix only contains local transform (not chain multiplied since root).
Matrix4 _localTransformInverse;

Matrix4 get localTransformInverse;
/// The inverse of the local transform that this surface applies to its children.
///
/// The default implementation is identity transform. Concrete
/// implementations may override this getter to supply a different transform.
Matrix4 get localTransformInverse =>
_localTransformInverse ??= Matrix4.identity();

/// Recomputes [transform] and [globalClip] fields.
///
Expand Down
13 changes: 13 additions & 0 deletions lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart
Expand Up @@ -98,6 +98,19 @@ void main() async {
await matchGoldenFile('compositing_shifted_physical_shape_clip.png', region: region);
}, timeout: const Timeout(Duration(seconds: 10)));

test('pushImageFilter', () async {
final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
builder.pushImageFilter(
ImageFilter.blur(sigmaX: 1, sigmaY: 3),
);
_drawTestPicture(builder);
builder.pop();

html.document.body.append(builder.build().webOnlyRootElement);

await matchGoldenFile('compositing_image_filter.png', region: region);
}, timeout: const Timeout(Duration(seconds: 10)));

group('Cull rect computation', () {
_testCullRectComputation();
});
Expand Down

0 comments on commit 3381392

Please sign in to comment.