| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "gm.h" |
| #include "SkCanvas.h" |
| #include "SkGeometry.h" |
| #include "SkPath.h" |
| #include "SkPaint.h" |
| #include "SkStrokeRec.h" |
| |
| static constexpr float kStrokeWidth = 40; |
| static constexpr int kCellSize = 200; |
| |
| static const SkPoint kCubics[][4] = { |
| {{122, 737}, {348, 553}, {403, 761}, {400, 760}}, |
| {{244, 520}, {244, 518}, {1141, 634}, {394, 688}}, |
| {{550, 194}, {138, 130}, {1035, 246}, {288, 300}}, |
| {{226, 733}, {556, 779}, {-43, 471}, {348, 683}}, |
| {{268, 204}, {492, 304}, {352, 23}, {433, 412}}, |
| {{172, 480}, {396, 580}, {256, 299}, {338, 677}}, |
| {{731, 340}, {318, 252}, {1026, -64}, {367, 265}}, |
| {{475, 708}, {62, 620}, {770, 304}, {220, 659}}, |
| }; |
| |
| static SkRect calc_tight_cubic_bounds(const SkPoint P[4], int depth=5) { |
| if (0 == depth) { |
| SkRect bounds; |
| bounds.fLeft = SkTMin(SkTMin(P[0].x(), P[1].x()), SkTMin(P[2].x(), P[3].x())); |
| bounds.fTop = SkTMin(SkTMin(P[0].y(), P[1].y()), SkTMin(P[2].y(), P[3].y())); |
| bounds.fRight = SkTMax(SkTMax(P[0].x(), P[1].x()), SkTMax(P[2].x(), P[3].x())); |
| bounds.fBottom = SkTMax(SkTMax(P[0].y(), P[1].y()), SkTMax(P[2].y(), P[3].y())); |
| return bounds; |
| } |
| |
| SkPoint chopped[7]; |
| SkChopCubicAt(P, chopped, .5f); |
| SkRect bounds = calc_tight_cubic_bounds(chopped, depth - 1); |
| bounds.join(calc_tight_cubic_bounds(chopped+3, depth - 1)); |
| return bounds; |
| } |
| |
| // This is a compilation of cubics that have given strokers grief. Feel free to add more. |
| class TrickyCubicStrokesGM : public skiagm::GM { |
| public: |
| TrickyCubicStrokesGM() {} |
| |
| protected: |
| |
| SkString onShortName() override { |
| return SkString("trickycubicstrokes"); |
| } |
| |
| SkISize onISize() override { |
| return SkISize::Make(3*kCellSize, 3*kCellSize); |
| } |
| |
| void onOnceBeforeDraw() override { |
| fStrokePaint.setAntiAlias(true); |
| fStrokePaint.setStrokeWidth(kStrokeWidth); |
| fStrokePaint.setColor(SK_ColorGREEN); |
| fStrokePaint.setStyle(SkPaint::kStroke_Style); |
| } |
| |
| void onDraw(SkCanvas* canvas) override { |
| canvas->clear(SK_ColorBLACK); |
| |
| for (size_t i = 0; i < SK_ARRAY_COUNT(kCubics); ++i) { |
| this->drawStroke(canvas, kCubics[i], |
| SkRect::MakeXYWH((i%3) * kCellSize, (i/3) * kCellSize, kCellSize, |
| kCellSize)); |
| } |
| } |
| |
| void drawStroke(SkCanvas* canvas, const SkPoint P[4], const SkRect& location) { |
| SkRect strokeBounds = calc_tight_cubic_bounds(P); |
| strokeBounds.outset(kStrokeWidth, kStrokeWidth); |
| |
| SkMatrix matrix; |
| matrix.setRectToRect(strokeBounds, location, SkMatrix::kCenter_ScaleToFit); |
| |
| SkPath path; |
| path.moveTo(P[0]); |
| path.cubicTo(P[1], P[2], P[3]); |
| |
| SkAutoCanvasRestore acr(canvas, true); |
| canvas->concat(matrix); |
| canvas->drawPath(path, fStrokePaint); |
| } |
| |
| private: |
| SkPaint fStrokePaint; |
| typedef GM INHERITED; |
| }; |
| |
| DEF_GM( return new TrickyCubicStrokesGM; ) |