1 /* 2 * Copyright 2017 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 "gm.h" 9 #include "SkCanvas.h" 10 #include "SkPath.h" 11 #include "SkResourceCache.h" 12 #include "SkShadowUtils.h" 13 14 void draw_shadow(SkCanvas* canvas, const SkPath& path, int height, SkColor color, SkPoint3 lightPos, 15 SkScalar lightR, bool isAmbient, uint32_t flags) { 16 SkScalar ambientAlpha = isAmbient ? .5f : 0.f; 17 SkScalar spotAlpha = isAmbient ? 0.f : .5f; 18 SkShadowUtils::DrawShadow(canvas, path, height, lightPos, lightR, ambientAlpha, spotAlpha, 19 color, flags); 20 } 21 22 static constexpr int kW = 800; 23 static constexpr int kH = 800; 24 25 void draw_paths(SkCanvas* canvas, bool hideOccluders) { 26 SkTArray<SkPath> paths; 27 paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10); 28 SkRRect oddRRect; 29 oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16); 30 paths.push_back().addRRect(oddRRect); 31 paths.push_back().addRect(SkRect::MakeWH(50, 50)); 32 paths.push_back().addCircle(25, 25, 25); 33 paths.push_back().cubicTo(100, 50, 20, 100, 0, 0); 34 paths.push_back().addOval(SkRect::MakeWH(20, 60)); 35 36 static constexpr SkScalar kPad = 15.f; 37 static constexpr SkScalar kLightR = 100.f; 38 static constexpr SkScalar kHeight = 50.f; 39 40 // transform light position relative to canvas to handle tiling 41 SkPoint lightXY = canvas->getTotalMatrix().mapXY(250, 400); 42 SkPoint3 lightPos = { lightXY.fX, lightXY.fY, 500 }; 43 44 canvas->translate(3 * kPad, 3 * kPad); 45 canvas->save(); 46 SkScalar x = 0; 47 SkScalar dy = 0; 48 SkTDArray<SkMatrix> matrices; 49 matrices.push()->reset(); 50 SkMatrix* m = matrices.push(); 51 m->setRotate(33.f, 25.f, 25.f); 52 m->postScale(1.2f, 0.8f, 25.f, 25.f); 53 for (auto& m : matrices) { 54 for (auto flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) { 55 for (const auto& path : paths) { 56 SkRect postMBounds = path.getBounds(); 57 m.mapRect(&postMBounds); 58 SkScalar w = postMBounds.width() + kHeight; 59 SkScalar dx = w + kPad; 60 if (x + dx > kW - 3 * kPad) { 61 canvas->restore(); 62 canvas->translate(0, dy); 63 canvas->save(); 64 x = 0; 65 dy = 0; 66 } 67 68 canvas->save(); 69 canvas->concat(m); 70 draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR, true, flags); 71 draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR, false, flags); 72 73 // Draw the path outline in green on top of the ambient and spot shadows. 74 SkPaint paint; 75 paint.setAntiAlias(true); 76 if (hideOccluders) { 77 if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) { 78 paint.setColor(SK_ColorCYAN); 79 } else { 80 paint.setColor(SK_ColorGREEN); 81 } 82 paint.setStyle(SkPaint::kStroke_Style); 83 paint.setStrokeWidth(0); 84 } else { 85 paint.setColor(SK_ColorLTGRAY); 86 if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) { 87 paint.setAlpha(128); 88 } 89 paint.setStyle(SkPaint::kFill_Style); 90 } 91 canvas->drawPath(path, paint); 92 canvas->restore(); 93 94 canvas->translate(dx, 0); 95 x += dx; 96 dy = SkTMax(dy, postMBounds.height() + kPad + kHeight); 97 } 98 } 99 } 100 // Show where the light is in x,y as a circle (specified in device space). 101 SkMatrix invCanvasM = canvas->getTotalMatrix(); 102 if (invCanvasM.invert(&invCanvasM)) { 103 canvas->save(); 104 canvas->concat(invCanvasM); 105 SkPaint paint; 106 paint.setColor(SK_ColorBLACK); 107 paint.setAntiAlias(true); 108 canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint); 109 canvas->restore(); 110 } 111 } 112 113 DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) { 114 draw_paths(canvas, true); 115 } 116 117 DEF_SIMPLE_GM(shadow_utils_occl, canvas, kW, kH) { 118 draw_paths(canvas, false); 119 } 120