| /* |
| * 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 "SkPictureRecorder.h" |
| #include "SkColor.h" |
| #include "SkLatticeIter.h" |
| #include "pipeline/skia/AnimatedDrawables.h" |
| #include <SkNoDrawCanvas.h> |
| |
| using namespace android; |
| using namespace android::uirenderer; |
| using namespace android::uirenderer::test; |
| |
| 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; |
| }; |
| |
| TEST(CanvasFrontend, saveCount) { |
| 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[CanvasOpType::Save]); |
| EXPECT_EQ(1, receiver[CanvasOpType::Restore]); |
| } |
| |
| TEST(CanvasFrontend, transform) { |
| SkNoDrawCanvas skiaCanvas(100, 100); |
| CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100); |
| |
| skiaCanvas.translate(10, 10); |
| opCanvas.translate(10, 10); |
| EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); |
| |
| { |
| skiaCanvas.save(); |
| opCanvas.save(SaveFlags::Matrix); |
| skiaCanvas.scale(2.0f, 1.125f); |
| opCanvas.scale(2.0f, 1.125f); |
| |
| EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); |
| skiaCanvas.restore(); |
| opCanvas.restore(); |
| } |
| |
| EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); |
| |
| { |
| skiaCanvas.save(); |
| opCanvas.save(SaveFlags::Matrix); |
| skiaCanvas.rotate(90.f); |
| opCanvas.rotate(90.f); |
| |
| EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); |
| |
| { |
| skiaCanvas.save(); |
| opCanvas.save(SaveFlags::Matrix); |
| skiaCanvas.skew(5.0f, 2.25f); |
| opCanvas.skew(5.0f, 2.25f); |
| |
| EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); |
| skiaCanvas.restore(); |
| opCanvas.restore(); |
| } |
| |
| skiaCanvas.restore(); |
| opCanvas.restore(); |
| } |
| |
| EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); |
| } |
| |
| TEST(CanvasFrontend, drawOpTransform) { |
| CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100); |
| const auto &receiver = opCanvas.receiver(); |
| |
| auto makeDrawRect = [] { |
| return CanvasOp<CanvasOpType::DrawRect>{ |
| .rect = SkRect::MakeWH(50, 50), |
| .paint = SkPaint(SkColors::kBlack), |
| }; |
| }; |
| |
| opCanvas.draw(makeDrawRect()); |
| |
| opCanvas.translate(10, 10); |
| opCanvas.draw(makeDrawRect()); |
| |
| opCanvas.save(); |
| opCanvas.scale(2.0f, 4.0f); |
| opCanvas.draw(makeDrawRect()); |
| opCanvas.restore(); |
| |
| opCanvas.save(); |
| opCanvas.translate(20, 15); |
| opCanvas.draw(makeDrawRect()); |
| opCanvas.save(); |
| opCanvas.rotate(90.f); |
| opCanvas.draw(makeDrawRect()); |
| opCanvas.restore(); |
| opCanvas.restore(); |
| |
| // Validate the results |
| std::vector<SkMatrix> transforms; |
| transforms.reserve(5); |
| receiver.for_each([&](auto op) { |
| // Filter for the DrawRect calls; ignore the save & restores |
| // (TODO: Add a filtered for_each variant to OpBuffer?) |
| if (op->type() == CanvasOpType::DrawRect) { |
| transforms.push_back(op->transform()); |
| } |
| }); |
| |
| EXPECT_EQ(transforms.size(), 5); |
| |
| { |
| // First result should be identity |
| const auto &result = transforms[0]; |
| EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType()); |
| EXPECT_EQ(SkMatrix::I(), result); |
| } |
| |
| { |
| // Should be translate 10, 10 |
| const auto &result = transforms[1]; |
| EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); |
| SkMatrix m; |
| m.setTranslate(10, 10); |
| EXPECT_EQ(m, result); |
| } |
| |
| { |
| // Should be translate 10, 10 + scale 2, 4 |
| const auto &result = transforms[2]; |
| EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType()); |
| SkMatrix m; |
| m.setTranslate(10, 10); |
| m.preScale(2.0f, 4.0f); |
| EXPECT_EQ(m, result); |
| } |
| |
| { |
| // Should be translate 10, 10 + translate 20, 15 |
| const auto &result = transforms[3]; |
| EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); |
| SkMatrix m; |
| m.setTranslate(30, 25); |
| EXPECT_EQ(m, result); |
| } |
| |
| { |
| // Should be translate 10, 10 + translate 20, 15 + rotate 90 |
| const auto &result = transforms[4]; |
| EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask, |
| result.getType()); |
| SkMatrix m; |
| m.setTranslate(30, 25); |
| m.preRotate(90.f); |
| EXPECT_EQ(m, result); |
| } |
| } |