Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Image.toByteData and Picture.toImage implementations (#3) #20750

Merged
merged 87 commits into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
93e3f0b
Update image.dart
deakjahn Jul 10, 2020
cc827d6
Update html_image_codec.dart
deakjahn Jul 10, 2020
6b9fd73
rebase
deakjahn Jul 28, 2020
352b31a
Update image.dart
deakjahn Jul 10, 2020
4ea7ef2
Update picture.dart
deakjahn Jul 10, 2020
f99c8b2
Update picture.dart
deakjahn Jul 10, 2020
1e7188f
Update picture.dart
deakjahn Jul 10, 2020
4017e40
Update html_image_codec.dart
deakjahn Jul 10, 2020
ed811cf
Update image.dart
deakjahn Jul 10, 2020
d6dc410
Update image.dart
deakjahn Jul 12, 2020
c2c70e4
Revert
deakjahn Jul 23, 2020
8b1ed1a
Tested by local engine
deakjahn Jul 24, 2020
bad4d45
Add files via upload
deakjahn Jul 24, 2020
22dbcf7
Delete picture_to_image_test.dart
deakjahn Jul 24, 2020
5eb7b8a
Test #1
deakjahn Jul 25, 2020
1feae02
Test to read only
deakjahn Jul 26, 2020
4c9001b
Finally
deakjahn Jul 28, 2020
a2ff894
Sorry, spaces
deakjahn Jul 28, 2020
0c72496
Test, finally
deakjahn Jul 28, 2020
91b12ca
@JS refactoring
deakjahn Jul 28, 2020
9eeb7e0
@JS refactoring
deakjahn Jul 28, 2020
6efd763
SkData and SkImageInfo
deakjahn Jul 29, 2020
22d2f96
Unused items
deakjahn Jul 29, 2020
f1d49be
Test to write PNG
deakjahn Jul 29, 2020
6675e13
Remove comments
deakjahn Aug 4, 2020
ced934c
Add comments
deakjahn Aug 4, 2020
7b78b27
CK test
deakjahn Aug 4, 2020
11c4073
Comment about copying
deakjahn Aug 5, 2020
72ef505
Update image.dart
deakjahn Jul 10, 2020
1afe140
Update html_image_codec.dart
deakjahn Jul 10, 2020
18642e4
rebase
deakjahn Jul 28, 2020
a20d80d
Update image.dart
deakjahn Jul 10, 2020
e3147c5
Update picture.dart
deakjahn Jul 10, 2020
ae8ba90
Update picture.dart
deakjahn Jul 10, 2020
abf4f78
Update picture.dart
deakjahn Jul 10, 2020
2abe68f
Update html_image_codec.dart
deakjahn Jul 10, 2020
0b765d8
Update image.dart
deakjahn Jul 10, 2020
1109004
Update image.dart
deakjahn Jul 12, 2020
17a2ef5
Revert
deakjahn Jul 23, 2020
0c2aac9
Tested by local engine
deakjahn Jul 24, 2020
301ae90
Add files via upload
deakjahn Jul 24, 2020
fbfd675
Delete picture_to_image_test.dart
deakjahn Jul 24, 2020
b49583b
Test #1
deakjahn Jul 25, 2020
26a49fb
Test to read only
deakjahn Jul 26, 2020
b23ca62
Test, finally
deakjahn Jul 28, 2020
41d664a
@JS refactoring
deakjahn Jul 28, 2020
fcd1d2f
@JS refactoring
deakjahn Jul 28, 2020
a29069f
SkData and SkImageInfo
deakjahn Jul 29, 2020
83a7399
Unused items
deakjahn Jul 29, 2020
99eb64c
Test to write PNG
deakjahn Jul 29, 2020
4b28ee1
Remove comments
deakjahn Aug 4, 2020
10f4d4c
CK test
deakjahn Aug 4, 2020
70d9884
Merge branch 'master' of https://github.com/deakjahn/engine
deakjahn Aug 7, 2020
a1346ea
Rebase manually
deakjahn Aug 7, 2020
4e41dfc
Rebase manually
deakjahn Aug 7, 2020
fb02673
New test
deakjahn Aug 7, 2020
8c2790c
Newline
deakjahn Aug 7, 2020
6a31d41
Rebase dirt
deakjahn Aug 7, 2020
dc77edc
SkImageInfo
deakjahn Aug 7, 2020
54e706c
New test
deakjahn Aug 7, 2020
d4a07a1
New test
deakjahn Aug 7, 2020
6db0a3a
New test
deakjahn Aug 7, 2020
6daead7
Missing annotation
deakjahn Aug 7, 2020
75770e7
Rebase
deakjahn Aug 11, 2020
ad715e1
ColorSpace
deakjahn Aug 13, 2020
69dd265
SkImageInfo correct names
deakjahn Aug 13, 2020
078d6bc
SkImageInfo correct names
deakjahn Aug 13, 2020
5dd02dd
SkImageInfo correct names
deakjahn Aug 13, 2020
5f4d5e7
SkImageInfo properties
deakjahn Aug 13, 2020
107ba3d
Inactive test
deakjahn Aug 13, 2020
93cd91d
Merge remote-tracking branch 'upstream/master'
deakjahn Aug 19, 2020
69ec1cc
Unused import
deakjahn Aug 21, 2020
9d5a611
Merge remote-tracking branch 'upstream/master'
deakjahn Aug 23, 2020
6e1d95d
Squash 2
deakjahn Aug 23, 2020
7da3c86
Merge branch 'master' of https://github.com/deakjahn/engine
deakjahn Aug 23, 2020
90d0b69
Squash 2.
deakjahn Aug 23, 2020
4c39264
Merge branch 'master' of https://github.com/deakjahn/engine
deakjahn Aug 23, 2020
9cf68f8
Merge remote-tracking branch 'upstream/master'
deakjahn Aug 25, 2020
91ed8f4
Add files via upload
deakjahn Aug 25, 2020
38cf1e1
Add files via upload
deakjahn Aug 25, 2020
faf4ba9
Add files via upload
deakjahn Aug 25, 2020
b895e66
Add files via upload
deakjahn Aug 25, 2020
a57c674
Update canvas_to_picture_test.dart
deakjahn Aug 27, 2020
ba82ef9
Merge remote-tracking branch 'upstream/master'
deakjahn Aug 30, 2020
a36894b
Merge branch 'deakjahn-patch-1' of https://github.com/deakjahn/engine…
deakjahn Aug 30, 2020
ae9605a
Update canvas_to_picture_test.dart
deakjahn Aug 30, 2020
21feeae
Update canvas_to_picture_test.dart
deakjahn Aug 30, 2020
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
79 changes: 78 additions & 1 deletion lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

/// Bindings for CanvasKit JavaScript API.
///
/// Prefer keeping the originl CanvasKit names so it is easier to locate
/// Prefer keeping the original CanvasKit names so it is easier to locate
/// the API behind these bindings in the Skia source code.

// @dart = 2.10
Expand All @@ -31,6 +31,8 @@ class CanvasKit {
external SkBlurStyleEnum get BlurStyle;
external SkTileModeEnum get TileMode;
external SkFillTypeEnum get FillType;
external SkAlphaTypeEnum get AlphaType;
external SkColorTypeEnum get ColorType;
external SkPathOpEnum get PathOp;
external SkClipOpEnum get ClipOp;
external SkPointModeEnum get PointMode;
Expand Down Expand Up @@ -62,6 +64,13 @@ class CanvasKit {
external SkParagraphStyle ParagraphStyle(
SkParagraphStyleProperties properties);
external SkTextStyle TextStyle(SkTextStyleProperties properties);
external SkSurface MakeSurface(
int width,
int height,
);
external Uint8List getSkDataBytes(
SkData skData,
);

// Text decoration enum is embedded in the CanvasKit object itself.
external int get NoDecoration;
Expand Down Expand Up @@ -128,6 +137,7 @@ class SkSurface {
external int width();
external int height();
external void dispose();
external SkImage makeImageSnapshot();
}

@JS()
Expand Down Expand Up @@ -623,6 +633,38 @@ SkTileMode toSkTileMode(ui.TileMode mode) {
return _skTileModes[mode.index];
}

@JS()
class SkAlphaTypeEnum {
external SkAlphaType get Opaque;
external SkAlphaType get Premul;
external SkAlphaType get Unpremul;
}

@JS()
class SkAlphaType {
external int get value;
}

@JS()
class SkColorTypeEnum {
external SkColorType get Alpha_8;
external SkColorType get RGB_565;
external SkColorType get ARGB_4444;
external SkColorType get RGBA_8888;
external SkColorType get RGB_888x;
external SkColorType get BGRA_8888;
external SkColorType get RGBA_1010102;
external SkColorType get RGB_101010x;
external SkColorType get Gray_8;
external SkColorType get RGBA_F16;
external SkColorType get RGBA_F32;
}

@JS()
class SkColorType {
external int get value;
}

@JS()
@anonymous
class SkAnimatedImage {
Expand All @@ -634,6 +676,8 @@ class SkAnimatedImage {
external SkImage getCurrentFrame();
external int width();
external int height();
external Uint8List readPixels(SkImageInfo imageInfo, int srcX, int srcY);
external SkData encodeToData();

/// Deletes the C++ object.
///
Expand All @@ -652,6 +696,8 @@ class SkImage {
SkTileMode tileModeY,
Float32List? matrix, // 3x3 matrix
);
external Uint8List readPixels(SkImageInfo imageInfo, int srcX, int srcY);
external SkData encodeToData();
}

@JS()
Expand Down Expand Up @@ -1662,3 +1708,34 @@ external Object? get _finalizationRegistryConstructor;

/// Whether the current browser supports `FinalizationRegistry`.
bool browserSupportsFinalizationRegistry = _finalizationRegistryConstructor != null;

@JS()
class SkData {
external int size();
external bool isEmpty();
external Uint8List bytes();
}

@JS()
@anonymous
class SkImageInfo {
external factory SkImageInfo({
required int width,
required int height,
SkAlphaType alphaType,
SkColorSpace colorSpace,
SkColorType colorType,
});
external SkAlphaType get alphaType;
external SkColorSpace get colorSpace;
external SkColorType get colorType;
external int get height;
external bool get isEmpty;
external bool get isOpaque;
external SkRect get bounds;
external int get width;
external SkImageInfo makeAlphaType(SkAlphaType alphaType);
external SkImageInfo makeColorSpace(SkColorSpace colorSpace);
external SkImageInfo makeColorType(SkColorType colorType);
external SkImageInfo makeWH(int width, int height);
}
40 changes: 38 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,25 @@ class CkAnimatedImage implements ui.Image {
@override
Future<ByteData> toByteData(
{ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
throw 'unimplemented';
Uint8List bytes;

if (format == ui.ImageByteFormat.rawRgba) {
final SkImageInfo imageInfo = SkImageInfo(
alphaType: canvasKit.AlphaType.Premul,
colorType: canvasKit.ColorType.RGBA_8888,
colorSpace: SkColorSpaceSRGB,
width: width,
height: height,
);
bytes = _skAnimatedImage.readPixels(imageInfo, 0, 0);
} else {
final SkData skData = _skAnimatedImage.encodeToData(); //defaults to PNG 100%
// make a copy that we can return
bytes = Uint8List.fromList(canvasKit.getSkDataBytes(skData));
}

final ByteData data = bytes.buffer.asByteData(0, bytes.length);
return Future<ByteData>.value(data);
}
}

Expand Down Expand Up @@ -105,7 +123,25 @@ class CkImage implements ui.Image {
@override
Future<ByteData> toByteData(
{ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
throw 'unimplemented';
Uint8List bytes;

if (format == ui.ImageByteFormat.rawRgba) {
final SkImageInfo imageInfo = SkImageInfo(
alphaType: canvasKit.AlphaType.Premul,
colorType: canvasKit.ColorType.RGBA_8888,
colorSpace: SkColorSpaceSRGB,
width: width,
height: height,
);
bytes = skImage.readPixels(imageInfo, 0, 0);
} else {
final SkData skData = skImage.encodeToData(); //defaults to PNG 100%
// make a copy that we can return
bytes = Uint8List.fromList(canvasKit.getSkDataBytes(skData));
}

final ByteData data = bytes.buffer.asByteData(0, bytes.length);
return Future<ByteData>.value(data);
}
}

Expand Down
10 changes: 7 additions & 3 deletions lib/web_ui/lib/src/engine/canvaskit/picture.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ class CkPicture implements ui.Picture {
}

@override
Future<ui.Image> toImage(int width, int height) {
throw UnsupportedError(
'Picture.toImage not yet implemented for CanvasKit and HTML');
Future<ui.Image> toImage(int width, int height) async {
final SkSurface skSurface = canvasKit.MakeSurface(width, height);
final SkCanvas skCanvas = skSurface.getCanvas();
skCanvas.drawPicture(skiaObject.skiaObject);
final SkImage skImage = skSurface.makeImageSnapshot();
skSurface.dispose();
return CkImage(skImage);
}
}

Expand Down
22 changes: 7 additions & 15 deletions lib/web_ui/lib/src/engine/html_image_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ class HtmlImage implements ui.Image {
final int height;

@override
Future<ByteData?> toByteData(
{ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
return futurize((Callback<ByteData?> callback) {
return _toByteData(format.index, (Uint8List? encoded) {
callback(encoded?.buffer.asByteData());
});
});
Future<ByteData?> toByteData({ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
if (imgElement.src?.startsWith('data:') == true) {
final data = UriData.fromUri(Uri.parse(imgElement.src!));
return Future.value(data.contentAsBytes().buffer.asByteData());
Copy link
Contributor

Choose a reason for hiding this comment

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

This ignores the format parameter. It's not entirely clear to me what format the data: URI is in, I'm trying to test this and I'm only seeing regular URIs or blob URIs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, this was some time ago already, we spent some with finalizing the PR, so the code itself is even older. Let me think. I seem to remember PNG was the only format available in the data URL, so it simply couldn't support anything else. The possibilities are:

  • Only serve PNG, throw for the others,
  • Always serve PNG, no matter what is asked for (current),
  • Convert if something else than PNG is asked for (unlikely, I guess).

} else {
return Future.value(null);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should probably throw instead of returning null. This indicates we don't support the format. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I really don't know what you normally do in a situation like that. If throw, than throw it is.

}
}

// Returns absolutely positioned actual image element on first call and
Expand All @@ -149,12 +149,4 @@ class HtmlImage implements ui.Image {
return imgElement;
}
}

// TODO(het): Support this for asset images and images generated from
// `Picture`s.
/// Returns an error message on failure, null on success.
String _toByteData(int format, Callback<Uint8List?> callback) {
callback(null);
return 'Image.toByteData is not supported in Flutter for Web';
}
}
25 changes: 25 additions & 0 deletions lib/web_ui/test/canvaskit/canvaskit_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1188,4 +1188,29 @@ void _canvasTests() {
20,
);
});

test('toImage.toByteData', () async {
final SkPictureRecorder otherRecorder = SkPictureRecorder();
final SkCanvas otherCanvas = otherRecorder.beginRecording(SkRect(
fLeft: 0,
fTop: 0,
fRight: 1,
fBottom: 1,
));
otherCanvas.drawRect(
SkRect(
fLeft: 0,
fTop: 0,
fRight: 1,
fBottom: 1,
),
SkPaint(),
);
final CkPicture picture = CkPicture(otherRecorder.finishRecordingAsPicture(), null);
final CkImage image = await picture.toImage(1, 1);
final ByteData rawData = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
expect(rawData, isNotNull);
final ByteData pngData = await image.toByteData(format: ui.ImageByteFormat.png);
expect(pngData, isNotNull);
});
}
55 changes: 55 additions & 0 deletions lib/web_ui/test/golden_tests/engine/canvas_to_picture_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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.

// @dart = 2.6
import 'dart:html' as html;

import 'package:ui/ui.dart';
import 'package:ui/src/engine.dart';

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';

void main() {
internalBootstrapBrowserTest(() => testMain);
}

void testMain() async {
final Rect region = Rect.fromLTWH(0, 0, 500, 500);

setUp(() async {
debugShowClipLayers = true;
SurfaceSceneBuilder.debugForgetFrameScene();
for (html.Node scene in html.document.querySelectorAll('flt-scene')) {
scene.remove();
}

await webOnlyInitializePlatform();
webOnlyFontCollection.debugRegisterTestFonts();
await webOnlyFontCollection.ensureFontsLoaded();
});

test('Convert Canvas to Picture', () async {
final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
final Picture testPicture = await _drawTestPictureWithCircle(region);
builder.addPicture(Offset.zero, testPicture);

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

//await matchGoldenFile('canvas_to_picture.png', region: region, write: true);
});
}

Picture _drawTestPictureWithCircle(Rect region) {
final EnginePictureRecorder recorder = PictureRecorder();
final RecordingCanvas canvas = recorder.beginRecording(region);
canvas.drawOval(
region,
Paint()
..style = PaintingStyle.fill
..color = Color(0xFF00FF00));
return recorder.endRecording();
}