| /* | |
| * Copyright 2018 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "gm/gm.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkMatrix.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPath.h" | |
| #include "include/core/SkPathBuilder.h" | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTypes.h" | |
| #include "src/core/SkRandom.h" | |
| #include "tools/ToolUtils.h" | |
| #include <array> | |
| #include <vector> | |
| namespace skiagm { | |
| static constexpr int kPadSize = 20; | |
| static constexpr int kBoxSize = 100; | |
| static constexpr SkPoint kJitters[] = {{0, 0}, {.5f, .5f}, {2/3.f, 1/3.f}}; | |
| // Tests various corners of different angles falling on the same pixel, particularly to ensure | |
| // analytic AA is working properly. | |
| class SharedCornersGM : public GM { | |
| public: | |
| SharedCornersGM() { this->setBGColor(ToolUtils::color_to_565(0xFF1A65D7)); } | |
| protected: | |
| SkString getName() const override { return SkString("sharedcorners"); } | |
| SkISize getISize() override { | |
| constexpr int numRows = 3 * 2; | |
| constexpr int numCols = (1 + std::size(kJitters)) * 2; | |
| return SkISize::Make(numCols * (kBoxSize + kPadSize) + kPadSize, | |
| numRows * (kBoxSize + kPadSize) + kPadSize); | |
| } | |
| void onOnceBeforeDraw() override { | |
| fFillPaint.setColor(SK_ColorWHITE); | |
| fFillPaint.setAntiAlias(true); | |
| fWireFramePaint = fFillPaint; | |
| fWireFramePaint.setStyle(SkPaint::kStroke_Style); | |
| } | |
| void onDraw(SkCanvas* canvas) override { | |
| canvas->translate(kPadSize, kPadSize); | |
| canvas->save(); | |
| // Adjacent rects. | |
| this->drawTriangleBoxes(canvas, | |
| {{0, 0}, {40, 0}, {80, 0}, {120, 0}, | |
| {0, 20}, {40, 20}, {80, 20}, {120, 20}, | |
| {40, 40}, {80, 40}, | |
| {40, 60}, {80, 60}}, | |
| {{{0, 1, 4}}, {{1, 5, 4}}, | |
| {{5, 1, 6}}, {{1, 2, 6}}, | |
| {{2, 3, 6}}, {{3, 7, 6}}, | |
| {{8, 5, 9}}, {{5, 6, 9}}, | |
| {{10, 8, 11}}, {{8, 9, 11}}}); | |
| // Obtuse angles. | |
| this->drawTriangleBoxes(canvas, | |
| {{ 0, 0}, {10, 0}, {20, 0}, | |
| { 0, 2}, {20, 2}, | |
| {10, 4}, | |
| { 0, 6}, {20, 6}, | |
| { 0, 8}, {10, 8}, {20, 8}}, | |
| {{{3, 1, 4}}, {{4, 5, 3}}, {{6, 5, 7}}, {{7, 9, 6}}, | |
| {{0, 1, 3}}, {{1, 2, 4}}, | |
| {{3, 5, 6}}, {{5, 4, 7}}, | |
| {{6, 9, 8}}, {{9, 7, 10}}}); | |
| canvas->restore(); | |
| canvas->translate((kBoxSize + kPadSize) * 4, 0); | |
| // Right angles. | |
| this->drawTriangleBoxes(canvas, | |
| {{0, 0}, {-1, 0}, {0, -1}, {1, 0}, {0, 1}}, | |
| {{{0, 1, 2}}, {{0, 2, 3}}, {{0, 3, 4}}, {{0, 4, 1}}}); | |
| // Acute angles. | |
| SkRandom rand; | |
| std::vector<SkPoint> pts; | |
| std::vector<std::array<int, 3>> indices; | |
| SkScalar theta = 0; | |
| pts.push_back({0, 0}); | |
| while (theta < 2*SK_ScalarPI) { | |
| pts.push_back({SkScalarCos(theta), SkScalarSin(theta)}); | |
| if (pts.size() > 2) { | |
| indices.push_back({{0, (int)pts.size() - 2, (int)pts.size() - 1}}); | |
| } | |
| theta += rand.nextRangeF(0, SK_ScalarPI/3); | |
| } | |
| indices.push_back({{0, (int)pts.size() - 1, 1}}); | |
| this->drawTriangleBoxes(canvas, pts, indices); | |
| } | |
| void drawTriangleBoxes(SkCanvas* canvas, const std::vector<SkPoint>& points, | |
| const std::vector<std::array<int, 3>>& triangles) { | |
| SkPathBuilder builder(SkPathFillType::kEvenOdd); | |
| builder.setIsVolatile(true); | |
| for (const std::array<int, 3>& triangle : triangles) { | |
| builder.moveTo(points[triangle[0]]); | |
| builder.lineTo(points[triangle[1]]); | |
| builder.lineTo(points[triangle[2]]); | |
| builder.close(); | |
| } | |
| const SkRect bounds = builder.computeBounds(); | |
| SkScalar scale = kBoxSize / std::max(bounds.height(), bounds.width()); | |
| builder.transform(SkMatrix::Scale(scale, scale)); | |
| SkPath path = builder.detach(); | |
| this->drawRow(canvas, path); | |
| canvas->translate(0, kBoxSize + kPadSize); | |
| SkMatrix rot; | |
| rot.setRotate(45, path.getBounds().centerX(), path.getBounds().centerY()); | |
| path = path.makeTransform(rot); | |
| this->drawRow(canvas, path); | |
| canvas->translate(0, kBoxSize + kPadSize); | |
| rot.setRotate(-45 - 69.38111f, path.getBounds().centerX(), path.getBounds().centerY()); | |
| path = path.makeTransform(rot); | |
| this->drawRow(canvas, path); | |
| canvas->translate(0, kBoxSize + kPadSize); | |
| } | |
| void drawRow(SkCanvas* canvas, const SkPath& path) { | |
| SkAutoCanvasRestore acr(canvas, true); | |
| const SkRect& bounds = path.getBounds(); | |
| canvas->translate((kBoxSize - bounds.width()) / 2 - bounds.left(), | |
| (kBoxSize - bounds.height()) / 2 - bounds.top()); | |
| canvas->drawPath(path, fWireFramePaint); | |
| canvas->translate(kBoxSize + kPadSize, 0); | |
| for (SkPoint jitter : kJitters) { | |
| { | |
| SkAutoCanvasRestore acr2(canvas, true); | |
| canvas->translate(jitter.x(), jitter.y()); | |
| canvas->drawPath(path, fFillPaint); | |
| } | |
| canvas->translate(kBoxSize + kPadSize, 0); | |
| } | |
| } | |
| SkPaint fWireFramePaint; | |
| SkPaint fFillPaint; | |
| }; | |
| DEF_GM(return new SharedCornersGM;) | |
| } // namespace skiagm |