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 21 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
eb5cc0e
Update painting.dart
polina-c Aug 9, 2022
401a169
Update painting.dart
polina-c Aug 9, 2022
d6df6b8
-
polina-c Aug 10, 2022
eaf2ec8
-
polina-c Aug 10, 2022
3e17cd1
Update image_test.dart
polina-c Aug 10, 2022
b47c31e
-
polina-c Aug 10, 2022
427592e
Update painting.dart
polina-c Aug 10, 2022
dbe4eaf
Merge branch 'flutter:main' into painting
polina-c Aug 11, 2022
804d542
Merge branch 'main' of github.com:flutter/engine into painting
polina-c Aug 11, 2022
1434af9
Update painting.dart
polina-c Aug 11, 2022
60a95a0
Update image_test.dart
polina-c Aug 11, 2022
e2a1f76
-
polina-c Aug 11, 2022
ca99635
Update image_dispose_test.dart
polina-c Aug 11, 2022
1f85ecf
Update image_test.dart
polina-c Aug 11, 2022
4a3d810
Merge branch 'painting' of github.com:polina-c/engine into painting
polina-c Aug 11, 2022
b577107
-
polina-c Aug 11, 2022
1e902a1
Update picture_test.dart
polina-c Aug 11, 2022
b6ff393
-
polina-c Aug 11, 2022
f03e228
Update image_test.dart
polina-c Aug 11, 2022
6832d11
Update picture_test.dart
polina-c Aug 11, 2022
2ef20d0
Update image_test.dart
polina-c Aug 11, 2022
932932f
Update image_test.dart
polina-c Aug 11, 2022
4ea889e
-
polina-c Aug 13, 2022
0f53a5b
-
polina-c Aug 13, 2022
c9df53e
Update image_test.dart
polina-c Aug 13, 2022
3f258cd
Update image_test.dart
polina-c Aug 24, 2022
f951116
Update picture_test.dart
polina-c Aug 24, 2022
7940b1e
Update image_test.dart
polina-c Aug 25, 2022
cc9912f
-
polina-c Aug 25, 2022
5ae80a6
Update painting.dart
polina-c Aug 25, 2022
ba7d0a5
-
polina-c Aug 25, 2022
7fec23d
-
polina-c Aug 25, 2022
9a8de06
-
polina-c Aug 29, 2022
22babb6
Create image_test.dart
polina-c Aug 29, 2022
fca82fe
Merge branch 'master' of github.com:flutter/engine into painting
polina-c Aug 30, 2022
41754ed
-
polina-c Aug 30, 2022
4b24628
-
polina-c Aug 30, 2022
59cb250
Update image_test.dart
polina-c Aug 31, 2022
0efa9c8
Update BUILD.gn
polina-c Aug 31, 2022
62431c1
-
polina-c Aug 31, 2022
a958c1d
Update painting.dart
polina-c Aug 31, 2022
20cd6d9
Update api_conform_test.dart
polina-c Aug 31, 2022
76f096e
Update painting.dart
polina-c Aug 31, 2022
6f949a2
Update painting.dart
polina-c Aug 31, 2022
f87653e
Update canvas.dart
polina-c Aug 31, 2022
99ae37d
addressed comments
polina-c Sep 7, 2022
360ff2a
Update painting.dart
polina-c Sep 8, 2022
0da0e31
Merge branch 'flutter:main' into painting
polina-c Sep 8, 2022
e972fd8
Merge branch 'flutter:main' into painting
polina-c Sep 8, 2022
bf75813
Merge branch 'flutter:main' into painting
polina-c Sep 9, 2022
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
33 changes: 32 additions & 1 deletion lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1649,12 +1649,27 @@ class Image {
return true;
}());
_image._handles.add(this);
onCreate?.call(this);
}

// C++ unit tests access this.
@pragma('vm:entry-point')
final _Image _image;

/// A callback that is invoked to report an object creation.
///
/// It's preferred to use [MemoryAllocations] in flutter/foundation.dart
/// than to use [onCreate] directly because [MemoryAllocations]
/// allows multiple callbacks.
static void Function(Image)? onCreate;

/// A callback that is invoked to report the object disposal.
///
/// It's preferred to use [MemoryAllocations] in flutter/foundation.dart
/// than to use [onDispose] directly because [MemoryAllocations]
/// allows multiple callbacks.
static void Function(Image)? onDispose;

StackTrace? _debugStack;

