| /* | |
| * Copyright 2016 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/SkBitmap.h" | |
| #include "include/core/SkBlurTypes.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkMaskFilter.h" | |
| #include "include/core/SkMatrix.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPath.h" | |
| #include "include/core/SkPathEffect.h" | |
| #include "include/core/SkPathUtils.h" | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkShader.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTileMode.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/effects/SkDashPathEffect.h" | |
| #include "include/effects/SkGradient.h" | |
| #include "include/private/SkTArray.h" | |
| #include "include/private/SkTemplates.h" | |
| #include "tools/ToolUtils.h" | |
| #include <array> | |
| #include <initializer_list> | |
| using namespace skia_private; | |
| constexpr int kNumColumns = 6; | |
| constexpr int kNumRows = 8; | |
| constexpr int kRadius = 40; // radius of the snowflake | |
| constexpr int kPad = 5; // padding on both sides of the snowflake | |
| constexpr int kNumSpokes = 6; | |
| constexpr SkScalar kStrokeWidth = 5.0f; | |
| static void draw_line(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1, | |
| const SkPaint& paint, bool useDrawPath) { | |
| if (useDrawPath) { | |
| canvas->drawPath(SkPath::Line(p0, p1), paint); | |
| } else { | |
| canvas->drawLine(p0, p1, paint); | |
| } | |
| } | |
| static void draw_fins(SkCanvas* canvas, const SkPoint& offset, float angle, const SkPaint& paint, | |
| bool useDrawPath) { | |
| SkScalar cos, sin; | |
| // first fin | |
| sin = SkScalarSin(angle + (SK_ScalarPI/4)); | |
| cos = SkScalarCos(angle + (SK_ScalarPI/4)); | |
| sin *= kRadius / 2.0f; | |
| cos *= kRadius / 2.0f; | |
| draw_line(canvas, offset, offset + SkPoint{cos, sin}, paint, useDrawPath); | |
| // second fin | |
| sin = SkScalarSin(angle - (SK_ScalarPI/4)); | |
| cos = SkScalarCos(angle - (SK_ScalarPI/4)); | |
| sin *= kRadius / 2.0f; | |
| cos *= kRadius / 2.0f; | |
| draw_line(canvas, offset, offset + SkPoint{cos, sin}, paint, useDrawPath); | |
| } | |
| // draw a snowflake centered at the origin | |
| static void draw_snowflake(SkCanvas* canvas, const SkPaint& paint, bool useDrawPath) { | |
| canvas->clipRect(SkRect::MakeLTRB(-kRadius-kPad, -kRadius-kPad, kRadius+kPad, kRadius+kPad)); | |
| SkScalar sin, cos, angle = 0.0f; | |
| for (int i = 0; i < kNumSpokes/2; ++i, angle += SK_ScalarPI/(kNumSpokes/2)) { | |
| sin = SkScalarSin(angle); | |
| cos = SkScalarCos(angle); | |
| sin *= kRadius; | |
| cos *= kRadius; | |
| // main spoke | |
| draw_line(canvas, {-cos, -sin}, {cos, sin}, paint, useDrawPath); | |
| // fins on positive side | |
| const SkPoint posOffset = SkPoint::Make(0.5f * cos, 0.5f * sin); | |
| draw_fins(canvas, posOffset, angle, paint, useDrawPath); | |
| // fins on negative side | |
| const SkPoint negOffset = SkPoint::Make(-0.5f * cos, -0.5f * sin); | |
| draw_fins(canvas, negOffset, angle+SK_ScalarPI, paint, useDrawPath); | |
| } | |
| } | |
| static void draw_row(SkCanvas* canvas, const SkPaint& paint, const SkMatrix& localMatrix, | |
| bool useDrawPath) { | |
| canvas->translate(kRadius+kPad, 0.0f); | |
| for (auto cap : { SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap }) { | |
| for (auto isAA : { true, false }) { | |
| SkPaint tmp(paint); | |
| tmp.setStrokeWidth(kStrokeWidth); | |
| tmp.setStyle(SkPaint::kStroke_Style); | |
| tmp.setStrokeCap(cap); | |
| tmp.setAntiAlias(isAA); | |
| int saveCount = canvas->save(); | |
| canvas->concat(localMatrix); | |
| draw_snowflake(canvas, tmp, useDrawPath); | |
| canvas->restoreToCount(saveCount); | |
| canvas->translate(2*(kRadius+kPad), 0.0f); | |
| } | |
| } | |
| } | |
| namespace skiagm { | |
| // This GM exercises the special case of a stroked lines. | |
| // Various shaders are applied to ensure the coordinate spaces work out right. | |
| class StrokedLinesGM : public GM { | |
| public: | |
| StrokedLinesGM(bool useDrawPath) : fUseDrawPath(useDrawPath) { | |
| this->setBGColor(ToolUtils::color_to_565(0xFF1A65D7)); | |
| } | |
| protected: | |
| SkString getName() const override { | |
| // To preserve history, useDrawPath==true has no suffix. | |
| SkString name{"strokedlines"}; | |
| if (!fUseDrawPath) { | |
| name.append("_drawPoints"); | |
| } | |
| return name; | |
| } | |
| SkISize getISize() override { | |
| return SkISize::Make(kNumColumns * (2*kRadius+2*kPad), kNumRows * (2*kRadius+2*kPad)); | |
| } | |
| void onOnceBeforeDraw() override { | |
| // paints | |
| { | |
| // basic white | |
| SkPaint p; | |
| p.setColor(SK_ColorWHITE); | |
| fPaints.push_back(p); | |
| } | |
| { | |
| // gradient | |
| SkColor4f colors[] = { SkColors::kRed, SkColors::kGreen }; | |
| SkPoint pts[] = { {-kRadius-kPad, -kRadius-kPad }, { kRadius+kPad, kRadius+kPad } }; | |
| SkPaint p; | |
| p.setShader(SkShaders::LinearGradient(pts, {{colors, {}, SkTileMode::kClamp}, {}})); | |
| fPaints.push_back(p); | |
| } | |
| { | |
| // dashing | |
| SkScalar intervals[] = { kStrokeWidth, kStrokeWidth }; | |
| SkPaint p; | |
| p.setColor(SK_ColorWHITE); | |
| p.setPathEffect(SkDashPathEffect::Make(intervals, kStrokeWidth)); | |
| fPaints.push_back(p); | |
| } | |
| { | |
| // Bitmap shader | |
| SkBitmap bm; | |
| bm.allocN32Pixels(2, 2); | |
| *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF; | |
| *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = 0x0; | |
| SkMatrix m; | |
| m.setRotate(12.0f); | |
| m.preScale(3.0f, 3.0f); | |
| SkPaint p; | |
| p.setShader(bm.makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, | |
| SkSamplingOptions(), m)); | |
| fPaints.push_back(p); | |
| } | |
| { | |
| // blur | |
| SkPaint p; | |
| p.setColor(SK_ColorWHITE); | |
| p.setMaskFilter(SkMaskFilter::MakeBlur(kOuter_SkBlurStyle, 3.0f)); | |
| fPaints.push_back(p); | |
| } | |
| // matrices | |
| { | |
| // rotation | |
| SkMatrix m; | |
| m.setRotate(12.0f); | |
| fMatrices.push_back(m); | |
| } | |
| { | |
| // skew | |
| SkMatrix m; | |
| m.setSkew(0.3f, 0.5f); | |
| fMatrices.push_back(m); | |
| } | |
| { | |
| // perspective | |
| SkMatrix m; | |
| m.reset(); | |
| m.setPerspX(-SK_Scalar1 / 300); | |
| m.setPerspY(SK_Scalar1 / 300); | |
| fMatrices.push_back(m); | |
| } | |
| SkASSERT(kNumRows == fPaints.size() + fMatrices.size()); | |
| } | |
| void onDraw(SkCanvas* canvas) override { | |
| canvas->translate(0, kRadius+kPad); | |
| for (int i = 0; i < fPaints.size(); ++i) { | |
| int saveCount = canvas->save(); | |
| draw_row(canvas, fPaints[i], SkMatrix::I(), fUseDrawPath); | |
| canvas->restoreToCount(saveCount); | |
| canvas->translate(0, 2*(kRadius+kPad)); | |
| } | |
| for (int i = 0; i < fMatrices.size(); ++i) { | |
| int saveCount = canvas->save(); | |
| draw_row(canvas, fPaints[0], fMatrices[i], fUseDrawPath); | |
| canvas->restoreToCount(saveCount); | |
| canvas->translate(0, 2*(kRadius+kPad)); | |
| } | |
| } | |
| private: | |
| TArray<SkPaint> fPaints; | |
| TArray<SkMatrix> fMatrices; | |
| const bool fUseDrawPath; | |
| using INHERITED = GM; | |
| }; | |
| ////////////////////////////////////////////////////////////////////////////// | |
| DEF_GM(return new StrokedLinesGM(true);) | |
| DEF_GM(return new StrokedLinesGM(false);) | |
| ////////////////////////////////////////////////////////////////////////////// | |
| static constexpr float kStrokeWidth = 20.f; | |
| static void draw_path(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1, SkPaint::Cap cap) { | |
| // Add a gradient *not* aligned with the line's points to show local coords are tracked properly | |
| constexpr SkRect kRect {-kStrokeWidth, -kStrokeWidth, 2*kStrokeWidth, 4*kStrokeWidth}; | |
| constexpr SkPoint kPts[] {{kRect.fLeft, kRect.fTop}, {kRect.fRight, kRect.fBottom}}; | |
| constexpr SkColor4f kColors[] {SkColors::kRed, SkColors::kGreen, SkColors::kBlue}; | |
| constexpr SkScalar kStops[] {0.f, 0.75f, 1.f}; | |
| sk_sp<SkShader> shader = SkShaders::LinearGradient(kPts, | |
| {{kColors, kStops, SkTileMode::kClamp}, {}}); | |
| SkPaint paint; | |
| paint.setAntiAlias(true); | |
| paint.setStyle(SkPaint::kStroke_Style); | |
| paint.setShader(std::move(shader)); | |
| paint.setStrokeWidth(kStrokeWidth); | |
| paint.setStrokeCap(cap); | |
| canvas->drawLine(p0, p1, paint); | |
| // Show outline and control points | |
| SkPath fillPath = skpathutils::FillPathWithPaint(SkPath::Line(p0, p1), paint); | |
| paint.setStyle(SkPaint::kStroke_Style); | |
| paint.setStrokeWidth(0); | |
| paint.setShader(nullptr); | |
| paint.setColor(SK_ColorRED); | |
| canvas->drawPath(fillPath, paint); | |
| paint.setStrokeWidth(3); | |
| paint.setStrokeCap(SkPaint::kSquare_Cap); | |
| canvas->drawPoints(SkCanvas::kPoints_PointMode, fillPath.points(), paint); | |
| } | |
| DEF_SIMPLE_GM(strokedline_caps, canvas, 1400, 740) { | |
| canvas->translate(kStrokeWidth*3/2, kStrokeWidth*3/2); | |
| static constexpr auto kCaps = std::to_array<SkPaint::Cap>({ | |
| SkPaint::kSquare_Cap, | |
| SkPaint::kButt_Cap, | |
| SkPaint::kRound_Cap, | |
| }); | |
| static constexpr auto kLengths = std::to_array<float>({ | |
| 4 * kStrokeWidth, | |
| kStrokeWidth, | |
| kStrokeWidth / 2, | |
| kStrokeWidth / 4, | |
| }); | |
| for (size_t i = 0; i < std::size(kCaps); ++i) { | |
| SkAutoCanvasRestore acr(canvas, true); | |
| auto drawLine = [&](float x0, float y0, float x1, float y1) { | |
| draw_path(canvas, {x0, y0}, {x1, y1}, kCaps[i]); | |
| canvas->translate(std::max(x0, x1) + 2 * kStrokeWidth, 0); | |
| }; | |
| for (size_t j = 0; j < std::size(kLengths); ++j) { | |
| float l = kLengths[j]; | |
| drawLine(0.f, 0.f, l, l); | |
| drawLine(l, l, 0.f, 0.f); | |
| drawLine(l/2, 0, l/2, l); | |
| drawLine(0, l/2, l, l/2); | |
| } | |
| drawLine(kStrokeWidth/2, kStrokeWidth/2, kStrokeWidth/2, kStrokeWidth/2); | |
| acr.restore(); | |
| canvas->translate(0, kLengths[0] + 2 * kStrokeWidth); | |
| } | |
| } | |
| } // namespace skiagm |