Loading libs/hwui/RecordingCanvas.cpp +37 −34 Original line number Original line Diff line number Diff line Loading @@ -149,26 +149,26 @@ 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; if (CC_LIKELY(!layerBounds.isEmpty())) { // if non-empty, can safely map by the inverse transform Matrix4 inverse; Matrix4 inverse; inverse.loadInverse(*previous.transform); inverse.loadInverse(*previous.transform); inverse.mapRect(layerBounds); inverse.mapRect(layerBounds); layerBounds.doIntersect(unmappedBounds); 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)) { snapshot.resetClip(0, 0, 0, 0); } return saveValue; } if (CC_LIKELY(clippedLayer)) { if (CC_LIKELY(clippedLayer)) { auto previousClip = getRecordedClip(); // note: done before new snapshot's clip has changed 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.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer; snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight()); snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight()); snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f); snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f); Loading @@ -177,22 +177,25 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, clip.translate(-unmappedBounds.left, -unmappedBounds.top); clip.translate(-unmappedBounds.left, -unmappedBounds.top); snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom); snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom); snapshot.roundRectClipState = nullptr; snapshot.roundRectClipState = nullptr; return saveValue; addOp(alloc().create_trivial<BeginLayerOp>( } unmappedBounds, *previous.transform, // transform to *draw* with previousClip, // clip to *draw* with refPaint(paint))); } else { } else { snapshot.flags |= Snapshot::kFlagIsLayer; if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>( addOp(alloc().create_trivial<BeginUnclippedLayerOp>( unmappedBounds, unmappedBounds, *mState.currentSnapshot()->transform, *mState.currentSnapshot()->transform, getRecordedClip(), getRecordedClip(), refPaint(paint))); refPaint(paint))) >= 0) { snapshot.flags |= Snapshot::kFlagIsLayer; return saveValue; } } } } // Layer not needed, so skip recording it... if (CC_LIKELY(clippedLayer)) { // ... and set empty clip to reject inner content, if possible snapshot.resetClip(0, 0, 0, 0); } return saveValue; return saveValue; } } Loading Loading @@ -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 Loading libs/hwui/RecordingCanvas.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -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 // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- Loading libs/hwui/tests/unit/RecordingCanvasTests.cpp +15 −0 Original line number Original line Diff line number Diff line Loading @@ -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) { Loading Loading
libs/hwui/RecordingCanvas.cpp +37 −34 Original line number Original line Diff line number Diff line Loading @@ -149,26 +149,26 @@ 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; if (CC_LIKELY(!layerBounds.isEmpty())) { // if non-empty, can safely map by the inverse transform Matrix4 inverse; Matrix4 inverse; inverse.loadInverse(*previous.transform); inverse.loadInverse(*previous.transform); inverse.mapRect(layerBounds); inverse.mapRect(layerBounds); layerBounds.doIntersect(unmappedBounds); 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)) { snapshot.resetClip(0, 0, 0, 0); } return saveValue; } if (CC_LIKELY(clippedLayer)) { if (CC_LIKELY(clippedLayer)) { auto previousClip = getRecordedClip(); // note: done before new snapshot's clip has changed 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.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer; snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight()); snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight()); snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f); snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f); Loading @@ -177,22 +177,25 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, clip.translate(-unmappedBounds.left, -unmappedBounds.top); clip.translate(-unmappedBounds.left, -unmappedBounds.top); snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom); snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom); snapshot.roundRectClipState = nullptr; snapshot.roundRectClipState = nullptr; return saveValue; addOp(alloc().create_trivial<BeginLayerOp>( } unmappedBounds, *previous.transform, // transform to *draw* with previousClip, // clip to *draw* with refPaint(paint))); } else { } else { snapshot.flags |= Snapshot::kFlagIsLayer; if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>( addOp(alloc().create_trivial<BeginUnclippedLayerOp>( unmappedBounds, unmappedBounds, *mState.currentSnapshot()->transform, *mState.currentSnapshot()->transform, getRecordedClip(), getRecordedClip(), refPaint(paint))); refPaint(paint))) >= 0) { snapshot.flags |= Snapshot::kFlagIsLayer; return saveValue; } } } } // Layer not needed, so skip recording it... if (CC_LIKELY(clippedLayer)) { // ... and set empty clip to reject inner content, if possible snapshot.resetClip(0, 0, 0, 0); } return saveValue; return saveValue; } } Loading Loading @@ -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 Loading
libs/hwui/RecordingCanvas.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -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 // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- Loading
libs/hwui/tests/unit/RecordingCanvasTests.cpp +15 −0 Original line number Original line Diff line number Diff line Loading @@ -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) { Loading