blob: cdaa6b433674c51db39dc5e20ad706abb33cc0f8 [file] [log] [blame]
// 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:async';
import 'dart:html' as html;
import 'dart:js_util' as js_util;
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' hide TextStyle, ImageShader;
import 'package:web_engine_tester/golden_tester.dart';
void main() {
internalBootstrapBrowserTest(() => testMain);
}
Future<void> testMain() async {
const double screenWidth = 600.0;
const double screenHeight = 800.0;
const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight);
// Commit a recording canvas to a bitmap, and compare with the expected
Future<void> _checkScreenshot(RecordingCanvas rc, String fileName,
{Rect region = const Rect.fromLTWH(0, 0, 500, 500),
double maxDiffRatePercent = 0.0,
bool write = false}) async {
final EngineCanvas engineCanvas =
BitmapCanvas(screenRect, RenderStrategy());
rc.endRecording();
rc.apply(engineCanvas, screenRect);
// Wrap in <flt-scene> so that our CSS selectors kick in.
final html.Element sceneElement = html.Element.tag('flt-scene');
try {
sceneElement.append(engineCanvas.rootElement);
html.document.body!.append(sceneElement);
await matchGoldenFile(
'$fileName.png',
region: region,
write: write,
maxDiffRatePercent: maxDiffRatePercent,
);
} finally {
// The page is reused across tests, so remove the element after taking the
// golden screenshot.
sceneElement.remove();
}
}
setUp(() async {
debugEmulateFlutterTesterEnvironment = true;
disposeWebGl();
await webOnlyInitializePlatform();
webOnlyFontCollection.debugRegisterTestFonts();
await webOnlyFontCollection.ensureFontsLoaded();
});
Future<void> _testVertices(
String fileName, Vertices vertices, BlendMode blendMode, Paint paint,
{bool write = false}) async {
final RecordingCanvas rc =
RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500));
rc.drawVertices(
vertices as SurfaceVertices, blendMode, paint as SurfacePaint);
await _checkScreenshot(rc, fileName, write: write);
}
test('Should draw green hairline triangles when colors array is null.',
() async {
final Vertices vertices = Vertices.raw(
VertexMode.triangles,
Float32List.fromList(<double>[
20.0,
20.0,
220.0,
10.0,
110.0,
220.0,
220.0,
320.0,
20.0,
310.0,
200.0,
420.0
]));
await _testVertices('draw_vertices_hairline_triangle', vertices,
BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0));
});
test(
'Should draw black hairline triangles when colors array is null'
' and Paint() has no color.', () async {
// ignore: unused_local_variable
final Int32List colors = Int32List.fromList(<int>[
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF
]);
final Vertices vertices = Vertices.raw(
VertexMode.triangles,
Float32List.fromList(<double>[
20.0,
20.0,
220.0,
10.0,
110.0,
220.0,
220.0,
320.0,
20.0,
310.0,
200.0,
420.0
]));
await _testVertices('draw_vertices_hairline_triangle_black', vertices,
BlendMode.srcOver, Paint());
});
/// Regression test for https://github.com/flutter/flutter/issues/71442.
test(
'Should draw filled triangles when colors array is null'
' and Paint() has color.', () async {
// ignore: unused_local_variable
final Int32List colors = Int32List.fromList(<int>[
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF
]);
final Vertices vertices = Vertices.raw(
VertexMode.triangles,
Float32List.fromList(<double>[
20.0,
20.0,
220.0,
10.0,
110.0,
220.0,
220.0,
320.0,
20.0,
310.0,
200.0,
420.0
]));
await _testVertices(
'draw_vertices_triangle_green_filled',
vertices,
BlendMode.srcOver,
Paint()
..style = PaintingStyle.fill
..color = const Color(0xFF00FF00));
});
test('Should draw hairline triangleFan.', () async {
final Vertices vertices = Vertices.raw(
VertexMode.triangleFan,
Float32List.fromList(<double>[
150.0,
150.0,
20.0,
10.0,
80.0,
20.0,
220.0,
15.0,
280.0,
30.0,
300.0,
420.0
]));
await _testVertices('draw_vertices_hairline_triangle_fan', vertices,
BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0));
});
test('Should draw hairline triangleStrip.', () async {
final Vertices vertices = Vertices.raw(
VertexMode.triangleStrip,
Float32List.fromList(<double>[
20.0,
20.0,
220.0,
10.0,
110.0,
220.0,
220.0,
320.0,
20.0,
310.0,
200.0,
420.0
]));
await _testVertices('draw_vertices_hairline_triangle_strip', vertices,
BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0));
});
test('Should draw triangles with colors.', () async {
final Int32List colors = Int32List.fromList(<int>[
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF
]);
final Vertices vertices = Vertices.raw(
VertexMode.triangles,
Float32List.fromList(<double>[
150.0,
150.0,
20.0,
10.0,
80.0,
20.0,
220.0,
15.0,
280.0,
30.0,
300.0,
420.0
]),
colors: colors);
await _testVertices('draw_vertices_triangles', vertices, BlendMode.srcOver,
Paint()..color = const Color.fromARGB(255, 0, 128, 0));
});
test('Should draw triangles with colors and indices.', () async {
final Int32List colors = Int32List.fromList(
<int>[0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFF0000, 0xFF0000FF]);
final Uint16List indices = Uint16List.fromList(<int>[0, 1, 2, 3, 4, 0]);
final RecordingCanvas rc =
RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500));
final Vertices vertices = Vertices.raw(
VertexMode.triangles,
Float32List.fromList(<double>[
210.0,
150.0,
30.0,
110.0,
80.0,
30.0,
220.0,
15.0,
280.0,
30.0,
]),
colors: colors,
indices: indices);
rc.drawVertices(
vertices as SurfaceVertices, BlendMode.srcOver, SurfacePaint());
await _checkScreenshot(rc, 'draw_vertices_triangles_indexed');
});
test('Should draw triangleFan with colors.', () async {
final Int32List colors = Int32List.fromList(<int>[
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF
]);
final Vertices vertices = Vertices.raw(
VertexMode.triangleFan,
Float32List.fromList(<double>[
150.0,
150.0,
20.0,
10.0,
80.0,
20.0,
220.0,
15.0,
280.0,
30.0,
300.0,
420.0
]),
colors: colors);
await _testVertices('draw_vertices_triangle_fan', vertices,
BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0));
});
test('Should draw triangleStrip with colors.', () async {
final Int32List colors = Int32List.fromList(<int>[
0xFFFF0000,
0xFF00FF00,
0xFF0000FF,
0xFFFF0000,
0xFF00FF00,
0xFF0000FF
]);
final Vertices vertices = Vertices.raw(
VertexMode.triangleStrip,
Float32List.fromList(<double>[
20.0,
20.0,
220.0,
10.0,
110.0,
220.0,
220.0,
320.0,
20.0,
310.0,
200.0,
420.0
]),
colors: colors);
await _testVertices('draw_vertices_triangle_strip', vertices,
BlendMode.srcOver, Paint()..color = const Color.fromARGB(255, 0, 128, 0));
});
Future<void> testTexture(TileMode tileMode, String filename) async {
final Uint16List indices = Uint16List.fromList(<int>[0, 1, 2, 3, 4, 0]);
final RecordingCanvas rc =
RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500));
final Vertices vertices = Vertices.raw(
VertexMode.triangles,
Float32List.fromList(<double>[
210.0,
150.0,
0.0,
0.0,
80.0,
30.0,
220.0,
15.0,
280.0,
30.0,
]),
indices: indices);
final Float32List matrix4 = Matrix4.identity().storage;
final HtmlImage img = await createTestImage();
final SurfacePaint paint = SurfacePaint();
final EngineImageShader imgShader = EngineImageShader(img, tileMode, tileMode,
Float64List.fromList(matrix4), FilterQuality.high);
paint.shader = imgShader;
rc.drawVertices(vertices as SurfaceVertices, BlendMode.srcOver, paint);
await _checkScreenshot(rc, filename, maxDiffRatePercent: 1.0);
}
test('Should draw triangle with texture and indices', () async {
await testTexture(TileMode.clamp, 'draw_vertices_texture');
});
test('Should draw triangle with texture and indices', () async {
await testTexture(TileMode.mirror, 'draw_vertices_texture_mirror');
});
test('Should draw triangle with texture and indices', () async {
await testTexture(TileMode.repeated, 'draw_vertices_texture_repeated');
});
}
Future<HtmlImage> createTestImage({int width = 50, int height = 40}) {
final html.CanvasElement canvas =
html.CanvasElement(width: width, height: height);
final html.CanvasRenderingContext2D ctx = canvas.context2D;
ctx.fillStyle = '#E04040';
ctx.fillRect(0, 0, width / 3, height);
ctx.fill();
ctx.fillStyle = '#40E080';
ctx.fillRect(width / 3, 0, width / 3, height);
ctx.fill();
ctx.fillStyle = '#2040E0';
ctx.fillRect(2 * width / 3, 0, width / 3, height);
ctx.fill();
final html.ImageElement imageElement = html.ImageElement();
final Completer<HtmlImage> completer = Completer<HtmlImage>();
imageElement.onLoad.listen((html.Event event) {
completer.complete(HtmlImage(imageElement, width, height));
});
imageElement.src = js_util.callMethod(canvas, 'toDataURL', <dynamic>[]);
return completer.future;
}