| /* |
| * 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/SkGradientShader.h" |
| #include "include/private/base/SkTArray.h" |
| #include "include/private/base/SkTemplates.h" |
| #include "tools/ToolUtils.h" |
| |
| #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 |
| SkColor colors[] = { SK_ColorRED, SK_ColorGREEN }; |
| SkPoint pts[] = { {-kRadius-kPad, -kRadius-kPad }, { kRadius+kPad, kRadius+kPad } }; |
| |
| SkPaint p; |
| p.setShader(SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp)); |
| |
| fPaints.push_back(p); |
| } |
| { |
| // dashing |
| SkScalar intervals[] = { kStrokeWidth, kStrokeWidth }; |
| int intervalCount = (int) std::size(intervals); |
| SkPaint p; |
| p.setColor(SK_ColorWHITE); |
| p.setPathEffect(SkDashPathEffect::Make(intervals, intervalCount, 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 SkColor kColors[] {SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE}; |
| constexpr SkScalar kStops[] {0.f, 0.75f, 1.f}; |
| sk_sp<SkShader> shader = SkGradientShader::MakeLinear(kPts, kColors, kStops, 3, |
| 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; |
| SkPath path = SkPath::Line(p0, p1); |
| skpathutils::FillPathWithPaint(path, paint, &fillPath); |
| |
| 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); |
| int n = fillPath.countPoints(); |
| AutoTArray<SkPoint> points(n); |
| fillPath.getPoints(points.get(), n); |
| canvas->drawPoints(SkCanvas::kPoints_PointMode, n, points.get(), paint); |
| } |
| |
| DEF_SIMPLE_GM(strokedline_caps, canvas, 1400, 740) { |
| canvas->translate(kStrokeWidth*3/2, kStrokeWidth*3/2); |
| |
| constexpr SkPaint::Cap kCaps[] = { |
| SkPaint::kSquare_Cap, SkPaint::kButt_Cap, SkPaint::kRound_Cap |
| }; |
| |
| constexpr float kLengths[] = { |
| 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 |