/// The number of image pixels along the image's horizontal axis.
Expand All @@ -1675,6 +1690,7 @@ class Image {
/// useful when trying to determine what parts of the program are keeping an
/// image resident in memory.
void dispose() {
onDispose?.call(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere: can we guard calling these so it's compiled out of release mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I know there is no compile time flags (like bool.fromEnvironment('flutter.memory_allocations')) in engine.
And 'normal' flags will not be more efficient both from performance perspective and code size perspective, than comparison to null. So there is no point in such guarding.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline - any flag we put in here would be at best a runtime check that's probably no faster than the null check here.

assert(!_disposed && !_image._disposed);
assert(_image._handles.contains(this));
_disposed = true;
Expand Down Expand Up @@ -5591,7 +5607,21 @@ class Picture extends NativeFieldWrapperClass1 {
///
/// To create a [Picture], use a [PictureRecorder].
@pragma('vm:entry-point')
Picture._();
Picture._() { onCreate?.call(this); }

/// A callback that is invoked to report an object creation.
///
/// It's preferred to use [MemoryAllocations] in flutter/foundation.dart
/// than to use [onCreate] directly because [MemoryAllocations]
/// allows multiple callbacks.
static void Function(Picture)? onCreate;

/// A callback that is invoked to report the object disposal.
///
/// It's preferred to use [MemoryAllocations] in flutter/foundation.dart
/// than to use [onDispose] directly because [MemoryAllocations]
/// allows multiple callbacks.
static void Function(Picture)? onDispose;

/// Creates an image from this picture.
///
Expand Down Expand Up @@ -5656,6 +5686,7 @@ class Picture extends NativeFieldWrapperClass1 {
_disposed = true;
return true;
}());
onDispose?.call(this);
_dispose();
}

Expand Down
5 changes: 4 additions & 1 deletion lib/web_ui/lib/canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,12 @@ abstract class Canvas {
}

abstract class Picture {
Picture() { onCreate?.call(this); }
static void Function(Picture)? onCreate;
static void Function(Picture)? onDispose;
Future<Image> toImage(int width, int height);
Image toImageSync(int width, int height);
void dispose();
void dispose() { onDispose?.call(this); }
bool get debugDisposed;
int get approximateBytesUsed;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/web_ui/lib/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ abstract class Gradient extends Shader {
}

abstract class Image {
static void Function(Image)? onCreate;
static void Function(Image)? onDispose;

int get width;
int get height;
Future<ByteData?> toByteData({ImageByteFormat format = ImageByteFormat.rawRgba});
Expand Down
12 changes: 8 additions & 4 deletions lib/web_ui/lib/src/engine/canvaskit/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,7 @@ Future<Uint8List> fetchImage(
/// A [ui.Image] backed by an `SkImage` from Skia.
class CkImage implements ui.Image, StackTraceDebugger {
CkImage(SkImage skImage, { this.videoFrame }) {
if (assertionsEnabled) {
_debugStackTrace = StackTrace.current;
}
_init();
if (browserSupportsFinalizationRegistry) {
box = SkiaObjectBox<CkImage, SkImage>(this, skImage);
} else {
Expand Down Expand Up @@ -200,10 +198,15 @@ class CkImage implements ui.Image, StackTraceDebugger {
}

CkImage.cloneOf(this.box) {
_init();
box.ref(this);
}

void _init() {
if (assertionsEnabled) {
_debugStackTrace = StackTrace.current;
}
box.ref(this);
ui.Image.onCreate?.call(this);
}

@override
Expand Down Expand Up @@ -237,6 +240,7 @@ class CkImage implements ui.Image, StackTraceDebugger {

@override
void dispose() {
ui.Image.onDispose?.call(this);
assert(
!_disposed,
'Cannot dispose an image that has already been disposed.',
Expand Down
4 changes: 1 addition & 3 deletions lib/web_ui/lib/src/engine/embedder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -419,9 +419,7 @@ class FlutterViewEmbedder {
/// Called immediately after browser window language change.
void _languageDidChange(DomEvent event) {
EnginePlatformDispatcher.instance.updateLocales();
if (ui.window.onLocaleChanged != null) {
ui.window.onLocaleChanged!();
}
ui.window.onLocaleChanged?.call();
}

static const String orientationLockTypeAny = 'any';
Expand Down
5 changes: 4 additions & 1 deletion lib/web_ui/lib/src/engine/html_image_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,17 @@ class SingleFrameInfo implements ui.FrameInfo {
}

class HtmlImage implements ui.Image {
HtmlImage(this.imgElement, this.width, this.height);
HtmlImage(this.imgElement, this.width, this.height) {
ui.Image.onCreate?.call(this);
}

final DomHTMLImageElement imgElement;
bool _requiresClone = false;

bool _disposed = false;
@override
void dispose() {
ui.Image.onDispose?.call(this);
// Do nothing. The codec that owns this image should take care of
// releasing the object url.
if (assertionsEnabled) {
Expand Down
60 changes: 60 additions & 0 deletions lib/web_ui/test/engine/image/image_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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.

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

void main() {
test('Image constructor invokes onCreate once', () async {
int onCreateInvokedCount = 0;
Image? createdImage;
Image.onCreate = (Image image) {
onCreateInvokedCount++;
createdImage = image;
};

final Image image1 = await _createImage();

expect(onCreateInvokedCount, 1);
expect(createdImage, image1);

final Image image2 = await _createImage();

expect(onCreateInvokedCount, 2);
expect(createdImage, image2);
Image.onCreate = null;
});

test('dispose() invokes onDispose once', () async {
int onDisposeInvokedCount = 0;
Image? disposedImage;
Image.onDispose = (Image image) {
onDisposeInvokedCount++;
disposedImage = image;
};

final Image image1 = await _createImage()..dispose();

expect(onDisposeInvokedCount, 1);
expect(disposedImage, image1);

final Image image2 = await _createImage()..dispose();

expect(onDisposeInvokedCount, 2);
expect(disposedImage, image2);

Image.onDispose = null;
});
}


Future<Image> _createImage() async => _createPicture().toImage(10, 10);

Picture _createPicture() {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect rect = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
canvas.clipRect(rect);
return recorder.endRecording();
}
57 changes: 57 additions & 0 deletions lib/web_ui/test/engine/picture_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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.

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

void main() {
test('Picture constructor invokes onCreate once', () async {
int onCreateInvokedCount = 0;
Picture? createdPicture;
Picture.onCreate = (Picture picture) {
onCreateInvokedCount++;
createdPicture = picture;
};

final Picture picture1 = _createPicture();

expect(onCreateInvokedCount, 1);
expect(createdPicture, picture1);

final Picture picture2 = _createPicture();

expect(onCreateInvokedCount, 2);
expect(createdPicture, picture2);
Picture.onCreate = null;
});

test('dispose() invokes onDispose once', () async {
int onDisposeInvokedCount = 0;
Picture? disposedPicture;
Picture.onDispose = (Picture picture) {
onDisposeInvokedCount++;
disposedPicture = picture;
};

final Picture picture1 = _createPicture()..dispose();

expect(onDisposeInvokedCount, 1);
expect(disposedPicture, picture1);

final Picture picture2 = _createPicture()..dispose();

expect(onDisposeInvokedCount, 2);
expect(disposedPicture, picture2);

Picture.onDispose = null;
});
}

Picture _createPicture() {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect rect = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
canvas.clipRect(rect);
return recorder.endRecording();
}
2 changes: 2 additions & 0 deletions testing/dart/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tests = [
"gradient_test.dart",
"http_allow_http_connections_test.dart",
"http_disallow_http_connections_test.dart",
"image_test.dart",
"image_descriptor_test.dart",
"image_dispose_test.dart",
"image_filter_test.dart",
Expand All @@ -33,6 +34,7 @@ tests = [
"paragraph_builder_test.dart",
"paragraph_test.dart",
"path_test.dart",
"picture_test.dart",
"platform_view_test.dart",
"plugin_utilities_test.dart",
"semantics_test.dart",
Expand Down
43 changes: 37 additions & 6 deletions testing/dart/image_dispose_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ void main() {
}());

test('Handles are distinct', () async {
final Uint8List bytes = await readFile('2x2.png');
final Uint8List bytes = await _readFile('2x2.png');
final Codec codec = await instantiateImageCodec(bytes);
final FrameInfo frame = await codec.getNextFrame();

Expand All @@ -36,7 +36,7 @@ void main() {
});

test('Canvas can paint image from handle and byte data from handle', () async {
final Uint8List bytes = await readFile('2x2.png');
final Uint8List bytes = await _readFile('2x2.png');
final Codec codec = await instantiateImageCodec(bytes);
final FrameInfo frame = await codec.getNextFrame();

Expand Down Expand Up @@ -66,7 +66,7 @@ void main() {
});

test('Records stack traces', () async {
final Uint8List bytes = await readFile('2x2.png');
final Uint8List bytes = await _readFile('2x2.png');
final Codec codec = await instantiateImageCodec(bytes);
final FrameInfo frame = await codec.getNextFrame();

Expand All @@ -92,7 +92,7 @@ void main() {
}, skip: !assertsEnabled);

test('Clones can be compared', () async {
final Uint8List bytes = await readFile('2x2.png');
final Uint8List bytes = await _readFile('2x2.png');
final Codec codec = await instantiateImageCodec(bytes);
final FrameInfo frame = await codec.getNextFrame();

Expand All @@ -115,7 +115,7 @@ void main() {
});

test('debugDisposed works', () async {
final Uint8List bytes = await readFile('2x2.png');
final Uint8List bytes = await _readFile('2x2.png');
final Codec codec = await instantiateImageCodec(bytes);
final FrameInfo frame = await codec.getNextFrame();

Expand All @@ -132,9 +132,40 @@ void main() {
expect(() => frame.image.debugDisposed, throwsStateError);
}
});

test('dispose() invokes onDispose once', () async {
int onDisposeInvokedCount = 0;
Image? disposedImage;
Image.onDispose = (Image image) {
onDisposeInvokedCount++;
disposedImage = image;
};

final Image image1 = await _createImage()..dispose();

expect(onDisposeInvokedCount, 1);
expect(disposedImage, image1);

final Image image2 = await _createImage()..dispose();

expect(onDisposeInvokedCount, 2);
expect(disposedImage, image2);

Image.onDispose = null;
});
}

Future<Image> _createImage() async => _createPicture().toImage(10, 10);

Picture _createPicture() {
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
const Rect rect = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
canvas.clipRect(rect);
return recorder.endRecording();
}

Future<Uint8List> readFile(String fileName) async {
Future<Uint8List> _readFile(String fileName) async {
final File file = File(path.join(
'flutter',
'testing',
Expand Down
Loading