[Skwasm] Implement miscellaneous drawing APIs (flutter/engine#42324)

Implements `drawVertices`, `drawPoints`, and `drawAtlas`.

These are the last rendering APIs that are unimplemented in Skwasm! (Although we still need to add platform view support).
This commit is contained in:
Jackson Gardner
2023-05-25 14:53:23 -07:00
committed by GitHub
parent 61720177c6
commit 8719978821
16 changed files with 665 additions and 132 deletions

View File

@@ -2043,6 +2043,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_sh
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skdata.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_vertices.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_line_metrics.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart + ../../../flutter/LICENSE
@@ -2122,6 +2123,7 @@ ORIGIN: ../../../flutter/lib/web_ui/skwasm/text/paragraph_builder.cpp + ../../..
ORIGIN: ../../../flutter/lib/web_ui/skwasm/text/paragraph_style.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/text/strut_style.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/text/text_style.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/vertices.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/wrappers.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/runtime/dart_isolate.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/runtime/dart_isolate.h + ../../../flutter/LICENSE
@@ -4696,6 +4698,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shad
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skdata.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_vertices.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/skwasm_module.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_line_metrics.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart
@@ -4775,6 +4778,7 @@ FILE: ../../../flutter/lib/web_ui/skwasm/text/paragraph_builder.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/text/paragraph_style.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/text/strut_style.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/text/text_style.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/vertices.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/wrappers.h
FILE: ../../../flutter/runtime/dart_isolate.cc
FILE: ../../../flutter/runtime/dart_isolate.h

View File

@@ -34,6 +34,7 @@ export 'skwasm_impl/raw/raw_shaders.dart';
export 'skwasm_impl/raw/raw_skdata.dart';
export 'skwasm_impl/raw/raw_skstring.dart';
export 'skwasm_impl/raw/raw_surface.dart';
export 'skwasm_impl/raw/raw_vertices.dart';
export 'skwasm_impl/raw/skwasm_module.dart';
export 'skwasm_impl/raw/text/raw_line_metrics.dart';
export 'skwasm_impl/raw/text/raw_paragraph.dart';

View File

@@ -259,21 +259,47 @@ class SkwasmCanvas implements SceneCanvas {
@override
void drawPoints(
ui.PointMode pointMode, List<ui.Offset> points, ui.Paint paint) {
throw UnimplementedError();
}
ui.PointMode pointMode,
List<ui.Offset> points,
ui.Paint paint
) => withStackScope((StackScope scope) {
final RawPointArray rawPoints = scope.convertPointArrayToNative(points);
canvasDrawPoints(
_handle,
pointMode.index,
rawPoints,
points.length,
(paint as SkwasmPaint).handle,
);
});
@override
void drawRawPoints(
ui.PointMode pointMode, Float32List points, ui.Paint paint) {
throw UnimplementedError();
}
ui.PointMode pointMode,
Float32List points,
ui.Paint paint
) => withStackScope((StackScope scope) {
final RawPointArray rawPoints = scope.convertDoublesToNative(points);
canvasDrawPoints(
_handle,
pointMode.index,
rawPoints,
points.length ~/ 2,
(paint as SkwasmPaint).handle,
);
});
@override
void drawVertices(
ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) {
throw UnimplementedError();
}
ui.Vertices vertices,
ui.BlendMode blendMode,
ui.Paint paint,
) => canvasDrawVertices(
_handle,
(vertices as SkwasmVertices).handle,
blendMode.index,
(paint as SkwasmPaint).handle,
);
@override
void drawAtlas(
@@ -284,9 +310,27 @@ class SkwasmCanvas implements SceneCanvas {
ui.BlendMode? blendMode,
ui.Rect? cullRect,
ui.Paint paint,
) {
throw UnimplementedError();
}
) => withStackScope((StackScope scope) {
final RawRSTransformArray rawTransforms = scope.convertRSTransformsToNative(transforms);
final RawRect rawRects = scope.convertRectsToNative(rects);
final RawColorArray rawColors = colors != null
? scope.convertColorArrayToNative(colors)
: nullptr;
final RawRect rawCullRect = cullRect != null
? scope.convertRectToNative(cullRect)
: nullptr;
canvasDrawAtlas(
_handle,
(atlas as SkwasmImage).handle,
rawTransforms,
rawRects,
rawColors,
transforms.length,
(blendMode ?? ui.BlendMode.src).index,
rawCullRect,
(paint as SkwasmPaint).handle,
);
});
@override
void drawRawAtlas(
@@ -297,9 +341,27 @@ class SkwasmCanvas implements SceneCanvas {
ui.BlendMode? blendMode,
ui.Rect? cullRect,
ui.Paint paint,
) {
throw UnimplementedError();
}
) => withStackScope((StackScope scope) {
final RawRSTransformArray rawTransforms = scope.convertDoublesToNative(rstTransforms);
final RawRect rawRects = scope.convertDoublesToNative(rects);
final RawColorArray rawColors = colors != null
? scope.convertIntsToUint32Native(colors)
: nullptr;
final RawRect rawCullRect = cullRect != null
? scope.convertRectToNative(cullRect)
: nullptr;
canvasDrawAtlas(
_handle,
(atlas as SkwasmImage).handle,
rawTransforms,
rawRects,
rawColors,
rstTransforms.length ~/ 4,
(blendMode ?? ui.BlendMode.src).index,
rawCullRect,
(paint as SkwasmPaint).handle,
);
});
@override
void drawShadow(

View File

@@ -209,6 +209,57 @@ external void canvasDrawParagraph(
double y,
);
@Native<Void Function(
CanvasHandle,
VerticesHandle,
Int,
PaintHandle,
)>(symbol: 'canvas_drawVertices', isLeaf: true)
external void canvasDrawVertices(
CanvasHandle handle,
VerticesHandle vertices,
int blendMode,
PaintHandle paint,
);
@Native<Void Function(
CanvasHandle,
Int,
RawPointArray,
Int,
PaintHandle,
)>(symbol: 'canvas_drawPoints', isLeaf: true)
external void canvasDrawPoints(
CanvasHandle handle,
int pointMode,
RawPointArray points,
int pointCount,
PaintHandle paint,
);
@Native<Void Function(
CanvasHandle,
ImageHandle,
RawRSTransformArray,
RawRect,
RawColorArray,
Int,
Int,
RawRect,
PaintHandle,
)>(symbol: 'canvas_drawAtlas', isLeaf: true)
external void canvasDrawAtlas(
CanvasHandle handle,
ImageHandle atlas,
RawRSTransformArray transforms,
RawRect rects,
RawColorArray colors,
int spriteCount,
int blendMode,
RawRect cullRect,
PaintHandle paint,
);
@Native<Void Function(CanvasHandle, RawMatrix44)>(
symbol: 'canvas_getTransform', isLeaf: true)
external void canvasGetTransform(CanvasHandle canvas, RawMatrix44 outMatrix);

View File

@@ -8,6 +8,7 @@ typedef RawRect = Pointer<Float>;
typedef RawIRect = Pointer<Int32>;
typedef RawRRect = Pointer<Float>;
typedef RawPointArray = Pointer<Float>;
typedef RawRSTransformArray = Pointer<Float>;
typedef RawMatrix33 = Pointer<Float>;
typedef RawMatrix44 = Pointer<Float>;
typedef RawColorArray = Pointer<Uint32>;

View File

@@ -85,6 +85,18 @@ class StackScope {
return pointer;
}
Pointer<Float> convertRectsToNative(List<ui.Rect> rects) {
final Pointer<Float> pointer = allocFloatArray(rects.length * 4);
for (int i = 0; i < rects.length; i++) {
final ui.Rect rect = rects[i];
pointer[i * 4] = rect.left;
pointer[i * 4 + 1] = rect.top;
pointer[i * 4 + 2] = rect.right;
pointer[i * 4 + 3] = rect.bottom;
}
return pointer;
}
ui.Rect convertRectFromNative(Pointer<Float> buffer) {
return ui.Rect.fromLTRB(
buffer[0],
@@ -132,6 +144,18 @@ class StackScope {
return pointer;
}
Pointer<Float> convertRSTransformsToNative(List<ui.RSTransform> transforms) {
final Pointer<Float> pointer = allocFloatArray(transforms.length * 4);
for (int i = 0; i < transforms.length; i++) {
final ui.RSTransform transform = transforms[i];
pointer[i * 4] = transform.scos;
pointer[i * 4 + 1] = transform.ssin;
pointer[i * 4 + 2] = transform.tx;
pointer[i * 4 + 3] = transform.ty;
}
return pointer;
}
Pointer<Float> convertDoublesToNative(List<double> values) {
final Pointer<Float> pointer = allocFloatArray(values.length);
for (int i = 0; i < values.length; i++) {
@@ -140,6 +164,22 @@ class StackScope {
return pointer;
}
Pointer<Uint16> convertIntsToUint16Native(List<int> values) {
final Pointer<Uint16> pointer = allocUint16Array(values.length);
for (int i = 0; i < values.length; i++) {
pointer[i] = values[i];
}
return pointer;
}
Pointer<Uint32> convertIntsToUint32Native(List<int> values) {
final Pointer<Uint32> pointer = allocUint32Array(values.length);
for (int i = 0; i < values.length; i++) {
pointer[i] = values[i];
}
return pointer;
}
Pointer<Float> convertPointArrayToNative(List<ui.Offset> points) {
final Pointer<Float> pointer = allocFloatArray(points.length * 2);
for (int i = 0; i < points.length; i++) {
@@ -162,6 +202,11 @@ class StackScope {
return stackAlloc(length).cast<Int8>();
}
Pointer<Uint16> allocUint16Array(int count) {
final int length = count * sizeOf<Uint16>();
return stackAlloc(length).cast<Uint16>();
}
Pointer<Int32> allocInt32Array(int count) {
final int length = count * sizeOf<Int32>();
return stackAlloc(length).cast<Int32>();

View File

@@ -0,0 +1,35 @@
// 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.
@DefaultAsset('skwasm')
library skwasm_impl;
import 'dart:ffi';
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
final class RawVertices extends Opaque {}
typedef VerticesHandle = Pointer<RawVertices>;
@Native<VerticesHandle Function(
Int vertexMode,
Int vertexCount,
RawPointArray positions,
RawPointArray textureCoordinates,
RawColorArray colors,
Int indexCount,
Pointer<Uint16> indices,
)>(symbol: 'vertices_create', isLeaf: true)
external VerticesHandle verticesCreate(
int vertexMode,
int vertexCount,
RawPointArray positions,
RawPointArray textureCoordinates,
RawColorArray colors,
int indexCount,
Pointer<Uint16> indices,
);
@Native<Void Function(VerticesHandle)>(symbol: 'vertices_dispose', isLeaf: true)
external void verticesDispose(VerticesHandle handle);

View File

@@ -11,7 +11,6 @@ import 'package:ui/src/engine.dart';
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
import 'package:ui/ui.dart' as ui;
// TODO(jacksongardner): Actually implement skwasm renderer.
class SkwasmRenderer implements Renderer {
late DomCanvasElement sceneElement;
late SkwasmSurface surface;

View File

@@ -4,8 +4,10 @@
// ignore_for_file: avoid_unused_constructor_parameters
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
import 'package:ui/ui.dart' as ui;
class SkwasmVertices implements ui.Vertices {
@@ -15,9 +17,28 @@ class SkwasmVertices implements ui.Vertices {
List<ui.Offset>? textureCoordinates,
List<ui.Color>? colors,
List<int>? indices,
}) {
throw UnimplementedError();
}
}) => withStackScope((StackScope scope) {
final RawPointArray rawPositions = scope.convertPointArrayToNative(positions);
final RawPointArray rawTextureCoordinates = textureCoordinates != null
? scope.convertPointArrayToNative(textureCoordinates)
: nullptr;
final RawColorArray rawColors = colors != null
? scope.convertColorArrayToNative(colors)
: nullptr;
final Pointer<Uint16> rawIndices = indices != null
? scope.convertIntsToUint16Native(indices)
: nullptr;
final int indexCount = indices != null ? indices.length : 0;
return SkwasmVertices._(verticesCreate(
mode.index,
positions.length,
rawPositions,
rawTextureCoordinates,
rawColors,
indexCount,
rawIndices,
));
});
factory SkwasmVertices.raw(
ui.VertexMode mode,
@@ -25,14 +46,42 @@ class SkwasmVertices implements ui.Vertices {
Float32List? textureCoordinates,
Int32List? colors,
Uint16List? indices,
}) {
throw UnimplementedError();
}
}) => withStackScope((StackScope scope) {
final RawPointArray rawPositions = scope.convertDoublesToNative(positions);
final RawPointArray rawTextureCoordinates = textureCoordinates != null
? scope.convertDoublesToNative(textureCoordinates)
: nullptr;
final RawColorArray rawColors = colors != null
? scope.convertIntsToUint32Native(colors)
: nullptr;
final Pointer<Uint16> rawIndices = indices != null
? scope.convertIntsToUint16Native(indices)
: nullptr;
final int indexCount = indices != null ? indices.length : 0;
return SkwasmVertices._(verticesCreate(
mode.index,
positions.length ~/ 2,
rawPositions,
rawTextureCoordinates,
rawColors,
indexCount,
rawIndices,
));
});
SkwasmVertices._(this.handle);
final VerticesHandle handle;
bool _isDisposed = false;
@override
bool get debugDisposed => throw UnimplementedError();
bool get debugDisposed => _isDisposed;
@override
void dispose() {
if (!_isDisposed) {
verticesDispose(handle);
_isDisposed = true;
}
}
}

View File

@@ -27,6 +27,7 @@ wasm_lib("skwasm") {
"text/paragraph_style.cpp",
"text/strut_style.cpp",
"text/text_style.cpp",
"vertices.cpp",
"wrappers.h",
]

View File

@@ -7,6 +7,7 @@
#include "wrappers.h"
#include "third_party/skia/include/core/SkPoint3.h"
#include "third_party/skia/include/core/SkVertices.h"
#include "third_party/skia/include/utils/SkShadowUtils.h"
#include "third_party/skia/modules/skparagraph/include/Paragraph.h"
@@ -227,6 +228,34 @@ SKWASM_EXPORT void canvas_drawImageNine(SkCanvas* canvas,
filterModeForQuality(quality), paint);
}
SKWASM_EXPORT void canvas_drawVertices(SkCanvas* canvas,
SkVertices* vertices,
SkBlendMode mode,
SkPaint* paint) {
canvas->drawVertices(sk_ref_sp<SkVertices>(vertices), mode, *paint);
}
SKWASM_EXPORT void canvas_drawPoints(SkCanvas* canvas,
SkCanvas::PointMode mode,
SkPoint* points,
int pointCount,
SkPaint* paint) {
canvas->drawPoints(mode, pointCount, points, *paint);
}
SKWASM_EXPORT void canvas_drawAtlas(SkCanvas* canvas,
SkImage* atlas,
SkRSXform* transforms,
SkRect* rects,
SkColor* colors,
int spriteCount,
SkBlendMode mode,
SkRect* cullRect,
SkPaint* paint) {
canvas->drawAtlas(atlas, transforms, rects, colors, spriteCount, mode,
SkSamplingOptions{}, cullRect, paint);
}
SKWASM_EXPORT void canvas_getTransform(SkCanvas* canvas, SkM44* outTransform) {
*outTransform = canvas->getLocalToDevice();
}

View File

@@ -0,0 +1,23 @@
// 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.
#include "export.h"
#include "third_party/skia/include/core/SkVertices.h"
SKWASM_EXPORT SkVertices* vertices_create(SkVertices::VertexMode vertexMode,
int vertexCount,
SkPoint* positions,
SkPoint* textureCoordinates,
SkColor* colors,
int indexCount,
uint16_t* indices) {
return SkVertices::MakeCopy(vertexMode, vertexCount, positions,
textureCoordinates, colors, indexCount, indices)
.release();
}
SKWASM_EXPORT void vertices_dispose(SkVertices* vertices) {
vertices->unref();
}

View File

@@ -1,81 +0,0 @@
// 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 'dart:typed_data';
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart';
import 'package:web_engine_tester/golden_tester.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
}
Future<void> testMain() async {
const Rect region = Rect.fromLTWH(0, 0, 400, 600);
late BitmapCanvas canvas;
setUp(() {
canvas = BitmapCanvas(region, RenderStrategy());
});
tearDown(() {
canvas.rootElement.remove();
});
test('draws points in all 3 modes', () async {
final SurfacePaintData paint = SurfacePaintData();
paint.strokeWidth = 2.0;
paint.color = 0xFF0000FF;
final Float32List points = offsetListToFloat32List(const <Offset>[
Offset(10, 10),
Offset(50, 10),
Offset(70, 70),
Offset(170, 70)
]);
canvas.drawPoints(PointMode.points, points, paint);
final Float32List points2 = offsetListToFloat32List(const <Offset>[
Offset(10, 110),
Offset(50, 110),
Offset(70, 170),
Offset(170, 170)
]);
canvas.drawPoints(PointMode.lines, points2, paint);
final Float32List points3 = offsetListToFloat32List(const <Offset>[
Offset(10, 210),
Offset(50, 210),
Offset(70, 270),
Offset(170, 270)
]);
canvas.drawPoints(PointMode.polygon, points3, paint);
domDocument.body!.append(canvas.rootElement);
await matchGoldenFile('canvas_draw_points.png', region: region);
});
test('Should draw points with strokeWidth', () async {
final SurfacePaintData nullStrokePaint =
SurfacePaintData()..color = 0xffff0000;
canvas.drawPoints(PointMode.lines, Float32List.fromList(<double>[
30.0, 20.0, 200.0, 20.0]), nullStrokePaint);
final SurfacePaintData strokePaint1 = SurfacePaintData()
..strokeWidth = 1.0
..color = 0xff0000ff;
canvas.drawPoints(PointMode.lines, Float32List.fromList(<double>[
30.0, 30.0, 200.0, 30.0]), strokePaint1);
final SurfacePaintData strokePaint3 = SurfacePaintData()
..strokeWidth = 3.0
..color = 0xff00a000;
canvas.drawPoints(PointMode.lines, Float32List.fromList(<double>[
30.0, 40.0, 200.0, 40.0]), strokePaint3);
canvas.drawPoints(PointMode.points, Float32List.fromList(<double>[
30.0, 50.0, 40.0, 50.0, 50.0, 50.0]), strokePaint3);
domDocument.body!.append(canvas.rootElement);
await matchGoldenFile('canvas_draw_points_stroke.png', region: region);
});
}

View File

@@ -0,0 +1,119 @@
// 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 'dart:typed_data';
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;
import 'package:web_engine_tester/golden_tester.dart';
import '../common/test_initialization.dart';
import 'utils.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
}
Future<void> testMain() async {
setUpUnitTests(
setUpTestViewDimensions: false,
);
const ui.Rect region = ui.Rect.fromLTWH(0, 0, 400, 600);
late ui.PictureRecorder recorder;
late ui.Canvas canvas;
setUp(() {
recorder = ui.PictureRecorder();
canvas = ui.Canvas(recorder, region);
});
tearDown(() {
});
test('draws points in all 3 modes', () async {
final ui.Paint paint = ui.Paint();
paint.strokeWidth = 2.0;
paint.color = const ui.Color(0xFF00FF00);
const List<ui.Offset> points = <ui.Offset>[
ui.Offset(10, 10),
ui.Offset(50, 10),
ui.Offset(70, 70),
ui.Offset(170, 70)
];
canvas.drawPoints(ui.PointMode.points, points, paint);
const List<ui.Offset> points2 = <ui.Offset>[
ui.Offset(10, 110),
ui.Offset(50, 110),
ui.Offset(70, 170),
ui.Offset(170, 170)
];
canvas.drawPoints(ui.PointMode.lines, points2, paint);
const List<ui.Offset> points3 = <ui.Offset>[
ui.Offset(10, 210),
ui.Offset(50, 210),
ui.Offset(70, 270),
ui.Offset(170, 270)
];
canvas.drawPoints(ui.PointMode.polygon, points3, paint);
await drawPictureUsingCurrentRenderer(recorder.endRecording());
await matchGoldenFile('ui_canvas_draw_points.png', region: region);
});
test('draws raw points in all 3 modes', () async {
final ui.Paint paint = ui.Paint();
paint.strokeWidth = 2.0;
paint.color = const ui.Color(0xFF0000FF);
final Float32List points = offsetListToFloat32List(const <ui.Offset>[
ui.Offset(10, 10),
ui.Offset(50, 10),
ui.Offset(70, 70),
ui.Offset(170, 70)
]);
canvas.drawRawPoints(ui.PointMode.points, points, paint);
final Float32List points2 = offsetListToFloat32List(const <ui.Offset>[
ui.Offset(10, 110),
ui.Offset(50, 110),
ui.Offset(70, 170),
ui.Offset(170, 170)
]);
canvas.drawRawPoints(ui.PointMode.lines, points2, paint);
final Float32List points3 = offsetListToFloat32List(const <ui.Offset>[
ui.Offset(10, 210),
ui.Offset(50, 210),
ui.Offset(70, 270),
ui.Offset(170, 270)
]);
canvas.drawRawPoints(ui.PointMode.polygon, points3, paint);
await drawPictureUsingCurrentRenderer(recorder.endRecording());
await matchGoldenFile('ui_canvas_draw_raw_points.png', region: region);
});
test('Should draw points with strokeWidth', () async {
final ui.Paint nullStrokePaint =
ui.Paint()..color = const ui.Color(0xffff0000);
canvas.drawRawPoints(ui.PointMode.lines, Float32List.fromList(<double>[
30.0, 20.0, 200.0, 20.0]), nullStrokePaint);
final ui.Paint strokePaint1 = ui.Paint()
..strokeWidth = 1.0
..color = const ui.Color(0xff0000ff);
canvas.drawRawPoints(ui.PointMode.lines, Float32List.fromList(<double>[
30.0, 30.0, 200.0, 30.0]), strokePaint1);
final ui.Paint strokePaint3 = ui.Paint()
..strokeWidth = 3.0
..color = const ui.Color(0xff00a000);
canvas.drawRawPoints(ui.PointMode.lines, Float32List.fromList(<double>[
30.0, 40.0, 200.0, 40.0]), strokePaint3);
canvas.drawRawPoints(ui.PointMode.points, Float32List.fromList(<double>[
30.0, 50.0, 40.0, 50.0, 50.0, 50.0]), strokePaint3);
await drawPictureUsingCurrentRenderer(recorder.endRecording());
await matchGoldenFile('ui_canvas_draw_points_stroke.png', region: region);
});
}

View File

@@ -0,0 +1,203 @@
// 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 'dart:math' as math;
import 'dart:typed_data';
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/ui.dart' as ui;
import 'package:web_engine_tester/golden_tester.dart';
import '../common/test_initialization.dart';
import 'utils.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
}
const ui.Rect kBlueSquareRegion = ui.Rect.fromLTRB(0, 0, 25, 25);
const ui.Rect kRedCircleRegion = ui.Rect.fromLTRB(25, 0, 50, 25);
const ui.Rect kMagentaStarRegion = ui.Rect.fromLTRB(0, 25, 30, 55);
const ui.Rect kGreenSquiggleRegion = ui.Rect.fromLTRB(30, 25, 50, 55);
const ui.Rect kTotalAtlasRegion = ui.Rect.fromLTRB(0, 0, 55, 55);
ui.Image generateAtlas() {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.drawColor(const ui.Color(0), ui.BlendMode.src);
// Draw the square
canvas.save();
canvas.clipRect(kBlueSquareRegion);
canvas.drawPaint(ui.Paint()..color = const ui.Color(0xFF0000FF));
canvas.restore();
// Draw the circle
canvas.save();
canvas.clipRect(kRedCircleRegion);
canvas.drawCircle(kRedCircleRegion.center, kRedCircleRegion.width / 2.0, ui.Paint()..color = const ui.Color(0xFFFF0000));
canvas.restore();
// Draw the star
canvas.save();
canvas.clipRect(kMagentaStarRegion);
final ui.Offset starCenter = kMagentaStarRegion.center;
final double radius = kMagentaStarRegion.height / 2.0;
// Start at the top (rotated 90 degrees)
double theta = -math.pi / 2.0;
// Rotate two fifths of the circle each time
const double rotation = 4.0 * math.pi / 5.0;
final ui.Path starPath = ui.Path();
starPath.moveTo(
starCenter.dx + radius * math.cos(theta),
starCenter.dy + radius * math.sin(theta)
);
for (int i = 0; i < 5; i++) {
theta += rotation;
starPath.lineTo(
starCenter.dx + radius * math.cos(theta),
starCenter.dy + radius * math.sin(theta)
);
}
canvas.drawPath(
starPath,
ui.Paint()
..color = const ui.Color(0xFFFF00FF)
..style = ui.PaintingStyle.fill
);
canvas.restore();
// Draw the Squiggle
canvas.save();
canvas.clipRect(kGreenSquiggleRegion);
final ui.Path squigglePath = ui.Path();
squigglePath.moveTo(kGreenSquiggleRegion.topCenter.dx, kGreenSquiggleRegion.topCenter.dy);
squigglePath.cubicTo(
kGreenSquiggleRegion.left - 10.0, kGreenSquiggleRegion.top + kGreenSquiggleRegion.height * 0.33,
kGreenSquiggleRegion.right + 10.0, kGreenSquiggleRegion.top + kGreenSquiggleRegion.height * 0.66,
kGreenSquiggleRegion.bottomCenter.dx, kGreenSquiggleRegion.bottomCenter.dy
);
canvas.drawPath(
squigglePath,
ui.Paint()
..color = const ui.Color(0xFF00FF00)
..style = ui.PaintingStyle.stroke
..strokeWidth = 5.0
);
canvas.restore();
final ui.Picture picture = recorder.endRecording();
return picture.toImageSync(
kTotalAtlasRegion.width.toInt(),
kTotalAtlasRegion.height.toInt()
);
}
Future<void> testMain() async {
setUpUnitTests(
setUpTestViewDimensions: false,
);
const ui.Rect region = ui.Rect.fromLTWH(0, 0, 300, 300);
test('drawAtlas', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder, region);
final ui.Image atlas = generateAtlas();
final List<ui.RSTransform> transforms = List<ui.RSTransform>.generate(
12, (int index) {
const double radius = 100;
const double rotation = math.pi / 6.0;
final double angle = rotation * index;
final double scos = math.sin(angle);
final double ssin = math.cos(angle);
return ui.RSTransform(
scos,
ssin,
region.center.dx + radius * scos,
region.center.dy + radius * ssin,
);
}
);
final List<ui.Rect> rects = <ui.Rect>[
kBlueSquareRegion,
kRedCircleRegion,
kMagentaStarRegion,
kGreenSquiggleRegion,
kBlueSquareRegion,
kRedCircleRegion,
kMagentaStarRegion,
kGreenSquiggleRegion,
kBlueSquareRegion,
kRedCircleRegion,
kMagentaStarRegion,
kGreenSquiggleRegion,
];
canvas.drawAtlas(
atlas, transforms, rects, null, null, null, ui.Paint());
await drawPictureUsingCurrentRenderer(recorder.endRecording());
await matchGoldenFile('ui_draw_atlas.png', region: region);
}, skip: isHtml); // HTML renderer doesn't support drawAtlas
test('drawAtlasRaw', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder, region);
final ui.Image atlas = generateAtlas();
final Float32List transforms = Float32List(12 * 4);
for (int i = 0; i < 12; i++) {
const double radius = 100;
const double rotation = math.pi / 6.0;
final double angle = rotation * i;
final double scos = math.sin(angle);
final double ssin = math.cos(angle);
transforms[i * 4] = scos;
transforms[i * 4 + 1] = ssin;
transforms[i * 4 + 2] = region.center.dx + radius * scos;
transforms[i * 4 + 3] = region.center.dy + radius * ssin;
}
final List<ui.Rect> rects = <ui.Rect>[
kBlueSquareRegion,
kRedCircleRegion,
kMagentaStarRegion,
kGreenSquiggleRegion,
kBlueSquareRegion,
kRedCircleRegion,
kMagentaStarRegion,
kGreenSquiggleRegion,
kBlueSquareRegion,
kRedCircleRegion,
kMagentaStarRegion,
kGreenSquiggleRegion,
];
final Float32List rawRects = Float32List(rects.length * 4);
for (int i = 0; i < rects.length; i++) {
rawRects[i * 4] = rects[i].left;
rawRects[i * 4 + 1] = rects[i].top;
rawRects[i * 4 + 2] = rects[i].right;
rawRects[i * 4 + 3] = rects[i].bottom;
}
final Int32List colors = Int32List(12);
for (int i = 0; i < 12; i++) {
final int rgb = 0xFF << (8 * (i % 3));
colors[i] = 0xFF000000 | rgb;
}
canvas.drawRawAtlas(
atlas,
transforms,
rawRects,
colors,
ui.BlendMode.dstIn,
null,
ui.Paint()
);
await drawPictureUsingCurrentRenderer(recorder.endRecording());
await matchGoldenFile('ui_draw_atlas_raw.png', region: region);
}, skip: isHtml); // HTML renderer doesn't support drawAtlas
}

