/* * 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 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 fPaints; TArray 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 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 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