Prevent EndLayerOps when Begin was rejected

am: 3c53ec51ef

Change-Id: I075b68a1b7bdee4cbebd734748dd485ff6cd67b3
This commit is contained in:
Chris Craik
2016-08-08 23:57:22 +00:00
committed by android-build-merger
3 changed files with 53 additions and 35 deletions

View File

@ -149,50 +149,53 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
// Map visible bounds back to layer space, and intersect with parameter bounds // Map visible bounds back to layer space, and intersect with parameter bounds
Rect layerBounds = visibleBounds; Rect layerBounds = visibleBounds;
Matrix4 inverse; if (CC_LIKELY(!layerBounds.isEmpty())) {
inverse.loadInverse(*previous.transform); // if non-empty, can safely map by the inverse transform
inverse.mapRect(layerBounds); Matrix4 inverse;
layerBounds.doIntersect(unmappedBounds); inverse.loadInverse(*previous.transform);
inverse.mapRect(layerBounds);
layerBounds.doIntersect(unmappedBounds);
}
int saveValue = mState.save((int) flags); int saveValue = mState.save((int) flags);
Snapshot& snapshot = *mState.writableSnapshot(); Snapshot& snapshot = *mState.writableSnapshot();
// layerBounds is in original bounds space, but clipped by current recording clip // layerBounds is in original bounds space, but clipped by current recording clip
if (layerBounds.isEmpty() || unmappedBounds.isEmpty()) { if (!layerBounds.isEmpty() && !unmappedBounds.isEmpty()) {
// Don't bother recording layer, since it's been rejected
if (CC_LIKELY(clippedLayer)) { if (CC_LIKELY(clippedLayer)) {
snapshot.resetClip(0, 0, 0, 0); auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed
if (addOp(alloc().create_trivial<BeginLayerOp>(
unmappedBounds,
*previous.transform, // transform to *draw* with
previousClip, // clip to *draw* with
refPaint(paint))) >= 0) {
snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
Rect clip = layerBounds;
clip.translate(-unmappedBounds.left, -unmappedBounds.top);
snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
snapshot.roundRectClipState = nullptr;
return saveValue;
}
} else {
if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
unmappedBounds,
*mState.currentSnapshot()->transform,
getRecordedClip(),
refPaint(paint))) >= 0) {
snapshot.flags |= Snapshot::kFlagIsLayer;
return saveValue;
}
} }
return saveValue;
} }
// Layer not needed, so skip recording it...
if (CC_LIKELY(clippedLayer)) { if (CC_LIKELY(clippedLayer)) {
auto previousClip = getRecordedClip(); // note: done before new snapshot's clip has changed // ... and set empty clip to reject inner content, if possible
snapshot.resetClip(0, 0, 0, 0);
snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
Rect clip = layerBounds;
clip.translate(-unmappedBounds.left, -unmappedBounds.top);
snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
snapshot.roundRectClipState = nullptr;
addOp(alloc().create_trivial<BeginLayerOp>(
unmappedBounds,
*previous.transform, // transform to *draw* with
previousClip, // clip to *draw* with
refPaint(paint)));
} else {
snapshot.flags |= Snapshot::kFlagIsLayer;
addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
unmappedBounds,
*mState.currentSnapshot()->transform,
getRecordedClip(),
refPaint(paint)));
} }
return saveValue; return saveValue;
} }
@ -619,7 +622,7 @@ void RecordingCanvas::callDrawGLFunction(Functor* functor,
functor)); functor));
} }
size_t RecordingCanvas::addOp(RecordedOp* op) { int RecordingCanvas::addOp(RecordedOp* op) {
// skip op with empty clip // skip op with empty clip
if (op->localClip && op->localClip->rect.isEmpty()) { if (op->localClip && op->localClip->rect.isEmpty()) {
// NOTE: this rejection happens after op construction/content ref-ing, so content ref'd // NOTE: this rejection happens after op construction/content ref-ing, so content ref'd

View File

@ -208,7 +208,7 @@ private:
void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint); void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
size_t addOp(RecordedOp* op); int addOp(RecordedOp* op);
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// lazy object copy // lazy object copy
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -545,6 +545,21 @@ TEST(RecordingCanvas, saveLayer_rotateClipped) {
EXPECT_EQ(3, count); EXPECT_EQ(3, count);
} }
TEST(RecordingCanvas, saveLayer_rejectBegin) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(0, -20); // avoid identity case
// empty clip rect should force layer + contents to be rejected
canvas.clipRect(0, -20, 200, -20, SkRegion::kIntersect_Op);
canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
canvas.restore();
});
ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
}
TEST(RecordingCanvas, drawRenderNode_rejection) { TEST(RecordingCanvas, drawRenderNode_rejection) {
auto child = TestUtils::createNode(50, 50, 150, 150, auto child = TestUtils::createNode(50, 50, 150, 150,
[](RenderProperties& props, RecordingCanvas& canvas) { [](RenderProperties& props, RecordingCanvas& canvas) {