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

Commit e8c00a3

Browse files
Initial support for images in Skwasm (#42019)
This partially implements flutter/flutter#126341 It does not implement image codecs, because they are going to get complicated with transferring video frames to the web worker and so on. I am going to deal with image codecs in a subsequent change.
1 parent 2ff91f3 commit e8c00a3

29 files changed

+1120
-218
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.da
20072007
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart + ../../../flutter/LICENSE
20082008
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart + ../../../flutter/LICENSE
20092009
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart + ../../../flutter/LICENSE
2010+
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart + ../../../flutter/LICENSE
20102011
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart + ../../../flutter/LICENSE
20112012
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart + ../../../flutter/LICENSE
20122013
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart + ../../../flutter/LICENSE
@@ -2077,6 +2078,7 @@ ORIGIN: ../../../flutter/lib/web_ui/skwasm/data.cpp + ../../../flutter/LICENSE
20772078
ORIGIN: ../../../flutter/lib/web_ui/skwasm/export.h + ../../../flutter/LICENSE
20782079
ORIGIN: ../../../flutter/lib/web_ui/skwasm/fonts.cpp + ../../../flutter/LICENSE
20792080
ORIGIN: ../../../flutter/lib/web_ui/skwasm/helpers.h + ../../../flutter/LICENSE
2081+
ORIGIN: ../../../flutter/lib/web_ui/skwasm/image.cpp + ../../../flutter/LICENSE
20802082
ORIGIN: ../../../flutter/lib/web_ui/skwasm/paint.cpp + ../../../flutter/LICENSE
20812083
ORIGIN: ../../../flutter/lib/web_ui/skwasm/path.cpp + ../../../flutter/LICENSE
20822084
ORIGIN: ../../../flutter/lib/web_ui/skwasm/picture.cpp + ../../../flutter/LICENSE
@@ -4626,6 +4628,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart
46264628
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart
46274629
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart
46284630
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart
4631+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart
46294632
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart
46304633
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart
46314634
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart
@@ -4696,6 +4699,7 @@ FILE: ../../../flutter/lib/web_ui/skwasm/data.cpp
46964699
FILE: ../../../flutter/lib/web_ui/skwasm/export.h
46974700
FILE: ../../../flutter/lib/web_ui/skwasm/fonts.cpp
46984701
FILE: ../../../flutter/lib/web_ui/skwasm/helpers.h
4702+
FILE: ../../../flutter/lib/web_ui/skwasm/image.cpp
46994703
FILE: ../../../flutter/lib/web_ui/skwasm/paint.cpp
47004704
FILE: ../../../flutter/lib/web_ui/skwasm/path.cpp
47014705
FILE: ../../../flutter/lib/web_ui/skwasm/picture.cpp

lib/web_ui/lib/painting.dart

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ class MaskFilter {
398398
String toString() => 'MaskFilter.blur($_style, ${_sigma.toStringAsFixed(1)})';
399399
}
400400

401+
// This needs to be kept in sync with the "_FilterQuality" enum in skwasm's canvas.cpp
401402
enum FilterQuality {
402403
none,
403404
low,
@@ -438,16 +439,19 @@ enum ColorSpace {
438439
extendedSRGB,
439440
}
440441

442+
// This must be kept in sync with the `ImageByteFormat` enum in Skwasm's surface.cpp.
441443
enum ImageByteFormat {
442444
rawRgba,
443445
rawStraightRgba,
444446
rawUnmodified,
445447
png,
446448
}
447449

450+
// This must be kept in sync with the `PixelFormat` enum in Skwasm's image.cpp.
448451
enum PixelFormat {
449452
rgba8888,
450453
bgra8888,
454+
rgbaFloat32,
451455
}
452456

453457
typedef ImageDecoderCallback = void Function(Image result);
@@ -561,6 +565,8 @@ Future<Codec> createBmp(
561565
swapRedBlue = true;
562566
case PixelFormat.rgba8888:
563567
swapRedBlue = false;
568+
case PixelFormat.rgbaFloat32:
569+
throw UnimplementedError('RGB conversion from rgbaFloat32 data is not implemented');
564570
}
565571

566572
// See https://en.wikipedia.org/wiki/BMP_file_format for format examples.
@@ -737,9 +743,19 @@ class Shadow {
737743
}
738744

739745
abstract class ImageShader implements Shader {
740-
factory ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4, {
746+
factory ImageShader(
747+
Image image,
748+
TileMode tmx,
749+
TileMode tmy,
750+
Float64List matrix4, {
741751
FilterQuality? filterQuality,
742-
}) => engine.renderer.createImageShader(image, tmx, tmy, matrix4, filterQuality);
752+
}) => engine.renderer.createImageShader(
753+
image,
754+
tmx,
755+
tmy,
756+
matrix4,
757+
filterQuality
758+
);
743759

744760
@override
745761
void dispose();

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ void skiaDecodeImageFromPixels(
6262
}
6363

6464
if (targetWidth != null || targetHeight != null) {
65-
if (!validUpscale(allowUpscaling, targetWidth, targetHeight, width, height)) {
66-
domWindow.console.warn('Cannot apply targetWidth/targetHeight when allowUpscaling is false.');
67-
} else {
65+
if (validUpscale(allowUpscaling, targetWidth, targetHeight, width, height)) {
6866
return callback(scaleImage(skImage, targetWidth, targetHeight));
6967
}
7068
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export 'skwasm_impl/picture.dart';
2121
export 'skwasm_impl/raw/raw_canvas.dart';
2222
export 'skwasm_impl/raw/raw_fonts.dart';
2323
export 'skwasm_impl/raw/raw_geometry.dart';
24+
export 'skwasm_impl/raw/raw_image.dart';
2425
export 'skwasm_impl/raw/raw_memory.dart';
2526
export 'skwasm_impl/raw/raw_paint.dart';
2627
export 'skwasm_impl/raw/raw_path.dart';

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

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,21 +183,51 @@ class SkwasmCanvas implements ui.Canvas {
183183
}
184184

185185
@override
186-
void drawImage(ui.Image uiImage, ui.Offset offset, ui.Paint uiPaint) {
187-
throw UnimplementedError();
188-
}
186+
void drawImage(ui.Image image, ui.Offset offset, ui.Paint paint) =>
187+
canvasDrawImage(
188+
_handle,
189+
(image as SkwasmImage).handle,
190+
offset.dx,
191+
offset.dy,
192+
(paint as SkwasmPaint).handle,
193+
paint.filterQuality.index,
194+
);
189195

190196
@override
191197
void drawImageRect(
192-
ui.Image uiImage, ui.Rect src, ui.Rect dst, ui.Paint uiPaint) {
193-
throw UnimplementedError();
194-
}
198+
ui.Image image,
199+
ui.Rect src,
200+
ui.Rect dst,
201+
ui.Paint paint) => withStackScope((StackScope scope) {
202+
final Pointer<Float> sourceRect = scope.convertRectToNative(src);
203+
final Pointer<Float> destRect = scope.convertRectToNative(dst);
204+
canvasDrawImageRect(
205+
_handle,
206+
(image as SkwasmImage).handle,
207+
sourceRect,
208+
destRect,
209+
(paint as SkwasmPaint).handle,
210+
paint.filterQuality.index,
211+
);
212+
});
195213

196214
@override
197215
void drawImageNine(
198-
ui.Image uiImage, ui.Rect center, ui.Rect dst, ui.Paint uiPaint) {
199-
throw UnimplementedError();
200-
}
216+
ui.Image image,
217+
ui.Rect center,
218+
ui.Rect dst,
219+
ui.Paint paint) => withStackScope((StackScope scope) {
220+
final Pointer<Int32> centerRect = scope.convertIRectToNative(center);
221+
final Pointer<Float> destRect = scope.convertRectToNative(dst);
222+
canvasDrawImageNine(
223+
_handle,
224+
(image as SkwasmImage).handle,
225+
centerRect,
226+
destRect,
227+
(paint as SkwasmPaint).handle,
228+
paint.filterQuality.index,
229+
);
230+
});
201231

202232
@override
203233
void drawPicture(ui.Picture picture) {

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

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,67 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:ffi';
56
import 'dart:typed_data';
67

8+
import 'package:ui/src/engine.dart';
9+
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
710
import 'package:ui/ui.dart' as ui;
811

912
class SkwasmImage implements ui.Image {
10-
@override
11-
int get width {
12-
throw UnimplementedError();
13+
SkwasmImage(this.handle);
14+
15+
factory SkwasmImage.fromPixels(
16+
Uint8List pixels,
17+
int width,
18+
int height,
19+
ui.PixelFormat format, {
20+
int? rowBytes,
21+
}) {
22+
final SkDataHandle dataHandle = skDataCreate(pixels.length);
23+
final Pointer<Uint8> dataPointer = skDataGetPointer(dataHandle).cast<Uint8>();
24+
for (int i = 0; i < pixels.length; i++) {
25+
dataPointer[i] = pixels[i];
26+
}
27+
final ImageHandle imageHandle = imageCreateFromPixels(
28+
dataHandle,
29+
width,
30+
height,
31+
format.index,
32+
rowBytes ?? 4 * width,
33+
);
34+
skDataDispose(dataHandle);
35+
return SkwasmImage(imageHandle);
1336
}
1437

38+
final ImageHandle handle;
39+
bool _isDisposed = false;
40+
1541
@override
16-
int get height {
17-
throw UnimplementedError();
18-
}
42+
int get width => imageGetWidth(handle);
43+
44+
@override
45+
int get height => imageGetHeight(handle);
1946

2047
@override
2148
Future<ByteData?> toByteData(
2249
{ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) {
23-
throw UnimplementedError();
50+
return (renderer as SkwasmRenderer).surface.rasterizeImage(this, format);
2451
}
2552

2653
@override
2754
ui.ColorSpace get colorSpace => ui.ColorSpace.sRGB;
2855

2956
@override
3057
void dispose() {
31-
throw UnimplementedError();
58+
if (!_isDisposed) {
59+
imageDispose(handle);
60+
_isDisposed = true;
61+
}
3262
}
3363

3464
@override
35-
bool get debugDisposed {
36-
throw UnimplementedError();
37-
}
65+
bool get debugDisposed => _isDisposed;
3866

3967
@override
4068
SkwasmImage clone() => this;

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

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,31 +76,31 @@ class SkwasmPaint implements ui.Paint {
7676
@override
7777
set strokeMiterLimit(double limit) => paintSetMiterLimit(_handle, limit);
7878

79-
// Unimplemented stuff below
8079
@override
81-
ui.ColorFilter? colorFilter;
80+
ui.Shader? get shader => _shader;
8281

8382
@override
84-
ui.FilterQuality filterQuality = ui.FilterQuality.none;
83+
set shader(ui.Shader? uiShader) {
84+
final SkwasmShader? skwasmShader = uiShader as SkwasmShader?;
85+
_shader = skwasmShader;
86+
final ShaderHandle shaderHandle =
87+
skwasmShader != null ? skwasmShader.handle : nullptr;
88+
paintSetShader(_handle, shaderHandle);
89+
}
8590

8691
@override
87-
ui.ImageFilter? imageFilter;
92+
ui.FilterQuality filterQuality = ui.FilterQuality.none;
8893

94+
// Unimplemented stuff below
8995
@override
90-
bool invertColors = false;
96+
ui.ColorFilter? colorFilter;
9197

9298
@override
93-
ui.MaskFilter? maskFilter;
99+
ui.ImageFilter? imageFilter;
94100

95101
@override
96-
ui.Shader? get shader => _shader;
102+
bool invertColors = false;
97103

98104
@override
99-
set shader(ui.Shader? uiShader) {
100-
final SkwasmShader? skwasmShader = uiShader as SkwasmShader?;
101-
_shader = skwasmShader;
102-
final ShaderHandle shaderHandle =
103-
skwasmShader != null ? skwasmShader.handle : nullptr;
104-
paintSetShader(_handle, shaderHandle);
105-
}
105+
ui.MaskFilter? maskFilter;
106106
}

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ class SkwasmPicture implements ui.Picture {
1212
PictureHandle get handle => _handle;
1313

1414
@override
15-
Future<ui.Image> toImage(int width, int height) {
16-
throw UnimplementedError();
17-
}
15+
Future<ui.Image> toImage(int width, int height) async => toImageSync(width, height);
1816

1917
@override
2018
void dispose() {
@@ -30,10 +28,8 @@ class SkwasmPicture implements ui.Picture {
3028
bool debugDisposed = false;
3129

3230
@override
33-
ui.Image toImageSync(int width, int height) {
34-
// TODO(jacksongardner): implement toImageSync
35-
throw UnimplementedError();
36-
}
31+
ui.Image toImageSync(int width, int height) =>
32+
SkwasmImage(imageCreateFromPicture(handle, width, height));
3733

3834
ui.Rect get cullRect {
3935
return withStackScope((StackScope s) {

lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import 'dart:ffi';
99

1010
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
1111

12-
final class CanvasWrapper extends Opaque {}
12+
final class RawCanvas extends Opaque {}
1313

14-
typedef CanvasHandle = Pointer<CanvasWrapper>;
14+
typedef CanvasHandle = Pointer<RawCanvas>;
1515

1616
@Native<Void Function(CanvasHandle)>(symbol: 'canvas_destroy', isLeaf: true)
1717
external void canvasDestroy(CanvasHandle canvas);
@@ -126,6 +126,57 @@ external void canvasDrawPath(
126126
symbol: 'canvas_drawPicture', isLeaf: true)
127127
external void canvasDrawPicture(CanvasHandle canvas, PictureHandle picture);
128128

129+
@Native<Void Function(
130+
CanvasHandle,
131+
ImageHandle,
132+
Float,
133+
Float,
134+
PaintHandle,
135+
Int
136+
)>(symbol: 'canvas_drawImage', isLeaf: true)
137+
external void canvasDrawImage(
138+
CanvasHandle handle,
139+
ImageHandle image,
140+
double offsetX,
141+
double offsetY,
142+
PaintHandle paint,
143+
int filterQuality,
144+
);
145+
146+
@Native<Void Function(
147+
CanvasHandle,
148+
ImageHandle,
149+
Pointer<Float>,
150+
Pointer<Float>,
151+
PaintHandle,
152+
Int,
153+
)>(symbol: 'canvas_drawImageRect', isLeaf: true)
154+
external void canvasDrawImageRect(
155+
CanvasHandle handle,
156+
ImageHandle image,
157+
Pointer<Float> sourceRect,
158+
Pointer<Float> destRect,
159+
PaintHandle paint,
160+
int filterQuality,
161+
);
162+
163+
@Native<Void Function(
164+
CanvasHandle,
165+
ImageHandle,
166+
Pointer<Int32>,
167+
Pointer<Float>,
168+
PaintHandle,
169+
Int,
170+
)>(symbol: 'canvas_drawImageNine', isLeaf: true)
171+
external void canvasDrawImageNine(
172+
CanvasHandle handle,
173+
ImageHandle image,
174+
Pointer<Int32> centerRect,
175+
Pointer<Float> destRect,
176+
PaintHandle paint,
177+
int filterQuality,
178+
);
179+
129180
@Native<Void Function(CanvasHandle, PathHandle, Float, Float, Int32, Bool)>(
130181
symbol: 'canvas_drawShadow', isLeaf: true)
131182
external void canvasDrawShadow(

0 commit comments

Comments
 (0)