blob: 053cd9c155b6b0e91d7ed67d373f2f1a3183094b [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:math';
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;
void main() {
internalBootstrapBrowserTest(() => testMain);
}
Future<void> testMain() async {
await ui.webOnlyInitializePlatform();
runCanvasTests(deviceClipRoundsOut: false);
}
void runCanvasTests({required bool deviceClipRoundsOut}) {
setUp(() {
EngineSemanticsOwner.debugResetSemantics();
});
group('ui.Canvas transform tests', () {
void transformsClose(Float64List value, Float64List expected) {
expect(expected.length, equals(16));
expect(value.length, equals(16));
for (int r = 0; r < 4; r++) {
for (int c = 0; c < 4; c++) {
expect(value[r*4 + c], closeTo(expected[r*4 + c], 1e-10));
}
}
}
void transformsNotClose(Float64List value, Float64List expected) {
// We check the lengths here even though [transformsClose] will
// check them so that the [TestFailure] we catch below can only
// be due to a difference in matrix values.
expect(expected.length, equals(16));
expect(value.length, equals(16));
try {
transformsClose(value, expected);
} on TestFailure {
return;
}
throw TestFailure('transforms were too close to equal'); // ignore: only_throw_errors
}
test('ui.Canvas.translate affects canvas.getTransform', () {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.translate(12, 14.5);
final Float64List matrix = Matrix4.translationValues(12, 14.5, 0).toFloat64();
final Float64List curMatrix = canvas.getTransform();
transformsClose(curMatrix, matrix);
canvas.translate(10, 10);
final Float64List newCurMatrix = canvas.getTransform();
transformsNotClose(newCurMatrix, matrix);
transformsClose(curMatrix, matrix);
});
test('ui.Canvas.scale affects canvas.getTransform', () {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.scale(12, 14.5);
final Float64List matrix = Matrix4.diagonal3Values(12, 14.5, 1).toFloat64();
final Float64List curMatrix = canvas.getTransform();
transformsClose(curMatrix, matrix);
canvas.scale(10, 10);
final Float64List newCurMatrix = canvas.getTransform();
transformsNotClose(newCurMatrix, matrix);
transformsClose(curMatrix, matrix);
});
test('Canvas.rotate affects canvas.getTransform', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.rotate(pi);
final Float64List matrix = Matrix4.rotationZ(pi).toFloat64();
final Float64List curMatrix = canvas.getTransform();
transformsClose(curMatrix, matrix);
canvas.rotate(pi / 2);
final Float64List newCurMatrix = canvas.getTransform();
transformsNotClose(newCurMatrix, matrix);
transformsClose(curMatrix, matrix);
});
test('Canvas.skew affects canvas.getTransform', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.skew(12, 14.5);
final Float64List matrix = (Matrix4.identity()..setEntry(0, 1, 12)..setEntry(1, 0, 14.5)).toFloat64();
final Float64List curMatrix = canvas.getTransform();
transformsClose(curMatrix, matrix);
canvas.skew(10, 10);
final Float64List newCurMatrix = canvas.getTransform();
transformsNotClose(newCurMatrix, matrix);
transformsClose(curMatrix, matrix);
});
test('Canvas.transform affects canvas.getTransform', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
final Float64List matrix = (Matrix4.identity()..translate(12.0, 14.5)..scale(12.0, 14.5)).toFloat64();
canvas.transform(matrix);
final Float64List curMatrix = canvas.getTransform();
transformsClose(curMatrix, matrix);
canvas.translate(10, 10);
final Float64List newCurMatrix = canvas.getTransform();
transformsNotClose(newCurMatrix, matrix);
transformsClose(curMatrix, matrix);
});
});
void rectsClose(ui.Rect value, ui.Rect expected) {
expect(value.left, closeTo(expected.left, 1e-6));
expect(value.top, closeTo(expected.top, 1e-6));
expect(value.right, closeTo(expected.right, 1e-6));
expect(value.bottom, closeTo(expected.bottom, 1e-6));
}
void rectsNotClose(ui.Rect value, ui.Rect expected) {
try {
rectsClose(value, expected);
} on TestFailure {
return;
}
throw TestFailure('transforms were too close to equal'); // ignore: only_throw_errors
}
group('ui.Canvas clip tests', () {
test('Canvas.clipRect affects canvas.getClipBounds', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100));
const ui.Rect clipRawBounds = ui.Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
const ui.Rect clipExpandedBounds = ui.Rect.fromLTRB(10, 11, 21, 26);
final ui.Rect clipDestBounds = deviceClipRoundsOut ? clipExpandedBounds : clipRawBounds;
canvas.clipRect(clipRawBounds);
// Save initial return values for testing restored values
final ui.Rect initialLocalBounds = canvas.getLocalClipBounds();
final ui.Rect initialDestinationBounds = canvas.getDestinationClipBounds();
rectsClose(initialLocalBounds, clipExpandedBounds);
rectsClose(initialDestinationBounds, clipDestBounds);
canvas.save();
canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 15, 15));
// Both clip bounds have changed
rectsNotClose(canvas.getLocalClipBounds(), clipExpandedBounds);
rectsNotClose(canvas.getDestinationClipBounds(), clipDestBounds);
// Previous return values have not changed
rectsClose(initialLocalBounds, clipExpandedBounds);
rectsClose(initialDestinationBounds, clipDestBounds);
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
canvas.save();
canvas.scale(2, 2);
const ui.Rect scaledExpandedBounds = ui.Rect.fromLTRB(5, 5.5, 10.5, 13);
rectsClose(canvas.getLocalClipBounds(), scaledExpandedBounds);
// Destination bounds are unaffected by transform
rectsClose(canvas.getDestinationClipBounds(), clipDestBounds);
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
});
test('Canvas.clipRRect affects canvas.getClipBounds', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100));
const ui.Rect clipRawBounds = ui.Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
const ui.Rect clipExpandedBounds = ui.Rect.fromLTRB(10, 11, 21, 26);
final ui.Rect clipDestBounds = deviceClipRoundsOut ? clipExpandedBounds : clipRawBounds;
final ui.RRect clip = ui.RRect.fromRectAndRadius(clipRawBounds, const ui.Radius.circular(3));
canvas.clipRRect(clip);
// Save initial return values for testing restored values
final ui.Rect initialLocalBounds = canvas.getLocalClipBounds();
final ui.Rect initialDestinationBounds = canvas.getDestinationClipBounds();
rectsClose(initialLocalBounds, clipExpandedBounds);
rectsClose(initialDestinationBounds, clipDestBounds);
canvas.save();
canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 15, 15));
// Both clip bounds have changed
rectsNotClose(canvas.getLocalClipBounds(), clipExpandedBounds);
rectsNotClose(canvas.getDestinationClipBounds(), clipDestBounds);
// Previous return values have not changed
rectsClose(initialLocalBounds, clipExpandedBounds);
rectsClose(initialDestinationBounds, clipDestBounds);
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
canvas.save();
canvas.scale(2, 2);
const ui.Rect scaledExpandedBounds = ui.Rect.fromLTRB(5, 5.5, 10.5, 13);
rectsClose(canvas.getLocalClipBounds(), scaledExpandedBounds);
// Destination bounds are unaffected by transform
rectsClose(canvas.getDestinationClipBounds(), clipDestBounds);
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
});
test('Canvas.clipPath affects canvas.getClipBounds', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100));
const ui.Rect clipRawBounds = ui.Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
const ui.Rect clipExpandedBounds = ui.Rect.fromLTRB(10, 11, 21, 26);
final ui.Rect clipDestBounds = deviceClipRoundsOut ? clipExpandedBounds : clipRawBounds;
final ui.Path clip = ui.Path()..addRect(clipRawBounds)..addOval(clipRawBounds);
canvas.clipPath(clip);
// Save initial return values for testing restored values
final ui.Rect initialLocalBounds = canvas.getLocalClipBounds();
final ui.Rect initialDestinationBounds = canvas.getDestinationClipBounds();
rectsClose(initialLocalBounds, clipExpandedBounds);
rectsClose(initialDestinationBounds, clipDestBounds);
canvas.save();
canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 15, 15));
// Both clip bounds have changed
rectsNotClose(canvas.getLocalClipBounds(), clipExpandedBounds);
rectsNotClose(canvas.getDestinationClipBounds(), clipDestBounds);
// Previous return values have not changed
rectsClose(initialLocalBounds, clipExpandedBounds);
rectsClose(initialDestinationBounds, clipDestBounds);
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
canvas.save();
canvas.scale(2, 2);
const ui.Rect scaledExpandedBounds = ui.Rect.fromLTRB(5, 5.5, 10.5, 13);
rectsClose(canvas.getLocalClipBounds(), scaledExpandedBounds);
// Destination bounds are unaffected by transform
rectsClose(canvas.getDestinationClipBounds(), clipDestBounds);
canvas.restore();
// save/restore returned the values to their original values
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
});
test('Canvas.clipRect(diff) does not affect canvas.getClipBounds', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100));
const ui.Rect clipRawBounds = ui.Rect.fromLTRB(10.2, 11.3, 20.4, 25.7);
const ui.Rect clipExpandedBounds = ui.Rect.fromLTRB(10, 11, 21, 26);
final ui.Rect clipDestBounds = deviceClipRoundsOut ? clipExpandedBounds : clipRawBounds;
canvas.clipRect(clipRawBounds);
// Save initial return values for testing restored values
final ui.Rect initialLocalBounds = canvas.getLocalClipBounds();
final ui.Rect initialDestinationBounds = canvas.getDestinationClipBounds();
rectsClose(initialLocalBounds, clipExpandedBounds);
rectsClose(initialDestinationBounds, clipDestBounds);
canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 15, 15), clipOp: ui.ClipOp.difference);
expect(canvas.getLocalClipBounds(), initialLocalBounds);
expect(canvas.getDestinationClipBounds(), initialDestinationBounds);
});
});
group('RestoreToCount function tests', () {
test('RestoreToCount can work', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.save();
canvas.save();
canvas.save();
canvas.save();
canvas.save();
expect(canvas.getSaveCount(), 6);
canvas.restoreToCount(2);
expect(canvas.getSaveCount(), 2);
canvas.restore();
expect(canvas.getSaveCount(), 1);
});
test('RestoreToCount count less than 1, the stack should be reset', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.save();
canvas.save();
canvas.save();
canvas.save();
canvas.save();
expect(canvas.getSaveCount(), equals(6));
canvas.restoreToCount(0);
expect(canvas.getSaveCount(), equals(1));
});
test('RestoreToCount count greater than current [getSaveCount]', () async {
final ui.PictureRecorder recorder = ui.PictureRecorder();
final ui.Canvas canvas = ui.Canvas(recorder);
canvas.save();
canvas.save();
canvas.save();
canvas.save();
canvas.save();
expect(canvas.getSaveCount(), equals(6));
canvas.restoreToCount(canvas.getSaveCount() + 1);
expect(canvas.getSaveCount(), equals(6));
});
});
}