Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web:canvaskit] migrate Paint API to UniqueRef #41230

Merged
merged 2 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 22 additions & 18 deletions lib/web_ui/lib/src/engine/canvaskit/canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -291,18 +291,20 @@ class CkCanvas {

void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter,
[CkPaint? paint]) {
final CkManagedSkImageFilterConvertible convertible;
if (filter is ui.ColorFilter) {
convertible = createCkColorFilter(filter as EngineColorFilter)!;
} else {
convertible = filter as CkManagedSkImageFilterConvertible;
}
return skCanvas.saveLayer(
paint?.skiaObject,
toSkRect(bounds),
convertible.imageFilter.skiaObject,
0,
);
final CkManagedSkImageFilterConvertible convertible;
if (filter is ui.ColorFilter) {
convertible = createCkColorFilter(filter as EngineColorFilter)!;
} else {
convertible = filter as CkManagedSkImageFilterConvertible;
}
convertible.imageFilter((SkImageFilter filter) {
skCanvas.saveLayer(
paint?.skiaObject,
toSkRect(bounds),
filter,
0,
);
});
}

void scale(double sx, double sy) {
Expand Down Expand Up @@ -1173,11 +1175,13 @@ class CkSaveLayerWithFilterCommand extends CkPaintCommand {
} else {
convertible = filter as CkManagedSkImageFilterConvertible;
}
return canvas.saveLayer(
paint?.skiaObject,
toSkRect(bounds),
convertible.imageFilter.skiaObject,
0,
);
convertible.imageFilter((SkImageFilter filter) {
canvas.saveLayer(
paint?.skiaObject,
toSkRect(bounds),
filter,
0,
);
});
}
}
127 changes: 70 additions & 57 deletions lib/web_ui/lib/src/engine/canvaskit/color_filter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,25 @@ import '../color_filter.dart';
import '../util.dart';
import 'canvaskit_api.dart';
import 'image_filter.dart';
import 'skia_object_cache.dart';
import 'native_memory.dart';

/// A concrete [ManagedSkiaObject] subclass that owns a [SkColorFilter] and
/// manages its lifecycle.
/// Owns a [SkColorFilter] and manages its lifecycle.
///
/// Seealso:
/// See also:
///
/// * [CkPaint.colorFilter], which uses a [ManagedSkColorFilter] to manage
/// the lifecycle of its [SkColorFilter].
class ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
class ManagedSkColorFilter {
ManagedSkColorFilter(CkColorFilter ckColorFilter)
: colorFilter = ckColorFilter;
: colorFilter = ckColorFilter {
_ref = UniqueRef<SkColorFilter>(this, colorFilter._initRawColorFilter(), 'ColorFilter');
}

final CkColorFilter colorFilter;

@override
SkColorFilter createDefault() => colorFilter._initRawColorFilter();

@override
SkColorFilter resurrect() => colorFilter._initRawColorFilter();
late final UniqueRef<SkColorFilter> _ref;

@override
void delete() {
rawSkiaObject?.delete();
}
SkColorFilter get skiaObject => _ref.nativeObject;

@override
int get hashCode => colorFilter.hashCode;
Expand All @@ -51,64 +45,83 @@ class ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
String toString() => colorFilter.toString();
}

