| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <gtest/gtest.h> |
| |
| #include <canvas/CanvasFrontend.h> |
| #include <canvas/CanvasOpBuffer.h> |
| #include <canvas/CanvasOps.h> |
| #include <canvas/CanvasOpRasterizer.h> |
| |
| #include <tests/common/CallCountingCanvas.h> |
| |
| #include "SkBlendMode.h" |
| #include "SkBitmap.h" |
| #include "SkCanvas.h" |
| #include "SkColor.h" |
| #include "SkImageInfo.h" |
| #include "SkLatticeIter.h" |
| #include "SkPaint.h" |
| #include "SkPath.h" |
| #include "SkPictureRecorder.h" |
| #include "SkRRect.h" |
| #include "SkRect.h" |
| #include "SkRegion.h" |
| #include "pipeline/skia/AnimatedDrawables.h" |
| #include <SkNoDrawCanvas.h> |
| |
| using namespace android; |
| using namespace android::uirenderer; |
| using namespace android::uirenderer::skiapipeline; |
| using namespace android::uirenderer::test; |
| |
| // We lazy |
| using Op = CanvasOpType; |
| |
| class CanvasOpCountingReceiver { |
| public: |
| template <CanvasOpType T> |
| void push_container(CanvasOpContainer<T>&& op) { |
| mOpCounts[static_cast<size_t>(T)] += 1; |
| } |
| |
| int operator[](CanvasOpType op) const { |
| return mOpCounts[static_cast<size_t>(op)]; |
| } |
| |
| private: |
| std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts; |
| }; |
| |
| template<typename T> |
| static int countItems(const T& t) { |
| int count = 0; |
| t.for_each([&](auto i) { |
| count++; |
| }); |
| return count; |
| } |
| |
| TEST(CanvasOp, verifyConst) { |
| CanvasOpBuffer buffer; |
| buffer.push<Op::DrawColor>({ |
| .color = SkColors::kBlack, |
| .mode = SkBlendMode::kSrcOver, |
| }); |
| buffer.for_each([](auto op) { |
| static_assert(std::is_const_v<std::remove_reference_t<decltype(*op)>>, |
| "Expected container to be const"); |
| static_assert(std::is_const_v<std::remove_reference_t<decltype(op->op())>>, |
| "Expected op to be const"); |
| }); |
| } |
| |
| TEST(CanvasOp, simplePush) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::Save>({}); |
| buffer.push<Op::Save>({}); |
| buffer.push<Op::Restore>({}); |
| EXPECT_GT(buffer.size(), 0); |
| |
| int saveCount = 0; |
| int restoreCount = 0; |
| int otherCount = 0; |
| |
| buffer.for_each([&](auto op) { |
| switch (op->type()) { |
| case Op::Save: |
| saveCount++; |
| break; |
| case Op::Restore: |
| restoreCount++; |
| break; |
| default: |
| otherCount++; |
| break; |
| } |
| }); |
| |
| EXPECT_EQ(saveCount, 2); |
| EXPECT_EQ(restoreCount, 1); |
| EXPECT_EQ(otherCount, 0); |
| |
| buffer.clear(); |
| int itemCount = 0; |
| buffer.for_each([&](auto op) { |
| itemCount++; |
| }); |
| EXPECT_EQ(itemCount, 0); |
| buffer.resize(0); |
| EXPECT_EQ(buffer.size(), 0); |
| } |
| |
| TEST(CanvasOp, simpleDrawPaint) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::DrawColor> ({ |
| .color = SkColor4f{1, 1, 1, 1}, |
| .mode = SkBlendMode::kSrcIn |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawPaintCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawPoint) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::DrawPoint> ({ |
| .x = 12, |
| .y = 42, |
| .paint = SkPaint{} |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawPoints); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawPoints) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| size_t numPts = 3; |
| auto pts = sk_sp<Points>( |
| new Points({ |
| {32, 16}, |
| {48, 48}, |
| {16, 32} |
| }) |
| ); |
| |
| buffer.push(CanvasOp<Op::DrawPoints> { |
| .count = numPts, |
| .paint = SkPaint{}, |
| .points = pts |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawPoints); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawLine) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::DrawLine> ({ |
| .startX = 16, |
| .startY = 28, |
| .endX = 12, |
| .endY = 30, |
| .paint = SkPaint{} |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawPoints); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawLines) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| size_t numPts = 3; |
| auto pts = sk_sp<Points>( |
| new Points({ |
| {32, 16}, |
| {48, 48}, |
| {16, 32} |
| }) |
| ); |
| buffer.push(CanvasOp<Op::DrawLines> { |
| .count = numPts, |
| .paint = SkPaint{}, |
| .points = pts |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawPoints); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawRect) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::DrawRect>({ |
| .rect = SkRect::MakeEmpty(), |
| .paint = SkPaint{}, |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawRectCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawRegionRect) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| SkRegion region; |
| region.setRect(SkIRect::MakeWH(12, 50)); |
| buffer.push<Op::DrawRegion>({ |
| .region = region, |
| .paint = SkPaint{}, |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| // If the region is a rectangle, drawRegion calls into drawRect as a fast path |
| EXPECT_EQ(1, canvas.drawRectCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawRegionPath) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| SkPath path; |
| path.addCircle(50, 50, 50); |
| SkRegion clip; |
| clip.setRect(SkIRect::MakeWH(100, 100)); |
| SkRegion region; |
| region.setPath(path, clip); |
| buffer.push<Op::DrawRegion>({ |
| .region = region, |
| .paint = SkPaint{}, |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawRegionCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawRoundRect) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::DrawRoundRect>({ |
| .rect = SkRect::MakeEmpty(), |
| .rx = 10, |
| .ry = 10, |
| .paint = SkPaint{}, |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawRRectCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawDoubleRoundRect) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| SkRect outer = SkRect::MakeLTRB(0, 0, 100, 100); |
| SkRect inner = SkRect::MakeLTRB(20, 20, 80, 80); |
| |
| const int numPts = 4; |
| SkRRect outerRRect; |
| |
| auto outerPts = std::make_unique<SkVector[]>(numPts); |
| outerPts[0].set(32, 16); |
| outerPts[1].set(48, 48); |
| outerPts[2].set(16, 32); |
| outerPts[3].set(20, 20); |
| outerRRect.setRectRadii(outer, outerPts.get()); |
| outerRRect.setRect(outer); |
| |
| SkRRect innerRRect; |
| auto innerPts = std::make_unique<SkVector[]>(numPts); |
| innerPts[0].set(16, 8); |
| innerPts[1].set(24, 24); |
| innerPts[2].set(8, 16); |
| innerPts[3].set(10, 10); |
| innerRRect.setRectRadii(inner, innerPts.get()); |
| |
| buffer.push<Op::DrawDoubleRoundRect> ({ |
| .outer = outerRRect, |
| .inner = innerRRect, |
| .paint = SkPaint{} |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawDRRectCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawCircle) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::DrawCircle>({ |
| .cx = 5, |
| .cy = 7, |
| .radius = 10, |
| .paint = SkPaint{} |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawOvalCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawOval) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::DrawOval> ({ |
| .oval = SkRect::MakeEmpty(), |
| .paint = SkPaint{} |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawOvalCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawArc) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| buffer.push<Op::DrawArc>({ |
| .oval = SkRect::MakeWH(100, 100), |
| .startAngle = 120, |
| .sweepAngle = 70, |
| .useCenter = true, |
| .paint = SkPaint{} |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawArcCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawPath) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| SkPath path; |
| path.addCircle(50, 50, 30); |
| buffer.push<Op::DrawPath> ({ |
| .path = path, |
| .paint = SkPaint{} |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawPathCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawRoundRectProperty) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| |
| auto left = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(1)); |
| auto top = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(2)); |
| auto right = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(3)); |
| auto bottom = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(4)); |
| auto radiusX = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(5)); |
| auto radiusY = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(6)); |
| auto propertyPaint = |
| sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{})); |
| |
| buffer.push<Op::DrawRoundRectProperty> ({ |
| .left = left, |
| .top = top, |
| .right = right, |
| .bottom = bottom, |
| .rx = radiusX, |
| .ry = radiusY, |
| .paint = propertyPaint |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawRRectCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawCircleProperty) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| |
| auto x = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(1)); |
| auto y = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(2)); |
| auto radius = sp<CanvasPropertyPrimitive>(new uirenderer::CanvasPropertyPrimitive(5)); |
| auto propertyPaint = |
| sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{})); |
| |
| buffer.push<Op::DrawCircleProperty> ({ |
| .x = x, |
| .y = y, |
| .radius = radius, |
| .paint = propertyPaint |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawOvalCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawVertices) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| |
| SkPoint pts[3] = {{64, 32}, {0, 224}, {128, 224}}; |
| SkColor colors[3] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN}; |
| sk_sp<SkVertices> vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 3, pts, |
| nullptr, colors); |
| buffer.push<Op::DrawVertices> ({ |
| .vertices = vertices, |
| .mode = SkBlendMode::kSrcOver, |
| .paint = SkPaint{} |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawVerticesCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawImage) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| |
| SkImageInfo info =SkImageInfo::Make(5, 1, |
| kGray_8_SkColorType, kOpaque_SkAlphaType); |
| sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info); |
| buffer.push<Op::DrawImage> ({ |
| bitmap, |
| 7, |
| 19, |
| SkFilterMode::kNearest, |
| SkPaint{} |
| } |
| ); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawImageRectCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawImageRect) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| |
| SkImageInfo info = SkImageInfo::Make(5, 1, |
| kGray_8_SkColorType, kOpaque_SkAlphaType); |
| |
| sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info); |
| buffer.push<Op::DrawImageRect> ({ |
| bitmap, SkRect::MakeWH(100, 100), |
| SkRect::MakeLTRB(120, 110, 220, 210), |
| SkFilterMode::kNearest, SkPaint{} |
| } |
| ); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawImageRectCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawImageLattice) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| |
| SkBitmap skBitmap; |
| skBitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60)); |
| |
| const int xDivs[] = { 20, 50 }; |
| const int yDivs[] = { 10, 40 }; |
| SkCanvas::Lattice::RectType fillTypes[3][3]; |
| memset(fillTypes, 0, sizeof(fillTypes)); |
| fillTypes[1][1] = SkCanvas::Lattice::kTransparent; |
| SkColor colors[9]; |
| SkCanvas::Lattice lattice = { xDivs, yDivs, fillTypes[0], 2, |
| 2, nullptr, colors }; |
| sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(&skBitmap); |
| buffer.push<Op::DrawImageLattice>( |
| { |
| bitmap, |
| SkRect::MakeWH(5, 1), |
| lattice, |
| SkFilterMode::kNearest, |
| SkPaint{} |
| } |
| ); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawImageLatticeCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawPicture) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| |
| SkPictureRecorder recorder; |
| SkCanvas* pictureCanvas = recorder.beginRecording({64, 64, 192, 192}); |
| SkPaint paint; |
| pictureCanvas->drawRect(SkRect::MakeWH(200, 200), paint); |
| paint.setColor(SK_ColorWHITE); |
| pictureCanvas->drawRect(SkRect::MakeLTRB(20, 20, 180, 180), paint); |
| sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); |
| buffer.push<Op::DrawPicture> ({ |
| .picture = picture |
| }); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| // Note because we are explicitly issuing 2 drawRect calls |
| // in the picture recorder above, when it is played back into |
| // CallCountingCanvas we will see 2 calls to drawRect instead of 1 |
| // call to drawPicture. |
| // This is because SkiaCanvas::drawPicture uses picture.playback(canvas) |
| // instead of canvas->drawPicture. |
| EXPECT_EQ(2, canvas.drawRectCount); |
| EXPECT_EQ(2, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, simpleDrawRipple) { |
| CanvasOpBuffer buffer; |
| EXPECT_EQ(buffer.size(), 0); |
| |
| const char* sksl = |
| "half4 main(float2 coord) {" |
| " return half4(1.);" |
| "}"; |
| auto [effect, error] = SkRuntimeEffect::MakeForShader(SkString(sksl)); |
| auto params = RippleDrawableParams{ |
| .x = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(100)), |
| .y = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(200)), |
| .radius = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(50)), |
| .progress = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(0.5)), |
| .turbulencePhase = sp<CanvasPropertyPrimitive>(new CanvasPropertyPrimitive(1)), |
| .color = 0xff00ff, |
| .paint = sp<CanvasPropertyPaint>(new CanvasPropertyPaint(SkPaint{})), |
| .effectBuilder = SkRuntimeShaderBuilder(effect)}; |
| buffer.push<Op::DrawRippleDrawable>({.params = params}); |
| |
| CallCountingCanvas canvas; |
| EXPECT_EQ(0, canvas.sumTotalDrawCalls()); |
| rasterizeCanvasBuffer(buffer, &canvas); |
| EXPECT_EQ(1, canvas.drawOvalCount); |
| EXPECT_EQ(1, canvas.sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, immediateRendering) { |
| auto canvas = std::make_shared<CallCountingCanvas>(); |
| |
| EXPECT_EQ(0, canvas->sumTotalDrawCalls()); |
| ImmediateModeRasterizer rasterizer{canvas}; |
| auto op = CanvasOp<Op::DrawRect>{ |
| .rect = SkRect::MakeEmpty(), |
| .paint = SkPaint{}, |
| }; |
| EXPECT_TRUE(CanvasOpTraits::can_draw<decltype(op)>); |
| rasterizer.draw(op); |
| EXPECT_EQ(1, canvas->drawRectCount); |
| EXPECT_EQ(1, canvas->sumTotalDrawCalls()); |
| } |
| |
| TEST(CanvasOp, frontendSaveCount) { |
| SkNoDrawCanvas skiaCanvas(100, 100); |
| CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100); |
| const auto& receiver = opCanvas.receiver(); |
| |
| EXPECT_EQ(1, skiaCanvas.getSaveCount()); |
| EXPECT_EQ(1, opCanvas.saveCount()); |
| |
| skiaCanvas.save(); |
| opCanvas.save(SaveFlags::MatrixClip); |
| EXPECT_EQ(2, skiaCanvas.getSaveCount()); |
| EXPECT_EQ(2, opCanvas.saveCount()); |
| |
| skiaCanvas.restore(); |
| opCanvas.restore(); |
| EXPECT_EQ(1, skiaCanvas.getSaveCount()); |
| EXPECT_EQ(1, opCanvas.saveCount()); |
| |
| skiaCanvas.restore(); |
| opCanvas.restore(); |
| EXPECT_EQ(1, skiaCanvas.getSaveCount()); |
| EXPECT_EQ(1, opCanvas.saveCount()); |
| |
| EXPECT_EQ(1, receiver[Op::Save]); |
| EXPECT_EQ(1, receiver[Op::Restore]); |
| } |