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

Commit c458d12

Browse files
author
jezell
authored
Add createImageFromTextureSource method to ui_web (#53483)
Adds createImageFromTextureSource for flutter web, exposed in dart:web_ui. ```dart final ui.Image uiImage = renderer.createImageFromTextureSource(bitmap, width: 150, height: 150, transferOwnership: false); ``` In canvaskit renderer, this will use MakeLazyImageFromTextureSource. In SkWasmRenderer, it will call imageCreateFromTextureSource, which was already implemented in SkWasm for createImageFromImageBitmap to use, but was not exposed in a way that it could be taken advantage of. By default, createImageFromTextureSource will create a copy of the object it is passed, but transferOwnership: true may be specified to allow transferable objects to be transferred to the renderer, avoiding the copy. Fixes: flutter/flutter#150479 Fixes: flutter/flutter#144815
1 parent 1a51d5e commit c458d12

File tree

7 files changed

+97
-0
lines changed

7 files changed

+97
-0
lines changed

lib/web_ui/lib/src/engine/canvaskit/renderer.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:js_interop';
67
import 'dart:math' as math;
78
import 'dart:typed_data';
89

@@ -238,6 +239,29 @@ class CanvasKitRenderer implements Renderer {
238239
return CkImage(skImage);
239240
}
240241

242+
@override
243+
FutureOr<ui.Image> createImageFromTextureSource(JSAny object,
244+
{required int width, required int height, required bool transferOwnership}) async {
245+
if (!transferOwnership) {
246+
final DomImageBitmap bitmap = await createImageBitmap(object, (x:0, y: 0, width: width, height: height));
247+
return createImageFromImageBitmap(bitmap);
248+
}
249+
final SkImage? skImage = canvasKit.MakeLazyImageFromTextureSourceWithInfo(
250+
object,
251+
SkPartialImageInfo(
252+
width: width.toDouble(),
253+
height: height.toDouble(),
254+
alphaType: canvasKit.AlphaType.Premul,
255+
colorType: canvasKit.ColorType.RGBA_8888,
256+
colorSpace: SkColorSpaceSRGB,
257+
));
258+
259+
if (skImage == null) {
260+
throw Exception('Failed to convert image bitmap to an SkImage.');
261+
}
262+
return CkImage(skImage);
263+
}
264+
241265
@override
242266
void decodeImageFromPixels(Uint8List pixels, int width, int height,
243267
ui.PixelFormat format, ui.ImageDecoderCallback callback,

lib/web_ui/lib/src/engine/html/renderer.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,4 +371,9 @@ class HtmlRenderer implements Renderer {
371371
imageElement.src = await canvas.toDataUrl();
372372
return completer.future;
373373
}
374+
375+
@override
376+
FutureOr<ui.Image> createImageFromTextureSource(JSAny object, { required int width, required int height, required bool transferOwnership }) {
377+
throw Exception('Not implemented for HTML renderer');
378+
}
374379
}

lib/web_ui/lib/src/engine/renderer.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:js_interop';
67
import 'dart:math' as math;
78
import 'dart:typed_data';
89

@@ -129,6 +130,9 @@ abstract class Renderer {
129130

130131
FutureOr<ui.Image> createImageFromImageBitmap(DomImageBitmap imageSource);
131132

133+
FutureOr<ui.Image> createImageFromTextureSource(JSAny object,
134+
{required int width, required int height, required bool transferOwnership});
135+
132136
void decodeImageFromPixels(
133137
Uint8List pixels,
134138
int width,

lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,19 @@ class SkwasmRenderer implements Renderer {
472472
surface.handle,
473473
));
474474
}
475+
476+
@override
477+
FutureOr<ui.Image> createImageFromTextureSource(JSAny textureSource, { required int width, required int height, required bool transferOwnership }) async {
478+
if (!transferOwnership) {
479+
textureSource = (await createImageBitmap(textureSource, (x: 0, y: 0, width: width, height: height))).toJSAnyShallow;
480+
}
481+
return SkwasmImage(imageCreateFromTextureSource(
482+
textureSource as JSObject,
483+
width,
484+
height,
485+
surface.handle,
486+
));
487+
}
475488
}
476489

477490
class SkwasmPictureRenderer implements PictureRenderer {

lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:js_interop';
67
import 'dart:math' as math;
78
import 'dart:typed_data';
89

@@ -189,4 +190,9 @@ class SkwasmRenderer implements Renderer {
189190
ui.Image createImageFromImageBitmap(DomImageBitmap imageSource) {
190191
throw UnimplementedError('Skwasm not implemented on this platform.');
191192
}
193+
194+
@override
195+
ui.Image createImageFromTextureSource(JSAny object, { required int width, required int height, required bool transferOwnership }) {
196+
throw Exception('Skwasm not implemented on this platform.');
197+
}
192198
}

lib/web_ui/lib/ui_web/src/ui_web/images.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,17 @@ FutureOr<ui.Image> createImageFromImageBitmap(JSAny imageSource) {
4444
imageSource as DomImageBitmap,
4545
);
4646
}
47+
48+
/// Creates a [ui.Image] from a valid texture source (for example
49+
/// HTMLImageElement | HTMLVideoElement | HTMLCanvasElement).
50+
///
51+
/// By default, [transferOwnership] specifies that the ownership of the texture
52+
/// will be not be transferred to the renderer, and a copy of the texture source
53+
/// will be made. If this is not desired, the ownership of the object can be
54+
/// transferred to the renderer and the engine will take ownership of the
55+
/// texture source and consume its contents.
56+
FutureOr<ui.Image> createImageFromTextureSource(JSAny object, { required int width, required int height, bool transferOwnership = false }) {
57+
return renderer.createImageFromTextureSource(
58+
object, width: width, height: height, transferOwnership: transferOwnership
59+
);
60+
}

lib/web_ui/test/ui/image_golden_test.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,37 @@ Future<void> testMain() async {
420420
});
421421
}
422422

423+
// This API doesn't work in headless Firefox due to requiring WebGL
424+
// See https://github.com/flutter/flutter/issues/109265
425+
if (!isFirefox && !isHtml) {
426+
emitImageTests('svg_image_bitmap_texture_source', () async {
427+
final DomBlob svgBlob = createDomBlob(<String>[
428+
'''
429+
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
430+
<path d="M25,75 A50,50 0 1,0 125 75 L75,25 Z" stroke="blue" stroke-width="10" fill="red"></path>
431+
</svg>
432+
'''
433+
], <String, String>{
434+
'type': 'image/svg+xml'
435+
});
436+
final String url = domWindow.URL.createObjectURL(svgBlob);
437+
final DomHTMLImageElement image = createDomHTMLImageElement();
438+
final Completer<void> completer = Completer<void>();
439+
late final DomEventListener loadListener;
440+
loadListener = createDomEventListener((DomEvent event) {
441+
completer.complete();
442+
image.removeEventListener('load', loadListener);
443+
});
444+
image.addEventListener('load', loadListener);
445+
image.src = url;
446+
await completer.future;
447+
448+
final ui.Image uiImage =
449+
await renderer.createImageFromTextureSource(image.toJSAnyShallow, width: 150, height: 150, transferOwnership: false);
450+
return uiImage;
451+
});
452+
}
453+
423454
emitImageTests('codec_list_resized', () async {
424455
final ByteBuffer data = await httpFetchByteBuffer('/test_images/mandrill_128.png');
425456
final ui.Codec codec = await renderer.instantiateImageCodec(

0 commit comments

Comments
 (0)