Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/web_ui/lib/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ abstract class Image {
String toString() => '[$width\u00D7$height]';
}

abstract class ColorFilter {
class ColorFilter implements ImageFilter {
const factory ColorFilter.mode(Color color, BlendMode blendMode) = engine.EngineColorFilter.mode;
const factory ColorFilter.matrix(List<double> matrix) = engine.EngineColorFilter.matrix;
const factory ColorFilter.linearToSrgbGamma() = engine.EngineColorFilter.linearToSrgbGamma;
Expand Down
20 changes: 16 additions & 4 deletions lib/web_ui/lib/src/engine/canvaskit/canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import 'dart:typed_data';

import 'package:ui/ui.dart' as ui;

import '../color_filter.dart';
import 'canvaskit_api.dart';
import 'color_filter.dart';
import 'image.dart';
import 'image_filter.dart';
import 'painting.dart';
Expand Down Expand Up @@ -290,8 +292,13 @@ class CkCanvas {

void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter,
[CkPaint? paint]) {
final CkManagedSkImageFilterConvertible convertible =
filter as CkManagedSkImageFilterConvertible;
final CkManagedSkImageFilterConvertible convertible;
Comment thread
alanwutang11 marked this conversation as resolved.
Outdated
if (filter is ui.ColorFilter) {
convertible = createCkColorFilter(filter as EngineColorFilter)!;
}
else {
Comment thread
alanwutang11 marked this conversation as resolved.
Outdated
convertible = filter as CkManagedSkImageFilterConvertible;
}
return skCanvas.saveLayer(
paint?.skiaObject,
toSkRect(bounds),
Expand Down Expand Up @@ -1163,8 +1170,13 @@ class CkSaveLayerWithFilterCommand extends CkPaintCommand {

@override
void apply(SkCanvas canvas) {
final CkManagedSkImageFilterConvertible convertible =
filter as CkManagedSkImageFilterConvertible;
final CkManagedSkImageFilterConvertible convertible;
if (filter is ui.ColorFilter) {
Comment thread
alanwutang11 marked this conversation as resolved.
Outdated
convertible = createCkColorFilter(filter as EngineColorFilter)!;
}
else {
Comment thread
alanwutang11 marked this conversation as resolved.
Outdated
convertible = filter as CkManagedSkImageFilterConvertible;
}
return canvas.saveLayer(
paint?.skiaObject,
toSkRect(bounds),
Expand Down
29 changes: 28 additions & 1 deletion lib/web_ui/lib/src/engine/canvaskit/color_filter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
/// Additionally, this class provides the interface for converting itself to a
/// [ManagedSkiaObject] that manages a skia image filter.
abstract class CkColorFilter
implements CkManagedSkImageFilterConvertible, EngineColorFilter {
implements CkManagedSkImageFilterConvertible {
const CkColorFilter();

/// Called by [ManagedSkiaObject.createDefault] and
Expand Down Expand Up @@ -227,3 +227,30 @@ class CkComposeColorFilter extends CkColorFilter {
@override
String toString() => 'ColorFilter.compose($outer, $inner)';
}

/// Convert the current [ColorFilter] to a CkColorFilter.
///
/// This workaround allows ColorFilter to be const constructbile and
/// efficiently comparable, so that widgets can check for ColorFilter equality to
/// avoid repainting.
CkColorFilter? createCkColorFilter(EngineColorFilter colorFilter) {
switch (colorFilter.type) {
case ColorFilterType.mode:
if (colorFilter.color == null || colorFilter.blendMode == null) {
return null;
}
return CkBlendModeColorFilter(colorFilter.color!, colorFilter.blendMode!);
case ColorFilterType.matrix:
if (colorFilter.matrix == null) {
return null;
}
assert(colorFilter.matrix!.length == 20, 'Color Matrix must have 20 entries.');
return CkMatrixColorFilter(colorFilter.matrix!);
case ColorFilterType.linearToSrgbGamma:
return const CkLinearToSrgbGammaColorFilter();
case ColorFilterType.srgbToLinearGamma:
return const CkSrgbToLinearGammaColorFilter();
default:
throw StateError('Unknown mode $colorFilter.type for ColorFilter.');
}
}
29 changes: 19 additions & 10 deletions lib/web_ui/lib/src/engine/canvaskit/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:typed_data';

import 'package:ui/ui.dart' as ui;

import '../color_filter.dart';
import 'canvaskit_api.dart';
import 'color_filter.dart';
import 'image_filter.dart';
Expand Down Expand Up @@ -131,7 +132,8 @@ class CkPaint extends ManagedSkiaObject<SkPaint> implements ui.Paint {
_effectiveColorFilter = _invertColorFilter;
} else {
_effectiveColorFilter = ManagedSkColorFilter(
CkComposeColorFilter(_invertColorFilter, _effectiveColorFilter!));
CkComposeColorFilter(_invertColorFilter, _effectiveColorFilter!)
);
}
}
skiaObject.setColorFilter(_effectiveColorFilter?.skiaObject);
Expand Down Expand Up @@ -201,20 +203,23 @@ class CkPaint extends ManagedSkiaObject<SkPaint> implements ui.Paint {
}

ui.FilterQuality _filterQuality = ui.FilterQuality.none;
EngineColorFilter? _engineColorFilter;

@override
ui.ColorFilter? get colorFilter => _effectiveColorFilter?.colorFilter;
ui.ColorFilter? get colorFilter => _engineColorFilter;

@override
set colorFilter(ui.ColorFilter? value) {
if (colorFilter == value) {
if (_engineColorFilter == value) {
return;
}

_engineColorFilter = value as EngineColorFilter?;
_originalColorFilter = null;
if (value == null) {
_effectiveColorFilter = null;
} else {
_effectiveColorFilter = ManagedSkColorFilter(value as CkColorFilter);
final CkColorFilter ckColorFilter = createCkColorFilter(value)!;
_effectiveColorFilter = ManagedSkColorFilter(ckColorFilter);
}

if (invertColors) {
Expand All @@ -223,7 +228,8 @@ class CkPaint extends ManagedSkiaObject<SkPaint> implements ui.Paint {
_effectiveColorFilter = _invertColorFilter;
} else {
_effectiveColorFilter = ManagedSkColorFilter(
CkComposeColorFilter(_invertColorFilter, _effectiveColorFilter!));
CkComposeColorFilter(_invertColorFilter, _effectiveColorFilter!)
);
}
}

Expand Down Expand Up @@ -255,8 +261,12 @@ class CkPaint extends ManagedSkiaObject<SkPaint> implements ui.Paint {
if (_imageFilter == value) {
return;
}

_imageFilter = value as CkManagedSkImageFilterConvertible?;
if (value is ui.ColorFilter) {
_imageFilter = createCkColorFilter(value as EngineColorFilter);
}
else {
_imageFilter = value as CkManagedSkImageFilterConvertible?;
}
_managedImageFilter = _imageFilter?.imageFilter;
skiaObject.setImageFilter(_managedImageFilter?.skiaObject);
}
Expand Down Expand Up @@ -305,8 +315,7 @@ final Float32List _invertColorMatrix = Float32List.fromList(const <double>[
1.0, 1.0, 1.0, 1.0, 0
]);

final ManagedSkColorFilter _invertColorFilter =
ManagedSkColorFilter(CkMatrixColorFilter(_invertColorMatrix));
final ManagedSkColorFilter _invertColorFilter = ManagedSkColorFilter(CkMatrixColorFilter(_invertColorMatrix));

class UniformData {
const UniformData({
Expand Down
37 changes: 28 additions & 9 deletions lib/web_ui/lib/src/engine/color_filter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@

import 'package:ui/ui.dart' as ui;

import 'canvaskit/color_filter.dart';
enum ColorFilterType {
mode,
matrix,
linearToSrgbGamma,
srgbToLinearGamma,
}

/// A description of a color filter to apply when drawing a shape or compositing
/// a layer with a particular [Paint]. A color filter is a function that takes
Expand All @@ -22,8 +27,9 @@ class EngineColorFilter implements ui.ColorFilter {
/// The output of this filter is then composited into the background according
/// to the [Paint.blendMode], using the output of this filter as the source
/// and the background as the destination.
const factory EngineColorFilter.mode(ui.Color color, ui.BlendMode blendMode) =
CkBlendModeColorFilter;
const EngineColorFilter.mode(ui.Color this.color, ui.BlendMode this.blendMode)
: matrix = null,
type = ColorFilterType.mode;

/// Construct a color filter that transforms a color by a 5x5 matrix, where
/// the fifth row is implicitly added in an identity configuration.
Expand Down Expand Up @@ -85,16 +91,29 @@ class EngineColorFilter implements ui.ColorFilter {
/// 0, 0, 0, 1, 0,
/// ]);
/// ```
const factory EngineColorFilter.matrix(List<double> matrix) =
CkMatrixColorFilter;
const EngineColorFilter.matrix(List<double> this.matrix)
: color = null,
blendMode = null,
type = ColorFilterType.matrix;

/// Construct a color filter that applies the sRGB gamma curve to the RGB
/// channels.
const factory EngineColorFilter.linearToSrgbGamma() =
CkLinearToSrgbGammaColorFilter;
const EngineColorFilter.linearToSrgbGamma()
: color = null,
blendMode = null,
matrix = null,
type = ColorFilterType.linearToSrgbGamma;

/// Creates a color filter that applies the inverse of the sRGB gamma curve
/// to the RGB channels.
const factory EngineColorFilter.srgbToLinearGamma() =
CkSrgbToLinearGammaColorFilter;
const EngineColorFilter.srgbToLinearGamma()
: color = null,
blendMode = null,
matrix = null,
type = ColorFilterType.srgbToLinearGamma;

final ui.Color? color;
final ui.BlendMode? blendMode;
final List<double>? matrix;
final ColorFilterType type;
}
31 changes: 28 additions & 3 deletions lib/web_ui/lib/src/engine/html/backdrop_filter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import 'package:ui/ui.dart' as ui;

import '../browser_detection.dart';
import '../color_filter.dart';
import '../dom.dart';
import '../embedder.dart';
import '../util.dart';
import '../vector_math.dart';
import 'shaders/shader.dart';
Expand All @@ -17,7 +19,8 @@ class PersistedBackdropFilter extends PersistedContainerSurface
implements ui.BackdropFilterEngineLayer {
PersistedBackdropFilter(PersistedBackdropFilter? super.oldLayer, this.filter);

final EngineImageFilter filter;
// final EngineImageFilter filter;
Comment thread
alanwutang11 marked this conversation as resolved.
Outdated
final ui.ImageFilter filter;

/// The dedicated child container element that's separate from the
/// [rootElement] is used to host child in front of [filterElement] that
Expand All @@ -26,6 +29,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface
DomElement? get childContainer => _childContainer;
DomElement? _childContainer;
DomElement? _filterElement;
DomElement? _svgFilter;
ui.Rect? _activeClipBounds;
// Cached inverted transform for [transform].
late Matrix4 _invertedTransform;
Expand All @@ -37,6 +41,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface
super.adoptElements(oldSurface);
_childContainer = oldSurface._childContainer;
_filterElement = oldSurface._filterElement;
_svgFilter = oldSurface._svgFilter;
oldSurface._childContainer = null;
}

Expand All @@ -62,12 +67,22 @@ class PersistedBackdropFilter extends PersistedContainerSurface
// Do not detach the child container from the root. It is permanently
// attached. The elements are reused together and are detached from the DOM
// together.
flutterViewEmbedder.removeResource(_svgFilter);
_svgFilter = null;
_childContainer = null;
_filterElement = null;
}

@override
void apply() {
EngineImageFilter backendFilter;
if (filter is ui.ColorFilter) {
backendFilter = createHtmlColorFilter(filter as EngineColorFilter)!;
} else {
backendFilter = filter as EngineImageFilter;
}
flutterViewEmbedder.removeResource(_svgFilter);
_svgFilter = null;
if (_previousTransform != transform) {
_invertedTransform = Matrix4.inverted(transform!);
_previousTransform = transform;
Expand Down Expand Up @@ -115,14 +130,24 @@ class PersistedBackdropFilter extends PersistedContainerSurface
..backgroundColor = '#000'
..opacity = '0.2';
} else {
if (backendFilter is ModeHtmlColorFilter) {
_svgFilter = backendFilter.makeSvgFilter(_filterElement);
/// Some blendModes do not make an svgFilter. See [EngineHtmlColorFilter.makeSvgFilter()]
if (_svgFilter == null) {
return;
}
Comment thread
alanwutang11 marked this conversation as resolved.
Outdated
} else if (backendFilter is MatrixHtmlColorFilter) {
_svgFilter = backendFilter.makeSvgFilter(_filterElement);
}

// 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.
if (browserEngine == BrowserEngine.webkit) {
setElementStyle(_filterElement!, '-webkit-backdrop-filter',
filter.filterAttribute);
backendFilter.filterAttribute);
}
setElementStyle(_filterElement!, 'backdrop-filter', filter.filterAttribute);
setElementStyle(_filterElement!, 'backdrop-filter', backendFilter.filterAttribute);
}
}

Expand Down
10 changes: 4 additions & 6 deletions lib/web_ui/lib/src/engine/html/bitmap_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import 'package:ui/ui.dart' as ui;

import '../browser_detection.dart';
import '../canvas_pool.dart';
import '../canvaskit/color_filter.dart';
import '../color_filter.dart';
import '../dom.dart';
import '../engine_canvas.dart';
import '../frame_reference.dart';
Expand All @@ -28,6 +26,7 @@ import 'path/path.dart';
import 'recording_canvas.dart';
import 'render_vertices.dart';
import 'shaders/image_shader.dart';
import 'shaders/shader.dart';

/// A raw HTML canvas that is directly written to.
class BitmapCanvas extends EngineCanvas {
Expand Down Expand Up @@ -647,13 +646,12 @@ class BitmapCanvas extends EngineCanvas {
ui.Image image, ui.Offset p, SurfacePaintData paint) {
final HtmlImage htmlImage = image as HtmlImage;
final ui.BlendMode? blendMode = paint.blendMode;
final EngineColorFilter? colorFilter =
paint.colorFilter as EngineColorFilter?;
final EngineHtmlColorFilter? colorFilter = createHtmlColorFilter(paint.colorFilter);
DomHTMLElement imgElement;
if (colorFilter is CkBlendModeColorFilter) {
if (colorFilter is ModeHtmlColorFilter) {
imgElement = _createImageElementWithBlend(
image, colorFilter.color, colorFilter.blendMode, paint);
} else if (colorFilter is CkMatrixColorFilter) {
} else if (colorFilter is MatrixHtmlColorFilter) {
imgElement = _createImageElementWithSvgColorMatrixFilter(
image, colorFilter.matrix, paint);
} else {
Expand Down
Loading