|  | /* | 
|  | * Copyright 2014 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/SkCanvas.h" | 
|  | #include "include/core/SkClipOp.h" | 
|  | #include "include/core/SkColor.h" | 
|  | #include "include/core/SkFont.h" | 
|  | #include "include/core/SkFontTypes.h" | 
|  | #include "include/core/SkMatrix.h" | 
|  | #include "include/core/SkPaint.h" | 
|  | #include "include/core/SkPathBuilder.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/SkTypeface.h" | 
|  | #include "include/core/SkTypes.h" | 
|  | #include "include/effects/SkGradientShader.h" | 
|  | #include "tools/ToolUtils.h" | 
|  |  | 
|  | static sk_sp<SkImage> make_img(int w, int h) { | 
|  | auto surf = SkSurface::MakeRaster(SkImageInfo::MakeN32(w, h, kOpaque_SkAlphaType)); | 
|  | auto canvas = surf->getCanvas(); | 
|  |  | 
|  | SkScalar wScalar = SkIntToScalar(w); | 
|  | SkScalar hScalar = SkIntToScalar(h); | 
|  |  | 
|  | SkPoint     pt = { wScalar / 2, hScalar / 2 }; | 
|  |  | 
|  | SkScalar    radius = 3 * std::max(wScalar, hScalar); | 
|  |  | 
|  | SkColor colors[] = {SK_ColorDKGRAY, | 
|  | ToolUtils::color_to_565(0xFF222255), | 
|  | ToolUtils::color_to_565(0xFF331133), | 
|  | ToolUtils::color_to_565(0xFF884422), | 
|  | ToolUtils::color_to_565(0xFF000022), | 
|  | SK_ColorWHITE, | 
|  | ToolUtils::color_to_565(0xFFAABBCC)}; | 
|  |  | 
|  | SkScalar    pos[] = {0, | 
|  | SK_Scalar1 / 6, | 
|  | 2 * SK_Scalar1 / 6, | 
|  | 3 * SK_Scalar1 / 6, | 
|  | 4 * SK_Scalar1 / 6, | 
|  | 5 * SK_Scalar1 / 6, | 
|  | SK_Scalar1}; | 
|  |  | 
|  | SkPaint paint; | 
|  | SkRect rect = SkRect::MakeWH(wScalar, hScalar); | 
|  | SkMatrix mat = SkMatrix::I(); | 
|  | for (int i = 0; i < 4; ++i) { | 
|  | paint.setShader(SkGradientShader::MakeRadial( | 
|  | pt, radius, | 
|  | colors, pos, | 
|  | SK_ARRAY_COUNT(colors), | 
|  | SkTileMode::kRepeat, | 
|  | 0, &mat)); | 
|  | canvas->drawRect(rect, paint); | 
|  | rect.inset(wScalar / 8, hScalar / 8); | 
|  | mat.preTranslate(6 * wScalar, 6 * hScalar); | 
|  | mat.postScale(SK_Scalar1 / 3, SK_Scalar1 / 3); | 
|  | } | 
|  |  | 
|  | SkFont font(ToolUtils::create_portable_typeface(), wScalar / 2.2f); | 
|  |  | 
|  | paint.setShader(nullptr); | 
|  | paint.setColor(SK_ColorLTGRAY); | 
|  | constexpr char kTxt[] = "Skia"; | 
|  | SkPoint texPos = { wScalar / 17, hScalar / 2 + font.getSize() / 2.5f }; | 
|  | canvas->drawSimpleText(kTxt, SK_ARRAY_COUNT(kTxt)-1, SkTextEncoding::kUTF8, | 
|  | texPos.fX, texPos.fY, font, paint); | 
|  | paint.setColor(SK_ColorBLACK); | 
|  | paint.setStyle(SkPaint::kStroke_Style); | 
|  | paint.setStrokeWidth(SK_Scalar1); | 
|  | canvas->drawSimpleText(kTxt, SK_ARRAY_COUNT(kTxt)-1, SkTextEncoding::kUTF8, | 
|  | texPos.fX, texPos.fY, font, paint); | 
|  | return surf->makeImageSnapshot(); | 
|  | } | 
|  |  | 
|  | namespace skiagm { | 
|  | /** | 
|  | * This GM tests convex polygon clips. | 
|  | */ | 
|  | class ConvexPolyClip : public GM { | 
|  | public: | 
|  | ConvexPolyClip() { | 
|  | this->setBGColor(0xFFFFFFFF); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | SkString onShortName() override { | 
|  | return SkString("convex_poly_clip"); | 
|  | } | 
|  |  | 
|  | SkISize onISize() override { | 
|  | // When benchmarking the saveLayer set of draws is skipped. | 
|  | int w = 435; | 
|  | if (kBench_Mode != this->getMode()) { | 
|  | w *= 2; | 
|  | } | 
|  | return SkISize::Make(w, 540); | 
|  | } | 
|  |  | 
|  | void onOnceBeforeDraw() override { | 
|  | // On < c++17, emplace_back() returns a void :( | 
|  | auto emplace_back = [](std::vector<Clip>& clips) -> Clip& { | 
|  | clips.emplace_back(); | 
|  | return clips.back(); | 
|  | }; | 
|  |  | 
|  | emplace_back(fClips).setPath(SkPath::Polygon({ | 
|  | {  5.f,   5.f}, | 
|  | {100.f,  20.f}, | 
|  | { 15.f, 100.f}, | 
|  | }, false)); | 
|  |  | 
|  | SkPathBuilder hexagon; | 
|  | constexpr SkScalar kRadius = 45.f; | 
|  | const SkPoint center = { kRadius, kRadius }; | 
|  | for (int i = 0; i < 6; ++i) { | 
|  | SkScalar angle = 2 * SK_ScalarPI * i / 6; | 
|  | SkPoint point = { SkScalarCos(angle), SkScalarSin(angle) }; | 
|  | point.scale(kRadius); | 
|  | point = center + point; | 
|  | if (0 == i) { | 
|  | hexagon.moveTo(point); | 
|  | } else { | 
|  | hexagon.lineTo(point); | 
|  | } | 
|  | } | 
|  | emplace_back(fClips).setPath(hexagon.snapshot()); | 
|  |  | 
|  | SkMatrix scaleM; | 
|  | scaleM.setScale(1.1f, 0.4f, kRadius, kRadius); | 
|  | emplace_back(fClips).setPath(hexagon.detach().makeTransform(scaleM)); | 
|  |  | 
|  | emplace_back(fClips).setRect(SkRect::MakeXYWH(8.3f, 11.6f, 78.2f, 72.6f)); | 
|  |  | 
|  | SkRect rect = SkRect::MakeLTRB(10.f, 12.f, 80.f, 86.f); | 
|  | SkMatrix rotM; | 
|  | rotM.setRotate(23.f, rect.centerX(), rect.centerY()); | 
|  | emplace_back(fClips).setPath(SkPath::Rect(rect).makeTransform(rotM)); | 
|  |  | 
|  | fImg = make_img(100, 100); | 
|  | } | 
|  |  | 
|  | void onDraw(SkCanvas* canvas) override { | 
|  | SkScalar y = 0; | 
|  | constexpr SkScalar kMargin = 10.f; | 
|  |  | 
|  | SkPaint bgPaint; | 
|  | bgPaint.setAlpha(0x15); | 
|  | SkISize size = canvas->getBaseLayerSize(); | 
|  | canvas->drawImageRect(fImg, SkRect::MakeIWH(size.fWidth, size.fHeight), | 
|  | SkSamplingOptions(), &bgPaint); | 
|  |  | 
|  | constexpr char kTxt[] = "Clip Me!"; | 
|  | SkFont         font(ToolUtils::create_portable_typeface(), 23); | 
|  | SkScalar textW = font.measureText(kTxt, SK_ARRAY_COUNT(kTxt)-1, SkTextEncoding::kUTF8); | 
|  | SkPaint txtPaint; | 
|  | txtPaint.setColor(SK_ColorDKGRAY); | 
|  |  | 
|  | SkScalar startX = 0; | 
|  | int testLayers = kBench_Mode != this->getMode(); | 
|  | for (int doLayer = 0; doLayer <= testLayers; ++doLayer) { | 
|  | for (const Clip& clip : fClips) { | 
|  | SkScalar x = startX; | 
|  | for (int aa = 0; aa < 2; ++aa) { | 
|  | if (doLayer) { | 
|  | SkRect bounds; | 
|  | clip.getBounds(&bounds); | 
|  | bounds.outset(2, 2); | 
|  | bounds.offset(x, y); | 
|  | canvas->saveLayer(&bounds, nullptr); | 
|  | } else { | 
|  | canvas->save(); | 
|  | } | 
|  | canvas->translate(x, y); | 
|  | clip.setOnCanvas(canvas, SkClipOp::kIntersect, SkToBool(aa)); | 
|  | canvas->drawImage(fImg, 0, 0); | 
|  | canvas->restore(); | 
|  | x += fImg->width() + kMargin; | 
|  | } | 
|  | for (int aa = 0; aa < 2; ++aa) { | 
|  |  | 
|  | SkPaint clipOutlinePaint; | 
|  | clipOutlinePaint.setAntiAlias(true); | 
|  | clipOutlinePaint.setColor(0x50505050); | 
|  | clipOutlinePaint.setStyle(SkPaint::kStroke_Style); | 
|  | clipOutlinePaint.setStrokeWidth(0); | 
|  |  | 
|  | if (doLayer) { | 
|  | SkRect bounds; | 
|  | clip.getBounds(&bounds); | 
|  | bounds.outset(2, 2); | 
|  | bounds.offset(x, y); | 
|  | canvas->saveLayer(&bounds, nullptr); | 
|  | } else { | 
|  | canvas->save(); | 
|  | } | 
|  | canvas->translate(x, y); | 
|  | SkPath closedClipPath = clip.asClosedPath(); | 
|  | canvas->drawPath(closedClipPath, clipOutlinePaint); | 
|  | clip.setOnCanvas(canvas, SkClipOp::kIntersect, SkToBool(aa)); | 
|  | canvas->scale(1.f, 1.8f); | 
|  | canvas->drawSimpleText(kTxt, SK_ARRAY_COUNT(kTxt)-1, SkTextEncoding::kUTF8, | 
|  | 0, 1.5f * font.getSize(), font, txtPaint); | 
|  | canvas->restore(); | 
|  | x += textW + 2 * kMargin; | 
|  | } | 
|  | y += fImg->height() + kMargin; | 
|  | } | 
|  | y = 0; | 
|  | startX += 2 * fImg->width() + SkScalarCeilToInt(2 * textW) + 6 * kMargin; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool runAsBench() const override { return true; } | 
|  |  | 
|  | private: | 
|  | class Clip { | 
|  | public: | 
|  | enum ClipType { | 
|  | kNone_ClipType, | 
|  | kPath_ClipType, | 
|  | kRect_ClipType | 
|  | }; | 
|  |  | 
|  | Clip () : fClipType(kNone_ClipType) {} | 
|  |  | 
|  | void setOnCanvas(SkCanvas* canvas, SkClipOp op, bool aa) const { | 
|  | switch (fClipType) { | 
|  | case kPath_ClipType: | 
|  | canvas->clipPath(fPathBuilder.snapshot(), op, aa); | 
|  | break; | 
|  | case kRect_ClipType: | 
|  | canvas->clipRect(fRect, op, aa); | 
|  | break; | 
|  | case kNone_ClipType: | 
|  | SkDEBUGFAIL("Uninitialized Clip."); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | SkPath asClosedPath() const { | 
|  | switch (fClipType) { | 
|  | case kPath_ClipType: | 
|  | return SkPathBuilder(fPathBuilder).close().detach(); | 
|  | break; | 
|  | case kRect_ClipType: | 
|  | return SkPath::Rect(fRect); | 
|  | case kNone_ClipType: | 
|  | SkDEBUGFAIL("Uninitialized Clip."); | 
|  | break; | 
|  | } | 
|  | return SkPath(); | 
|  | } | 
|  |  | 
|  | void setPath(const SkPath& path) { | 
|  | fClipType = kPath_ClipType; | 
|  | fPathBuilder = path; | 
|  | } | 
|  |  | 
|  | void setRect(const SkRect& rect) { | 
|  | fClipType = kRect_ClipType; | 
|  | fRect = rect; | 
|  | fPathBuilder.reset(); | 
|  | } | 
|  |  | 
|  | ClipType getType() const { return fClipType; } | 
|  |  | 
|  | void getBounds(SkRect* bounds) const { | 
|  | switch (fClipType) { | 
|  | case kPath_ClipType: | 
|  | *bounds = fPathBuilder.computeBounds(); | 
|  | break; | 
|  | case kRect_ClipType: | 
|  | *bounds = fRect; | 
|  | break; | 
|  | case kNone_ClipType: | 
|  | SkDEBUGFAIL("Uninitialized Clip."); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | ClipType fClipType; | 
|  | SkPathBuilder fPathBuilder; | 
|  | SkRect fRect; | 
|  | }; | 
|  |  | 
|  | std::vector<Clip> fClips; | 
|  | sk_sp<SkImage>    fImg;; | 
|  |  | 
|  | using INHERITED = GM; | 
|  | }; | 
|  |  | 
|  | DEF_GM(return new ConvexPolyClip;) | 
|  | }  // namespace skiagm |