robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2015 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "SampleCode.h" |
| 9 | #include "SkCanvas.h" |
| 10 | #include "SkInterpolator.h" |
| 11 | #include "SkTime.h" |
| 12 | |
| 13 | // This slide tests out the match up between BW clipping and rendering. It can |
| 14 | // draw a large rect through some clip geometry and draw the same geometry |
| 15 | // normally. Which one is drawn first can be toggled. The pair of objects is translated |
| 16 | // fractionally (via an animator) to expose snapping bugs. The key bindings are: |
| 17 | // 1-9: the different geometries |
| 18 | // t: toggle which is drawn first the clip or the normal geometry |
robertphillips | c89f6fb | 2015-02-09 07:47:17 -0800 | [diff] [blame] | 19 | // f: flip-flops which corner the bottom AA clip rect occupies in the complex clip cases |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 20 | |
| 21 | // The possible geometric combinations to test |
| 22 | enum Geometry { |
| 23 | kRect_Geometry, |
| 24 | kRRect_Geometry, |
| 25 | kCircle_Geometry, |
| 26 | kConvexPath_Geometry, |
| 27 | kConcavePath_Geometry, |
| 28 | kRectAndRect_Geometry, |
| 29 | kRectAndRRect_Geometry, |
| 30 | kRectAndConvex_Geometry, |
| 31 | kRectAndConcave_Geometry |
| 32 | }; |
| 33 | |
| 34 | // The basic rect used is [kMin,kMin]..[kMax,kMax] |
| 35 | static const float kMin = 100.5f; |
| 36 | static const float kMid = 200.0f; |
| 37 | static const float kMax = 299.5f; |
| 38 | |
robertphillips | c89f6fb | 2015-02-09 07:47:17 -0800 | [diff] [blame] | 39 | // The translation applied to the base AA rect in the combination cases |
| 40 | // (i.e., kRectAndRect through kRectAndConcave) |
| 41 | static const float kXlate = 100.0f; |
| 42 | |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 43 | SkRect create_rect(const SkPoint& offset) { |
| 44 | SkRect r = SkRect::MakeLTRB(kMin, kMin, kMax, kMax); |
| 45 | r.offset(offset); |
| 46 | return r; |
| 47 | } |
| 48 | |
| 49 | SkRRect create_rrect(const SkPoint& offset) { |
| 50 | SkRRect rrect; |
| 51 | rrect.setRectXY(create_rect(offset), 10, 10); |
| 52 | return rrect; |
| 53 | } |
| 54 | |
| 55 | SkRRect create_circle(const SkPoint& offset) { |
| 56 | SkRRect circle; |
| 57 | circle.setOval(create_rect(offset)); |
| 58 | return circle; |
| 59 | } |
| 60 | |
| 61 | SkPath create_convex_path(const SkPoint& offset) { |
| 62 | SkPath convexPath; |
| 63 | convexPath.moveTo(kMin, kMin); |
| 64 | convexPath.lineTo(kMax, kMax); |
| 65 | convexPath.lineTo(kMin, kMax); |
| 66 | convexPath.close(); |
| 67 | convexPath.offset(offset.fX, offset.fY); |
| 68 | return convexPath; |
| 69 | } |
| 70 | |
| 71 | SkPath create_concave_path(const SkPoint& offset) { |
| 72 | SkPath concavePath; |
| 73 | concavePath.moveTo(kMin, kMin); |
| 74 | concavePath.lineTo(kMid, 105.0f); |
| 75 | concavePath.lineTo(kMax, kMin); |
| 76 | concavePath.lineTo(295.0f, kMid); |
| 77 | concavePath.lineTo(kMax, kMax); |
| 78 | concavePath.lineTo(kMid, 295.0f); |
| 79 | concavePath.lineTo(kMin, kMax); |
| 80 | concavePath.lineTo(105.0f, kMid); |
| 81 | concavePath.close(); |
| 82 | |
| 83 | concavePath.offset(offset.fX, offset.fY); |
| 84 | return concavePath; |
| 85 | } |
| 86 | |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 87 | static void draw_normal_geom(SkCanvas* canvas, const SkPoint& offset, int geom, bool useAA) { |
| 88 | SkPaint p; |
| 89 | p.setAntiAlias(useAA); |
| 90 | p.setColor(SK_ColorBLACK); |
| 91 | |
| 92 | switch (geom) { |
| 93 | case kRect_Geometry: // fall thru |
| 94 | case kRectAndRect_Geometry: |
| 95 | canvas->drawRect(create_rect(offset), p); |
| 96 | break; |
| 97 | case kRRect_Geometry: // fall thru |
| 98 | case kRectAndRRect_Geometry: |
| 99 | canvas->drawRRect(create_rrect(offset), p); |
| 100 | break; |
| 101 | case kCircle_Geometry: |
| 102 | canvas->drawRRect(create_circle(offset), p); |
| 103 | break; |
| 104 | case kConvexPath_Geometry: // fall thru |
| 105 | case kRectAndConvex_Geometry: |
| 106 | canvas->drawPath(create_convex_path(offset), p); |
| 107 | break; |
| 108 | case kConcavePath_Geometry: // fall thru |
| 109 | case kRectAndConcave_Geometry: |
| 110 | canvas->drawPath(create_concave_path(offset), p); |
| 111 | break; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | class ClipDrawMatchView : public SampleView { |
| 116 | public: |
robertphillips | c89f6fb | 2015-02-09 07:47:17 -0800 | [diff] [blame] | 117 | ClipDrawMatchView() : fTrans(2, 5), fGeom(kRect_Geometry), fClipFirst(true), fSign(1) { |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 118 | SkScalar values[2]; |
| 119 | |
| 120 | fTrans.setRepeatCount(999); |
| 121 | values[0] = values[1] = 0; |
| 122 | fTrans.setKeyFrame(0, SkTime::GetMSecs() + 1000, values); |
| 123 | values[1] = 1; |
| 124 | fTrans.setKeyFrame(1, SkTime::GetMSecs() + 2000, values); |
| 125 | values[0] = values[1] = 1; |
| 126 | fTrans.setKeyFrame(2, SkTime::GetMSecs() + 3000, values); |
| 127 | values[1] = 0; |
| 128 | fTrans.setKeyFrame(3, SkTime::GetMSecs() + 4000, values); |
| 129 | values[0] = 0; |
| 130 | fTrans.setKeyFrame(4, SkTime::GetMSecs() + 5000, values); |
| 131 | } |
| 132 | |
| 133 | protected: |
mtklein | 36352bf | 2015-03-25 18:17:31 -0700 | [diff] [blame^] | 134 | bool onQuery(SkEvent* evt) override { |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 135 | if (SampleCode::TitleQ(*evt)) { |
| 136 | SampleCode::TitleR(evt, "ClipDrawMatch"); |
| 137 | return true; |
| 138 | } |
| 139 | SkUnichar uni; |
| 140 | if (SampleCode::CharQ(*evt, &uni)) { |
| 141 | switch (uni) { |
| 142 | case '1': fGeom = kRect_Geometry; this->inval(NULL); return true; |
| 143 | case '2': fGeom = kRRect_Geometry; this->inval(NULL); return true; |
| 144 | case '3': fGeom = kCircle_Geometry; this->inval(NULL); return true; |
| 145 | case '4': fGeom = kConvexPath_Geometry; this->inval(NULL); return true; |
| 146 | case '5': fGeom = kConcavePath_Geometry; this->inval(NULL); return true; |
| 147 | case '6': fGeom = kRectAndRect_Geometry; this->inval(NULL); return true; |
| 148 | case '7': fGeom = kRectAndRRect_Geometry; this->inval(NULL); return true; |
| 149 | case '8': fGeom = kRectAndConvex_Geometry; this->inval(NULL); return true; |
| 150 | case '9': fGeom = kRectAndConcave_Geometry; this->inval(NULL); return true; |
robertphillips | c89f6fb | 2015-02-09 07:47:17 -0800 | [diff] [blame] | 151 | case 'f': fSign = -fSign; this->inval(NULL); return true; |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 152 | case 't': fClipFirst = !fClipFirst; this->inval(NULL); return true; |
| 153 | default: break; |
| 154 | } |
| 155 | } |
| 156 | return this->INHERITED::onQuery(evt); |
| 157 | } |
| 158 | |
robertphillips | c89f6fb | 2015-02-09 07:47:17 -0800 | [diff] [blame] | 159 | void drawClippedGeom(SkCanvas* canvas, const SkPoint& offset, bool useAA) { |
| 160 | |
| 161 | int count = canvas->save(); |
| 162 | |
| 163 | switch (fGeom) { |
| 164 | case kRect_Geometry: |
| 165 | canvas->clipRect(create_rect(offset), SkRegion::kReplace_Op, useAA); |
| 166 | break; |
| 167 | case kRRect_Geometry: |
| 168 | canvas->clipRRect(create_rrect(offset), SkRegion::kReplace_Op, useAA); |
| 169 | break; |
| 170 | case kCircle_Geometry: |
| 171 | canvas->clipRRect(create_circle(offset), SkRegion::kReplace_Op, useAA); |
| 172 | break; |
| 173 | case kConvexPath_Geometry: |
| 174 | canvas->clipPath(create_convex_path(offset), SkRegion::kReplace_Op, useAA); |
| 175 | break; |
| 176 | case kConcavePath_Geometry: |
| 177 | canvas->clipPath(create_concave_path(offset), SkRegion::kReplace_Op, useAA); |
| 178 | break; |
| 179 | case kRectAndRect_Geometry: { |
| 180 | SkRect r = create_rect(offset); |
| 181 | r.offset(fSign * kXlate, fSign * kXlate); |
| 182 | canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips |
| 183 | canvas->clipRect(create_rect(offset), SkRegion::kIntersect_Op, useAA); |
| 184 | } break; |
| 185 | case kRectAndRRect_Geometry: { |
| 186 | SkRect r = create_rect(offset); |
| 187 | r.offset(fSign * kXlate, fSign * kXlate); |
| 188 | canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips |
| 189 | canvas->clipRRect(create_rrect(offset), SkRegion::kIntersect_Op, useAA); |
| 190 | } break; |
| 191 | case kRectAndConvex_Geometry: { |
| 192 | SkRect r = create_rect(offset); |
| 193 | r.offset(fSign * kXlate, fSign * kXlate); |
| 194 | canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips |
| 195 | canvas->clipPath(create_convex_path(offset), SkRegion::kIntersect_Op, useAA); |
| 196 | } break; |
| 197 | case kRectAndConcave_Geometry: { |
| 198 | SkRect r = create_rect(offset); |
| 199 | r.offset(fSign * kXlate, fSign * kXlate); |
| 200 | canvas->clipRect(r, SkRegion::kReplace_Op, true); // AA here forces shader clips |
| 201 | canvas->clipPath(create_concave_path(offset), SkRegion::kIntersect_Op, useAA); |
| 202 | } break; |
| 203 | } |
| 204 | |
| 205 | SkISize size = canvas->getDeviceSize(); |
| 206 | SkRect bigR = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height())); |
| 207 | |
| 208 | SkPaint p; |
| 209 | p.setColor(SK_ColorRED); |
| 210 | |
| 211 | canvas->drawRect(bigR, p); |
| 212 | canvas->restoreToCount(count); |
| 213 | } |
| 214 | |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 215 | // Draw a big red rect through some clip geometry and also draw that same |
| 216 | // geometry in black. The order in which they are drawn can be swapped. |
| 217 | // This tests whether the clip and normally drawn geometry match up. |
| 218 | void drawGeometry(SkCanvas* canvas, const SkPoint& offset, bool useAA) { |
| 219 | if (fClipFirst) { |
robertphillips | c89f6fb | 2015-02-09 07:47:17 -0800 | [diff] [blame] | 220 | this->drawClippedGeom(canvas, offset, useAA); |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 221 | } |
| 222 | |
| 223 | draw_normal_geom(canvas, offset, fGeom, useAA); |
| 224 | |
| 225 | if (!fClipFirst) { |
robertphillips | c89f6fb | 2015-02-09 07:47:17 -0800 | [diff] [blame] | 226 | this->drawClippedGeom(canvas, offset, useAA); |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 227 | } |
| 228 | } |
| 229 | |
mtklein | 36352bf | 2015-03-25 18:17:31 -0700 | [diff] [blame^] | 230 | void onDrawContent(SkCanvas* canvas) override { |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 231 | SkScalar trans[2]; |
| 232 | fTrans.timeToValues(SkTime::GetMSecs(), trans); |
| 233 | |
| 234 | SkPoint offset; |
| 235 | offset.set(trans[0], trans[1]); |
| 236 | |
| 237 | int saveCount = canvas->save(); |
| 238 | this->drawGeometry(canvas, offset, false); |
| 239 | canvas->restoreToCount(saveCount); |
| 240 | |
| 241 | this->inval(NULL); |
| 242 | } |
| 243 | |
| 244 | private: |
| 245 | SkInterpolator fTrans; |
| 246 | Geometry fGeom; |
| 247 | bool fClipFirst; |
robertphillips | c89f6fb | 2015-02-09 07:47:17 -0800 | [diff] [blame] | 248 | int fSign; |
robertphillips | 7defaa6 | 2015-01-27 06:17:22 -0800 | [diff] [blame] | 249 | |
| 250 | typedef SampleView INHERITED; |
| 251 | }; |
| 252 | |
| 253 | ////////////////////////////////////////////////////////////////////////////// |
| 254 | |
| 255 | static SkView* MyFactory() { return new ClipDrawMatchView; } |
| 256 | static SkViewRegister reg(MyFactory); |