1 2 /* 3 * Copyright 2017 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 #include "SampleCode.h" 9 #include "SkAnimTimer.h" 10 #include "SkBlurMask.h" 11 #include "SkBlurMaskFilter.h" 12 #include "SkColorFilter.h" 13 #include "SkCamera.h" 14 #include "SkCanvas.h" 15 #include "SkPath.h" 16 #include "SkPathOps.h" 17 #include "SkPoint3.h" 18 #include "SkShadowUtils.h" 19 #include "SkUtils.h" 20 #include "SkView.h" 21 #include "sk_tool_utils.h" 22 23 //////////////////////////////////////////////////////////////////////////// 24 25 class ShadowUtilsView : public SampleView { 26 SkTArray<SkPath> fConvexPaths; 27 SkTArray<SkPath> fConcavePaths; 28 SkScalar fZDelta; 29 30 bool fShowAmbient; 31 bool fShowSpot; 32 bool fUseAlt; 33 bool fShowObject; 34 bool fIgnoreShadowAlpha; 35 36 public: 37 ShadowUtilsView() 38 : fZDelta(0) 39 , fShowAmbient(true) 40 , fShowSpot(true) 41 , fUseAlt(false) 42 , fShowObject(false) 43 , fIgnoreShadowAlpha(false) {} 44 45 protected: 46 void onOnceBeforeDraw() override { 47 fConvexPaths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10); 48 SkRRect oddRRect; 49 oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16); 50 fConvexPaths.push_back().addRRect(oddRRect); 51 fConvexPaths.push_back().addRect(SkRect::MakeWH(50, 50)); 52 fConvexPaths.push_back().addCircle(25, 25, 25); 53 fConvexPaths.push_back().cubicTo(100, 50, 20, 100, 0, 0); 54 fConvexPaths.push_back().addOval(SkRect::MakeWH(20, 60)); 55 56 // star 57 fConcavePaths.push_back().moveTo(0.0f, -33.3333f); 58 fConcavePaths.back().lineTo(9.62f, -16.6667f); 59 fConcavePaths.back().lineTo(28.867f, -16.6667f); 60 fConcavePaths.back().lineTo(19.24f, 0.0f); 61 fConcavePaths.back().lineTo(28.867f, 16.6667f); 62 fConcavePaths.back().lineTo(9.62f, 16.6667f); 63 fConcavePaths.back().lineTo(0.0f, 33.3333f); 64 fConcavePaths.back().lineTo(-9.62f, 16.6667f); 65 fConcavePaths.back().lineTo(-28.867f, 16.6667f); 66 fConcavePaths.back().lineTo(-19.24f, 0.0f); 67 fConcavePaths.back().lineTo(-28.867f, -16.6667f); 68 fConcavePaths.back().lineTo(-9.62f, -16.6667f); 69 fConcavePaths.back().close(); 70 71 // dumbbell 72 fConcavePaths.push_back().moveTo(50, 0); 73 fConcavePaths.back().cubicTo(100, 25, 60, 50, 50, 0); 74 fConcavePaths.back().cubicTo(0, -25, 40, -50, 50, 0); 75 } 76 77 // overrides from SkEventSink 78 bool onQuery(SkEvent* evt) override { 79 if (SampleCode::TitleQ(*evt)) { 80 SampleCode::TitleR(evt, "ShadowUtils"); 81 return true; 82 } 83 84 SkUnichar uni; 85 if (SampleCode::CharQ(*evt, &uni)) { 86 bool handled = false; 87 switch (uni) { 88 case 'W': 89 fShowAmbient = !fShowAmbient; 90 handled = true; 91 break; 92 case 'S': 93 fShowSpot = !fShowSpot; 94 handled = true; 95 break; 96 case 'T': 97 fUseAlt = !fUseAlt; 98 handled = true; 99 break; 100 case 'O': 101 fShowObject = !fShowObject; 102 handled = true; 103 break; 104 case '>': 105 fZDelta += 0.5f; 106 handled = true; 107 break; 108 case '<': 109 fZDelta -= 0.5f; 110 handled = true; 111 break; 112 case '?': 113 fIgnoreShadowAlpha = !fIgnoreShadowAlpha; 114 handled = true; 115 break; 116 default: 117 break; 118 } 119 if (handled) { 120 return true; 121 } 122 } 123 return this->INHERITED::onQuery(evt); 124 } 125 126 void drawBG(SkCanvas* canvas) { 127 canvas->drawColor(0xFFFFFFFF); 128 } 129 130 void drawShadowedPath(SkCanvas* canvas, const SkPath& path, 131 const SkPoint3& zPlaneParams, 132 const SkPaint& paint, SkScalar ambientAlpha, 133 const SkPoint3& lightPos, SkScalar lightWidth, SkScalar spotAlpha, 134 uint32_t flags) { 135 if (fIgnoreShadowAlpha) { 136 ambientAlpha = 255; 137 spotAlpha = 255; 138 } 139 if (!fShowAmbient) { 140 ambientAlpha = 0; 141 } 142 if (!fShowSpot) { 143 spotAlpha = 0; 144 } 145 if (fUseAlt) { 146 flags |= SkShadowFlags::kGeometricOnly_ShadowFlag; 147 } 148 149 SkColor ambientColor = SkColorSetARGB(ambientAlpha * 255, 255, 0, 0); 150 SkColor spotColor = SkColorSetARGB(spotAlpha * 255, 0, 0, 255); 151 SkShadowUtils::DrawShadow(canvas, path, zPlaneParams, 152 lightPos, lightWidth, 153 ambientColor, spotColor, flags); 154 155 if (fShowObject) { 156 canvas->drawPath(path, paint); 157 } else { 158 SkPaint strokePaint; 159 160 strokePaint.setColor(paint.getColor()); 161 strokePaint.setStyle(SkPaint::kStroke_Style); 162 163 canvas->drawPath(path, strokePaint); 164 } 165 } 166 167 void onDrawContent(SkCanvas* canvas) override { 168 this->drawBG(canvas); 169 170 static constexpr int kW = 800; 171 static constexpr SkScalar kPad = 15.f; 172 static constexpr SkScalar kLightR = 100.f; 173 static constexpr SkScalar kHeight = 50.f; 174 static constexpr SkScalar kAmbientAlpha = 0.5f; 175 static constexpr SkScalar kSpotAlpha = 0.5f; 176 static constexpr SkPoint3 lightPos = { 250, 400, 500 }; 177 178 canvas->translate(3 * kPad, 3 * kPad); 179 canvas->save(); 180 SkScalar x = 0; 181 SkScalar dy = 0; 182 SkTDArray<SkMatrix> matrices; 183 matrices.push()->reset(); 184 SkMatrix* m = matrices.push(); 185 m->setRotate(33.f, 25.f, 25.f); 186 m->postScale(1.2f, 0.8f, 25.f, 25.f); 187 SkPaint paint; 188 paint.setColor(SK_ColorGREEN); 189 paint.setAntiAlias(true); 190 SkPoint3 zPlaneParams = SkPoint3::Make(0, 0, SkTMax(1.0f, kHeight + fZDelta)); 191 192 // convex paths 193 for (auto& m : matrices) { 194 for (auto flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) { 195 for (const auto& path : fConvexPaths) { 196 SkRect postMBounds = path.getBounds(); 197 m.mapRect(&postMBounds); 198 SkScalar w = postMBounds.width() + kHeight; 199 SkScalar dx = w + kPad; 200 if (x + dx > kW - 3 * kPad) { 201 canvas->restore(); 202 canvas->translate(0, dy); 203 canvas->save(); 204 x = 0; 205 dy = 0; 206 } 207 208 canvas->save(); 209 canvas->concat(m); 210 this->drawShadowedPath(canvas, path, zPlaneParams, paint, kAmbientAlpha, 211 lightPos, kLightR, kSpotAlpha, flags); 212 canvas->restore(); 213 214 canvas->translate(dx, 0); 215 x += dx; 216 dy = SkTMax(dy, postMBounds.height() + kPad + kHeight); 217 } 218 } 219 } 220 221 // concave paths 222 canvas->restore(); 223 canvas->translate(kPad, dy); 224 canvas->save(); 225 x = kPad; 226 dy = 0; 227 for (auto& m : matrices) { 228 for (const auto& path : fConcavePaths) { 229 SkRect postMBounds = path.getBounds(); 230 m.mapRect(&postMBounds); 231 SkScalar w = postMBounds.width(); 232 SkScalar dx = w + kPad; 233 234 canvas->save(); 235 canvas->concat(m); 236 this->drawShadowedPath(canvas, path, zPlaneParams, paint, kAmbientAlpha, lightPos, 237 kLightR, kSpotAlpha, kNone_ShadowFlag); 238 canvas->restore(); 239 240 canvas->translate(dx, 0); 241 x += dx; 242 dy = SkTMax(dy, postMBounds.height() + kPad + kHeight); 243 } 244 } 245 246 // Show where the light is in x,y as a circle (specified in device space). 247 SkMatrix invCanvasM = canvas->getTotalMatrix(); 248 if (invCanvasM.invert(&invCanvasM)) { 249 canvas->save(); 250 canvas->concat(invCanvasM); 251 SkPaint paint; 252 paint.setColor(SK_ColorBLACK); 253 paint.setAntiAlias(true); 254 canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint); 255 canvas->restore(); 256 } 257 } 258 259 private: 260 typedef SampleView INHERITED; 261 }; 262 263 ////////////////////////////////////////////////////////////////////////////// 264 265 static SkView* MyFactory() { return new ShadowUtilsView; } 266 static SkViewRegister reg(MyFactory); 267