[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:
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ wasm_lib("skwasm") {
|
||||
"text/paragraph_style.cpp",
|
||||
"text/strut_style.cpp",
|
||||
"text/text_style.cpp",
|
||||
"vertices.cpp",
|
||||
"wrappers.h",
|
||||
]
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
23
engine/src/flutter/lib/web_ui/skwasm/vertices.cpp
Normal file
23
engine/src/flutter/lib/web_ui/skwasm/vertices.cpp
Normal 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();
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>[
|
||||
Reference in New Issue
Block a user