| /* | |
| * 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); | |
| const SkPath path = SkPath::Circle(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); | |
| const SkPath path = SkPath::Circle(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 = SkColor4f{1.f, 0.f, 1.f, 0.f}, | |
| .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]); | |
| } |