| /* |
| * 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" |
| |
| 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 onShortName() override { |
| return SkString("nonclosedpaths"); |
| } |
| |
| // 12 * 18 + 3 cases, every case is 100 * 100 pixels. |
| SkISize onISize() 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) |
| constexpr int kStrokeWidth[] = {0, 10, 40, 50}; |
| int numWidths = std::size(kStrokeWidth); |
| |
| constexpr SkPaint::Style kStyle[] = { |
| SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style |
| }; |
| |
| constexpr SkPaint::Cap kCap[] = { |
| SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap |
| }; |
| |
| constexpr SkPaint::Join kJoin[] = { |
| SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join |
| }; |
| |
| constexpr ClosureType kType[] = { |
| 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 |