/// A [ui.ColorFilter] backed by Skia's [SkColorFilter].
///
/// Additionally, this class provides the interface for converting itself to a
/// [ManagedSkiaObject] that manages a skia image filter.
abstract class CkColorFilter
implements CkManagedSkImageFilterConvertible {
/// CanvasKit implementation of [ui.ColorFilter].
abstract class CkColorFilter implements CkManagedSkImageFilterConvertible {
const CkColorFilter();

/// Called by [ManagedSkiaObject.createDefault] and
/// [ManagedSkiaObject.resurrect] to create a new [SkImageFilter], when this
/// filter is used as an [ImageFilter].
SkImageFilter initRawImageFilter() =>
canvasKit.ImageFilter.MakeColorFilter(_initRawColorFilter(), null);
/// Converts this color filter into an image filter.
///
/// Passes the ownership of the returned [SkImageFilter] to the caller. It is
/// the caller's responsibility to manage the lifecycle of the returned value.
SkImageFilter initRawImageFilter() {
final SkColorFilter skColorFilter = _initRawColorFilter();
final SkImageFilter result = canvasKit.ImageFilter.MakeColorFilter(skColorFilter, null);

// The underlying SkColorFilter is now owned by the SkImageFilter, so we
// need to drop the reference to allow it to be collected.
skColorFilter.delete();
return result;
}

/// Called by [ManagedSkiaObject.createDefault] and
/// [ManagedSkiaObject.resurrect] to create a new [SkColorFilter], when this
/// filter is used as a [ColorFilter].
/// Creates a Skia object based on the properties of this color filter.
///
/// Passes the ownership of the returned [SkColorFilter] to the caller. It is
/// the caller's responsibility to manage the lifecycle of the returned value.
SkColorFilter _initRawColorFilter();

@override
ManagedSkiaObject<SkImageFilter> get imageFilter =>
CkColorFilterImageFilter(colorFilter: this);
void imageFilter(SkImageFilterBorrow borrow) {
// Since ColorFilter has a const constructor it cannot store dynamically
// created Skia objects. Therefore a new SkImageFilter is created every time
// it's used. However, once used it's no longer needed, so it's deleted
// immediately to free memory.
final SkImageFilter skImageFilter = initRawImageFilter();
borrow(skImageFilter);
skImageFilter.delete();
}
}

/// A reusable identity transform matrix.
///
/// WARNING: DO NOT MUTATE THIS MATRIX! It is a shared global singleton.
Float32List _identityTransform = _computeIdentityTransform();

Float32List _computeIdentityTransform() {
final Float32List result = Float32List(20);
const List<int> translationIndices = <int>[0, 6, 12, 18];
for (final int i in translationIndices) {
result[i] = 1;
}
_identityTransform = result;
return result;
}

SkColorFilter createSkColorFilterFromColorAndBlendMode(ui.Color color, ui.BlendMode blendMode) {
/// Return the identity matrix when the color opacity is 0. Replicates
/// effect of applying no filter
if (color.opacity == 0) {
return canvasKit.ColorFilter.MakeMatrix(_identityTransform);
}
final SkColorFilter? filter = canvasKit.ColorFilter.MakeBlend(
toSharedSkColor1(color),
toSkBlendMode(blendMode),
);
if (filter == null) {
throw ArgumentError('Invalid parameters for blend mode ColorFilter');
}
return filter;
}


class CkBlendModeColorFilter extends CkColorFilter {
const CkBlendModeColorFilter(this.color, this.blendMode);

final ui.Color color;
final ui.BlendMode blendMode;

static Float32List get identityTransform => _identityTransform ?? _computeIdentityTransform();
static Float32List? _identityTransform;

static Float32List _computeIdentityTransform() {
final Float32List result = Float32List(20);
const List<int> translationIndices = <int>[0, 6, 12, 18];
for (final int i in translationIndices) {
result[i] = 1;
}
_identityTransform = result;
return result;
}

@override
SkColorFilter _initRawColorFilter() {
/// Return the identity matrix when the color opacity is 0. Replicates
/// effect of applying no filter
if (color.opacity == 0) {
return canvasKit.ColorFilter.MakeMatrix(identityTransform);
}
final SkColorFilter? filter = canvasKit.ColorFilter.MakeBlend(
toSharedSkColor1(color),
toSkBlendMode(blendMode),
);
if (filter == null) {
throw ArgumentError('Invalid parameters for blend mode ColorFilter');
}
return filter;
return createSkColorFilterFromColorAndBlendMode(color, blendMode);
}

@override
Expand Down
4 changes: 3 additions & 1 deletion lib/web_ui/lib/src/engine/canvaskit/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,14 @@ CkImage scaleImage(SkImage image, int? targetWidth, int? targetHeight) {
final CkPictureRecorder recorder = CkPictureRecorder();
final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest);

final CkPaint paint = CkPaint();
canvas.drawImageRect(
CkImage(image),
ui.Rect.fromLTWH(0, 0, image.width(), image.height()),
ui.Rect.fromLTWH(0, 0, targetWidth!.toDouble(), targetHeight!.toDouble()),
CkPaint()
paint,
);
paint.dispose();

final CkPicture picture = recorder.endRecording();
final ui.Image finalImage = picture.toImageSync(
Expand Down