| /* | |
| * Copyright 2013 Google Inc. | |
| * | |
| * 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/SkPaint.h" | |
| #include "include/core/SkPathBuilder.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTypes.h" | |
| #include <array> | |
| namespace skiagm { | |
| // This GM tests a grab-bag of non-closed paths. All these paths look like | |
| // closed rects, but they don't call path.close(). Depending on the stroke | |
| // settings these slightly different paths give widely different results. | |
| class NonClosedPathsGM: public GM { | |
| public: | |
| NonClosedPathsGM() {} | |
| enum ClosureType { | |
| TotallyNonClosed, // The last point doesn't coincide with the first one in the contour. | |
| // The path looks not closed at all. | |
| FakeCloseCorner, // The last point coincides with the first one at a corner. | |
| // The path looks closed, but final rendering has 2 ends with cap. | |
| FakeCloseMiddle, // The last point coincides with the first one in the middle of a line. | |
| // The path looks closed, and the final rendering looks closed too. | |
| kClosureTypeCount | |
| }; | |
| protected: | |
| SkString getName() const override { return SkString("nonclosedpaths"); } | |
| // 12 * 18 + 3 cases, every case is 100 * 100 pixels. | |
| SkISize getISize() override { return SkISize::Make(1220, 1920); } | |
| // Use rect-like geometry for non-closed path, for right angles make it | |
| // easier to show the visual difference of lineCap and lineJoin. | |
| static SkPath MakePath(ClosureType type) { | |
| SkPathBuilder path; | |
| if (FakeCloseMiddle == type) { | |
| path.moveTo(30, 50); | |
| path.lineTo(30, 30); | |
| } else { | |
| path.moveTo(30, 30); | |
| } | |
| path.lineTo(70, 30); | |
| path.lineTo(70, 70); | |
| path.lineTo(30, 70); | |
| path.lineTo(30, 50); | |
| if (FakeCloseCorner == type) { | |
| path.lineTo(30, 30); | |
| } | |
| return path.detach(); | |
| } | |
| // Set the location for the current test on the canvas | |
| static void SetLocation(SkCanvas* canvas, int counter, int lineNum) { | |
| SkScalar x = SK_Scalar1 * 100 * (counter % lineNum) + 10 + SK_Scalar1 / 4; | |
| SkScalar y = SK_Scalar1 * 100 * (counter / lineNum) + 10 + 3 * SK_Scalar1 / 4; | |
| canvas->translate(x, y); | |
| } | |
| void onDraw(SkCanvas* canvas) override { | |
| // Stroke widths are: | |
| // 0(may use hairline rendering), 10(common case for stroke-style) | |
| // 40 and 50(>= geometry width/height, make the contour filled in fact) | |
| static constexpr auto kStrokeWidth = std::to_array<int>({0, 10, 40, 50}); | |
| int numWidths = std::size(kStrokeWidth); | |
| static constexpr auto kStyle = std::to_array<SkPaint::Style>( | |
| {SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style}); | |
| static constexpr auto kCap = std::to_array<SkPaint::Cap>({ | |
| SkPaint::kButt_Cap, | |
| SkPaint::kRound_Cap, | |
| SkPaint::kSquare_Cap, | |
| }); | |
| static constexpr auto kJoin = std::to_array<SkPaint::Join>({ | |
| SkPaint::kMiter_Join, | |
| SkPaint::kRound_Join, | |
| SkPaint::kBevel_Join, | |
| }); | |
| static constexpr auto kType = std::to_array<ClosureType>({ | |
| TotallyNonClosed, | |
| FakeCloseCorner, | |
| FakeCloseMiddle, | |
| }); | |
| int counter = 0; | |
| SkPaint paint; | |
| paint.setAntiAlias(true); | |
| // For stroke style painter and fill-and-stroke style painter | |
| for (size_t type = 0; type < kClosureTypeCount; ++type) { | |
| for (size_t style = 0; style < std::size(kStyle); ++style) { | |
| for (size_t cap = 0; cap < std::size(kCap); ++cap) { | |
| for (size_t join = 0; join < std::size(kJoin); ++join) { | |
| for (int width = 0; width < numWidths; ++width) { | |
| canvas->save(); | |
| SetLocation(canvas, counter, SkPaint::kJoinCount * numWidths); | |
| SkPath path = MakePath(kType[type]); | |
| paint.setStyle(kStyle[style]); | |
| paint.setStrokeCap(kCap[cap]); | |
| paint.setStrokeJoin(kJoin[join]); | |
| paint.setStrokeWidth(SkIntToScalar(kStrokeWidth[width])); | |
| canvas->drawPath(path, paint); | |
| canvas->restore(); | |
| ++counter; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // For fill style painter | |
| paint.setStyle(SkPaint::kFill_Style); | |
| for (size_t type = 0; type < kClosureTypeCount; ++type) { | |
| canvas->save(); | |
| SetLocation(canvas, counter, SkPaint::kJoinCount * numWidths); | |
| SkPath path = MakePath(kType[type]); | |
| canvas->drawPath(path, paint); | |
| canvas->restore(); | |
| ++counter; | |
| } | |
| } | |
| private: | |
| using INHERITED = GM; | |
| }; | |
| ////////////////////////////////////////////////////////////////////////////// | |
| DEF_GM(return new NonClosedPathsGM;) | |
| } // namespace skiagm |