| /* | |
| * Copyright 2015 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "SampleCode.h" | |
| #include "SkCanvas.h" | |
| #include "SkInterpolator.h" | |
| #include "SkPath.h" | |
| #include "SkRRect.h" | |
| #include "SkTime.h" | |
| // This slide tests out the match up between BW clipping and rendering. It can | |
| // draw a large rect through some clip geometry and draw the same geometry | |
| // normally. Which one is drawn first can be toggled. The pair of objects is translated | |
| // fractionally (via an animator) to expose snapping bugs. The key bindings are: | |
| // 1-9: the different geometries | |
| // t: toggle which is drawn first the clip or the normal geometry | |
| // f: flip-flops which corner the bottom AA clip rect occupies in the complex clip cases | |
| // The possible geometric combinations to test | |
| enum Geometry { | |
| kRect_Geometry, | |
| kRRect_Geometry, | |
| kCircle_Geometry, | |
| kConvexPath_Geometry, | |
| kConcavePath_Geometry, | |
| kRectAndRect_Geometry, | |
| kRectAndRRect_Geometry, | |
| kRectAndConvex_Geometry, | |
| kRectAndConcave_Geometry | |
| }; | |
| // The basic rect used is [kMin,kMin]..[kMax,kMax] | |
| static const float kMin = 100.5f; | |
| static const float kMid = 200.0f; | |
| static const float kMax = 299.5f; | |
| // The translation applied to the base AA rect in the combination cases | |
| // (i.e., kRectAndRect through kRectAndConcave) | |
| static const float kXlate = 100.0f; | |
| SkRect create_rect(const SkPoint& offset) { | |
| SkRect r = SkRect::MakeLTRB(kMin, kMin, kMax, kMax); | |
| r.offset(offset); | |
| return r; | |
| } | |
| SkRRect create_rrect(const SkPoint& offset) { | |
| SkRRect rrect; | |
| rrect.setRectXY(create_rect(offset), 10, 10); | |
| return rrect; | |
| } | |
| SkRRect create_circle(const SkPoint& offset) { | |
| SkRRect circle; | |
| circle.setOval(create_rect(offset)); | |
| return circle; | |
| } | |
| SkPath create_convex_path(const SkPoint& offset) { | |
| SkPath convexPath; | |
| convexPath.moveTo(kMin, kMin); | |
| convexPath.lineTo(kMax, kMax); | |
| convexPath.lineTo(kMin, kMax); | |
| convexPath.close(); | |
| convexPath.offset(offset.fX, offset.fY); | |
| return convexPath; | |
| } | |
| SkPath create_concave_path(const SkPoint& offset) { | |
| SkPath concavePath; | |
| concavePath.moveTo(kMin, kMin); | |
| concavePath.lineTo(kMid, 105.0f); | |
| concavePath.lineTo(kMax, kMin); | |
| concavePath.lineTo(295.0f, kMid); | |
| concavePath.lineTo(kMax, kMax); | |
| concavePath.lineTo(kMid, 295.0f); | |
| concavePath.lineTo(kMin, kMax); | |
| concavePath.lineTo(105.0f, kMid); | |
| concavePath.close(); | |
| concavePath.offset(offset.fX, offset.fY); | |
| return concavePath; | |
| } | |
| static void draw_normal_geom(SkCanvas* canvas, const SkPoint& offset, int geom, bool useAA) { | |
| SkPaint p; | |
| p.setAntiAlias(useAA); | |
| p.setColor(SK_ColorBLACK); | |
| switch (geom) { | |
| case kRect_Geometry: // fall thru | |
| case kRectAndRect_Geometry: | |
| canvas->drawRect(create_rect(offset), p); | |
| break; | |
| case kRRect_Geometry: // fall thru | |
| case kRectAndRRect_Geometry: | |
| canvas->drawRRect(create_rrect(offset), p); | |
| break; | |
| case kCircle_Geometry: | |
| canvas->drawRRect(create_circle(offset), p); | |
| break; | |
| case kConvexPath_Geometry: // fall thru | |
| case kRectAndConvex_Geometry: | |
| canvas->drawPath(create_convex_path(offset), p); | |
| break; | |
| case kConcavePath_Geometry: // fall thru | |
| case kRectAndConcave_Geometry: | |
| canvas->drawPath(create_concave_path(offset), p); | |
| break; | |
| } | |
| } | |
| class ClipDrawMatchView : public SampleView { | |
| public: | |
| ClipDrawMatchView() : fTrans(2, 5), fGeom(kRect_Geometry), fClipFirst(true), fSign(1) { | |
| SkScalar values[2]; | |
| fTrans.setRepeatCount(999); | |
| values[0] = values[1] = 0; | |
| fTrans.setKeyFrame(0, GetMSecs() + 1000, values); | |
| values[1] = 1; | |
| fTrans.setKeyFrame(1, GetMSecs() + 2000, values); | |
| values[0] = values[1] = 1; | |
| fTrans.setKeyFrame(2, GetMSecs() + 3000, values); | |
| values[1] = 0; | |
| fTrans.setKeyFrame(3, GetMSecs() + 4000, values); | |
| values[0] = 0; | |
| fTrans.setKeyFrame(4, GetMSecs() + 5000, values); | |
| } | |
| protected: | |
| bool onQuery(SkEvent* evt) override { | |
| if (SampleCode::TitleQ(*evt)) { | |
| SampleCode::TitleR(evt, "ClipDrawMatch"); | |
| return true; | |
| } | |
| SkUnichar uni; | |
| if (SampleCode::CharQ(*evt, &uni)) { | |
| switch (uni) { | |
| case '1': fGeom = kRect_Geometry; this->inval(nullptr); return true; | |
| case '2': fGeom = kRRect_Geometry; this->inval(nullptr); return true; | |
| case '3': fGeom = kCircle_Geometry; this->inval(nullptr); return true; | |
| case '4': fGeom = kConvexPath_Geometry; this->inval(nullptr); return true; | |
| case '5': fGeom = kConcavePath_Geometry; this->inval(nullptr); return true; | |
| case '6': fGeom = kRectAndRect_Geometry; this->inval(nullptr); return true; | |
| case '7': fGeom = kRectAndRRect_Geometry; this->inval(nullptr); return true; | |
| case '8': fGeom = kRectAndConvex_Geometry; this->inval(nullptr); return true; | |
| case '9': fGeom = kRectAndConcave_Geometry; this->inval(nullptr); return true; | |
| case 'f': fSign = -fSign; this->inval(nullptr); return true; | |
| case 't': fClipFirst = !fClipFirst; this->inval(nullptr); return true; | |
| default: break; | |
| } | |
| } | |
| return this->INHERITED::onQuery(evt); | |
| } | |
| void drawClippedGeom(SkCanvas* canvas, const SkPoint& offset, bool useAA) { | |
| int count = canvas->save(); | |
| switch (fGeom) { | |
| case kRect_Geometry: | |
| canvas->clipRect(create_rect(offset), SkRegion::kReplace_Op, useAA); | |
| break; | |
| case kRRect_Geometry: | |
| canvas->clipRRect(create_rrect(offset), SkRegion::kReplace_Op, useAA); | |
| break; | |
| case kCircle_Geometry: | |
| canvas->clipRRect(create_circle(offset), SkRegion::kReplace_Op, useAA); | |
| break; | |
| case kConvexPath_Geometry: | |
| canvas->clipPath(create_convex_path(offset), SkRegion::kReplace_Op, useAA); | |
| break; | |
| case kConcavePath_Geometry: | |
| canvas->clipPath(create_concave_path(offset), SkRegion::kReplace_Op, useAA); | |
| break; | |
| case kRectAndRect_Geometry: { | |
| SkRect r = create_rect(offset); | |
| r.offset(fSign * kXlate, fSign * kXlate); | |
| canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips | |
| canvas->clipRect(create_rect(offset), SkRegion::kIntersect_Op, useAA); | |
| } break; | |
| case kRectAndRRect_Geometry: { | |
| SkRect r = create_rect(offset); | |
| r.offset(fSign * kXlate, fSign * kXlate); | |
| canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips | |
| canvas->clipRRect(create_rrect(offset), SkRegion::kIntersect_Op, useAA); | |
| } break; | |
| case kRectAndConvex_Geometry: { | |
| SkRect r = create_rect(offset); | |
| r.offset(fSign * kXlate, fSign * kXlate); | |
| canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips | |
| canvas->clipPath(create_convex_path(offset), SkRegion::kIntersect_Op, useAA); | |
| } break; | |
| case kRectAndConcave_Geometry: { | |
| SkRect r = create_rect(offset); | |
| r.offset(fSign * kXlate, fSign * kXlate); | |
| canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips | |
| canvas->clipPath(create_concave_path(offset), SkRegion::kIntersect_Op, useAA); | |
| } break; | |
| } | |
| SkISize size = canvas->getDeviceSize(); | |
| SkRect bigR = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height())); | |
| SkPaint p; | |
| p.setColor(SK_ColorRED); | |
| canvas->drawRect(bigR, p); | |
| canvas->restoreToCount(count); | |
| } | |
| // Draw a big red rect through some clip geometry and also draw that same | |
| // geometry in black. The order in which they are drawn can be swapped. | |
| // This tests whether the clip and normally drawn geometry match up. | |
| void drawGeometry(SkCanvas* canvas, const SkPoint& offset, bool useAA) { | |
| if (fClipFirst) { | |
| this->drawClippedGeom(canvas, offset, useAA); | |
| } | |
| draw_normal_geom(canvas, offset, fGeom, useAA); | |
| if (!fClipFirst) { | |
| this->drawClippedGeom(canvas, offset, useAA); | |
| } | |
| } | |
| void onDrawContent(SkCanvas* canvas) override { | |
| SkScalar trans[2]; | |
| fTrans.timeToValues(GetMSecs(), trans); | |
| SkPoint offset; | |
| offset.set(trans[0], trans[1]); | |
| int saveCount = canvas->save(); | |
| this->drawGeometry(canvas, offset, false); | |
| canvas->restoreToCount(saveCount); | |
| this->inval(nullptr); | |
| } | |
| SkMSec GetMSecs() const { | |
| return static_cast<SkMSec>(SkTime::GetMSecs() - fStart); | |
| } | |
| private: | |
| SkInterpolator fTrans; | |
| Geometry fGeom; | |
| bool fClipFirst; | |
| int fSign; | |
| const double fStart = SkTime::GetMSecs(); | |
| typedef SampleView INHERITED; | |
| }; | |
| ////////////////////////////////////////////////////////////////////////////// | |
| static SkView* MyFactory() { return new ClipDrawMatchView; } | |
| static SkViewRegister reg(MyFactory); |