View File

@@ -6,12 +6,11 @@ import 'dart:typed_data';
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;
import 'package:web_engine_tester/golden_tester.dart';
import '../common/matchers.dart';
import 'common.dart';
import '../common/test_initialization.dart';
import 'utils.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
@@ -19,56 +18,49 @@ void main() {
void testMain() {
group('Vertices', () {
setUpCanvasKitTest();
setUpUnitTests(setUpTestViewDimensions: false);
test('can be constructed, drawn, and disposed of', () {
final CkVertices vertices = _testVertices();
expect(vertices, isA<CkVertices>());
expect(vertices.skiaObject, isNotNull);
final ui.Vertices vertices = _testVertices();
expect(vertices.debugDisposed, isFalse);
final CkPictureRecorder recorder = CkPictureRecorder();
final CkCanvas canvas =
recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 100, 100));
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(
recorder,
const ui.Rect.fromLTRB(0, 0, 100, 100)
);
canvas.drawVertices(
vertices,
ui.BlendMode.srcOver,
CkPaint(),
ui.Paint(),
);
vertices.dispose();
expect(vertices.debugDisposed, isTrue);
expect(() => vertices.skiaObject, throwsA(isAssertionError));
});
});
test('Vertices are not anti-aliased by default', () async {
const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 500);
final CkPictureRecorder recorder = CkPictureRecorder();
final CkCanvas canvas = recorder.beginRecording(region);
final CkVertices vertices = ui.Vertices.raw(
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder, region);
final ui.Vertices vertices = ui.Vertices.raw(
ui.VertexMode.triangles,
Float32List.fromList(_circularVertices),
indices: Uint16List.fromList(_circularVertexIndices),
) as CkVertices;
);
canvas.scale(3.0, 3.0);
canvas.drawVertices(
vertices,
ui.BlendMode.srcOver,
CkPaint()..color = const ui.Color(0xFFFF0000),
ui.Paint()..color = const ui.Color(0xFFFF0000),
);
final CkPicture verticesPicture = recorder.endRecording();
final LayerSceneBuilder builder = LayerSceneBuilder();
builder.pushOffset(0, 0);
builder.addPicture(ui.Offset.zero, verticesPicture);
CanvasKitRenderer.instance.rasterizer
.draw(builder.build().layerTree);
await matchGoldenFile('canvaskit_vertices_antialiased.png', region: region);
}, skip: isSafari);
await drawPictureUsingCurrentRenderer(recorder.endRecording());
await matchGoldenFile('ui_vertices_antialiased.png', region: region);
}, skip: isHtml); // https://github.com/flutter/flutter/issues/127454
}
CkVertices _testVertices() {
ui.Vertices _testVertices() {
return ui.Vertices(
ui.VertexMode.triangles,
const <ui.Offset>[
@@ -87,7 +79,7 @@ CkVertices _testVertices() {
ui.Color.fromRGBO(0, 0, 255, 1.0),
],
indices: <int>[0, 1, 2],
) as CkVertices;
);
}
const List<double> _circularVertices = <double>[