Skip to content

Commit

Permalink
tweaks and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yjbanov committed Apr 10, 2020
1 parent 6ed058a commit 56e5b76
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 30 deletions.
9 changes: 7 additions & 2 deletions lib/web_ui/lib/src/engine/surface/picture.dart
Expand Up @@ -491,7 +491,7 @@ abstract class PersistedPicture extends PersistedLeafSurface {

// The new cull rect contains area not covered by a previous rect. Perhaps
// the clip is growing, moving around the picture, or both. In this case
// a part of the picture may not been painted. We will need to
// a part of the picture may not have been painted. We will need to
// request a new canvas and paint the picture on it. However, this is also
// a strong signal that the clip will continue growing as typically
// Flutter uses animated transitions. So instead of allocating the canvas
Expand All @@ -500,12 +500,14 @@ abstract class PersistedPicture extends PersistedLeafSurface {
// will hit the above case where the new cull rect is fully contained
// within the cull rect we compute now.

// If any of the borders moved.
// Compute the delta, by which each of the side of the clip rect has "moved"
// since the last time we updated the cull rect.
final double leftwardDelta = oldOptimalLocalCullRect.left - _exactLocalCullRect.left;
final double upwardDelta = oldOptimalLocalCullRect.top - _exactLocalCullRect.top;
final double rightwardDelta = _exactLocalCullRect.right - oldOptimalLocalCullRect.right;
final double bottomwardDelta = _exactLocalCullRect.bottom - oldOptimalLocalCullRect.bottom;

// Compute the new optimal rect to paint into.
final ui.Rect newLocalCullRect = ui.Rect.fromLTRB(
_exactLocalCullRect.left - _predictTrend(leftwardDelta, _exactLocalCullRect.width),
_exactLocalCullRect.top - _predictTrend(upwardDelta, _exactLocalCullRect.height),
Expand All @@ -518,6 +520,9 @@ abstract class PersistedPicture extends PersistedLeafSurface {
return localCullRectChanged;
}

/// Predicts the delta a particular side of a clip rect will move given the
/// [delta] it moved by last, and the respective [extent] (width or height)
/// of the clip.
static double _predictTrend(double delta, double extent) {
if (delta <= 0.0) {
// Shrinking. Give it 10% of the extent in case the trend is reversed.
Expand Down
100 changes: 77 additions & 23 deletions lib/web_ui/lib/src/engine/surface/recording_canvas.dart
Expand Up @@ -27,17 +27,25 @@ class RecordingCanvas {
final _PaintBounds _paintBounds;

/// Maximum paintable bounds for this canvas.
ui.Rect _pictureBounds;
ui.Rect get pictureBounds {
assert(
_pictureBounds != null,
!_debugRecordingEnded,
'Picture bounds not available yet. Call [endRecording] before accessing picture bounds.',
);
return _pictureBounds;
}
ui.Rect _pictureBounds;

final List<PaintCommand> _commands = <PaintCommand>[];

/// In debug mode returns the list of recorded paint commands for testing.
List<PaintCommand> get debugPaintCommands {
if (assertionsEnabled) {
return _commands;
}
throw UnsupportedError('For debugging only.');
}

RecordingCanvas(ui.Rect bounds) : _paintBounds = _PaintBounds(bounds);

/// Whether this canvas is doing arbitrary paint operations not expressible
Expand All @@ -64,6 +72,8 @@ class RecordingCanvas {
bool get didDraw => _didDraw;
bool _didDraw = false;

/// When assertions are enabled used to ensure that [endRecording] is called
/// before calling [apply] or [pictureBounds].
bool _debugRecordingEnded = false;

/// Stops recording drawing commands and computes paint bounds.
Expand All @@ -75,24 +85,30 @@ class RecordingCanvas {
/// directly it is up to you to call this method explicitly.
void endRecording() {
_pictureBounds = _paintBounds.computeBounds();
_debugRecordingEnded = true;
if (assertionsEnabled) {
_debugRecordingEnded = true;
}
}

/// Applies the recorded commands onto an [engineCanvas].
///
/// The [clipRect] specifies the clip applied to the picture (screen clip at a minimum).
/// The [clipRect] specifies the clip applied to the picture (screen clip at
/// a minimum). The commands that fall outside the clip are skipped and are
/// not applied to the [engineCanvas]. A command must have a non-zero
/// intersection with the clip in order to be applied.
void apply(EngineCanvas engineCanvas, ui.Rect clipRect) {
assert(_debugRecordingEnded);
if (_debugDumpPaintCommands) {
final StringBuffer debugBuf = StringBuffer();
int skips = 0;
debugBuf.writeln(
'--- Applying RecordingCanvas to ${engineCanvas.runtimeType} '
'with bounds $_paintBounds and clip $clipRect (w = ${clipRect.width}, h = ${clipRect.height})');
'with bounds $_paintBounds and clip $clipRect (w = ${clipRect.width},'
' h = ${clipRect.height})');
for (int i = 0; i < _commands.length; i++) {
final PaintCommand command = _commands[i];
if (command is DrawCommand) {
if (_isOutsideClipRegion(command, clipRect)) {
if (_isInvisible(command, clipRect)) {
// The drawing command is outside the clip region. No need to apply.
debugBuf.writeln('SKIPPED: ctx.$command;');
skips += 1;
Expand Down Expand Up @@ -121,7 +137,7 @@ class RecordingCanvas {
for (int i = 0, len = _commands.length; i < len; i++) {
final PaintCommand command = _commands[i];
if (command is DrawCommand) {
if (_isOutsideClipRegion(command, clipRect)) {
if (_isInvisible(command, clipRect)) {
// The drawing command is outside the clip region. No need to apply.
continue;
}
Expand All @@ -140,11 +156,18 @@ class RecordingCanvas {
engineCanvas.endOfPaint();
}

static bool _isOutsideClipRegion(DrawCommand command, ui.Rect clipRect) {
return command.rightBound < clipRect.left ||
command.bottomBound < clipRect.top ||
command.leftBound > clipRect.right ||
command.topBound > clipRect.bottom;
/// Return true is the [command] is invisible because it is clipped out.
static bool _isInvisible(DrawCommand command, ui.Rect clipRect) {
if (command.isClippedOut) {
return true;
}

// Check top and bottom first because vertical scrolling is more common
// than horizontal scrolling.
return command.bottomBound < clipRect.top ||
command.topBound > clipRect.bottom ||
command.rightBound < clipRect.left ||
command.leftBound > clipRect.right;
}

/// Prints recorded commands.
Expand Down Expand Up @@ -233,23 +256,26 @@ class RecordingCanvas {

void clipRect(ui.Rect rect) {
assert(!_debugRecordingEnded);
_paintBounds.clipRect(rect);
final PaintClipRect command = PaintClipRect(rect);
_paintBounds.clipRect(rect, command);
_hasArbitraryPaint = true;
_commands.add(PaintClipRect(rect));
_commands.add(command);
}

void clipRRect(ui.RRect rrect) {
assert(!_debugRecordingEnded);
_paintBounds.clipRect(rrect.outerRect);
final PaintClipRRect command = PaintClipRRect(rrect);
_paintBounds.clipRect(rrect.outerRect, command);
_hasArbitraryPaint = true;
_commands.add(PaintClipRRect(rrect));
_commands.add(command);
}

void clipPath(ui.Path path, {bool doAntiAlias = true}) {
assert(!_debugRecordingEnded);
_paintBounds.clipRect(path.getBounds());
final PaintClipPath command = PaintClipPath(path);
_paintBounds.clipRect(path.getBounds(), command);
_hasArbitraryPaint = true;
_commands.add(PaintClipPath(path));
_commands.add(command);
}

void drawColor(ui.Color color, ui.BlendMode blendMode) {
Expand Down Expand Up @@ -566,11 +592,26 @@ abstract class PaintCommand {
void serializeToCssPaint(List<List<dynamic>> serializedCommands);
}

/// A [PaintCommand] that puts pixels on the screen (unlike [SaveCommand]).
/// A [PaintCommand] that affect pixels on the screen (unlike, for example, the
/// [SaveCommand]).
abstract class DrawCommand extends PaintCommand {
/// Whether the command is completely clipped out of the picture.
bool isClippedOut = false;

/// The left bound of the graphic produced by this command in picture-global
/// coordinates.
double leftBound = double.negativeInfinity;

/// The top bound of the graphic produced by this command in picture-global
/// coordinates.
double topBound = double.negativeInfinity;

/// The right bound of the graphic produced by this command in picture-global
/// coordinates.
double rightBound = double.infinity;

/// The bottom bound of the graphic produced by this command in
/// picture-global coordinates.
double bottomBound = double.infinity;
}

Expand Down Expand Up @@ -748,7 +789,7 @@ class PaintSkew extends PaintCommand {
}
}

class PaintClipRect extends PaintCommand {
class PaintClipRect extends DrawCommand {
final ui.Rect rect;

PaintClipRect(this.rect);
Expand All @@ -773,7 +814,7 @@ class PaintClipRect extends PaintCommand {
}
}

class PaintClipRRect extends PaintCommand {
class PaintClipRRect extends DrawCommand {
final ui.RRect rrect;

PaintClipRRect(this.rrect);
Expand Down Expand Up @@ -801,7 +842,7 @@ class PaintClipRRect extends PaintCommand {
}
}

class PaintClipPath extends PaintCommand {
class PaintClipPath extends DrawCommand {
final SurfacePath path;

PaintClipPath(this.path);
Expand Down Expand Up @@ -1881,7 +1922,7 @@ class _PaintBounds {
_currentMatrix.multiply(skewMatrix);
}

void clipRect(ui.Rect rect) {
void clipRect(ui.Rect rect, DrawCommand command) {
// If we have an active transform, calculate screen relative clipping
// rectangle and union with current clipping rectangle.
if (!_currentMatrixIsIdentity) {
Expand Down Expand Up @@ -1923,6 +1964,14 @@ class _PaintBounds {
_currentClipBottom = rect.bottom;
}
}
if (_currentClipLeft >= _currentClipRight || _currentClipTop >= _currentClipBottom) {
command.isClippedOut = true;
} else {
command.leftBound = _currentClipLeft;
command.topBound = _currentClipTop;
command.rightBound = _currentClipRight;
command.bottomBound = _currentClipBottom;
}
}

/// Grow painted area to include given rectangle.
Expand All @@ -1933,6 +1982,7 @@ class _PaintBounds {
/// Grow painted area to include given rectangle.
void growLTRB(double left, double top, double right, double bottom, DrawCommand command) {
if (left == right || top == bottom) {
command.isClippedOut = true;
return;
}

Expand All @@ -1952,15 +2002,19 @@ class _PaintBounds {

if (_clipRectInitialized) {
if (transformedPointLeft > _currentClipRight) {
command.isClippedOut = true;
return;
}
if (transformedPointRight < _currentClipLeft) {
command.isClippedOut = true;
return;
}
if (transformedPointTop > _currentClipBottom) {
command.isClippedOut = true;
return;
}
if (transformedPointBottom < _currentClipTop) {
command.isClippedOut = true;
return;
}
if (transformedPointLeft < _currentClipLeft) {
Expand Down

0 comments on commit 56e5b76

Please sign in to comment.