| /* |
| * 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 |