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 14void draw_shadow(SkCanvas* canvas, const SkPath& path, int height, SkColor color, SkPoint3 lightPos, 15 SkScalar lightR, bool isAmbient, uint32_t flags, SkResourceCache* cache) { 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, cache); 20} 21 22static constexpr int kW = 800; 23static constexpr int kH = 800; 24 25DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) { 26 // SkShadowUtils uses a cache of SkVertices meshes. The vertices are created in a local 27 // coordinate system and then translated when reused. The coordinate system depends on 28 // parameters to the generating draw. If other threads are hitting the cache while this GM is 29 // running then we may have different cache behavior leading to slight rendering differences. 30 // To avoid that we use our own isolated cache rather than the global cache. 31 SkResourceCache cache(1 << 20); 32 33 SkTArray<SkPath> paths; 34 paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10); 35 SkRRect oddRRect; 36 oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16); 37 paths.push_back().addRRect(oddRRect); 38 paths.push_back().addRect(SkRect::MakeWH(50, 50)); 39 paths.push_back().addCircle(25, 25, 25); 40 paths.push_back().cubicTo(100, 50, 20, 100, 0, 0); 41 paths.push_back().addOval(SkRect::MakeWH(20, 60)); 42 43 static constexpr SkScalar kPad = 15.f; 44 static constexpr SkPoint3 kLightPos = {250, 400, 500}; 45 static constexpr SkScalar kLightR = 100.f; 46 static constexpr SkScalar kHeight = 50.f; 47 canvas->translate(3 * kPad, 3 * kPad); 48 canvas->save(); 49 SkScalar x = 0; 50 SkScalar dy = 0; 51 SkTDArray<SkMatrix> matrices; 52 matrices.push()->reset(); 53 SkMatrix* m = matrices.push(); 54 m->setRotate(33.f, 25.f, 25.f); 55 m->postScale(1.2f, 0.8f, 25.f, 25.f); 56 for (auto& m : matrices) { 57 for (auto flags : {kNone_ShadowFlag, kTransparentOccluder_ShadowFlag}) { 58 for (const auto& path : paths) { 59 SkRect postMBounds = path.getBounds(); 60 m.mapRect(&postMBounds); 61 SkScalar w = postMBounds.width() + kHeight; 62 SkScalar dx = w + kPad; 63 if (x + dx > kW - 3 * kPad) { 64 canvas->restore(); 65 canvas->translate(0, dy); 66 canvas->save(); 67 x = 0; 68 dy = 0; 69 } 70 71 canvas->save(); 72 canvas->concat(m); 73 draw_shadow(canvas, path, kHeight, SK_ColorRED, kLightPos, kLightR, true, flags, 74 &cache); 75 draw_shadow(canvas, path, kHeight, SK_ColorBLUE, kLightPos, kLightR, false, flags, 76 &cache); 77 78 // Draw the path outline in green on top of the ambient and spot shadows. 79 SkPaint paint; 80 paint.setColor(SK_ColorGREEN); 81 paint.setAntiAlias(true); 82 paint.setStyle(SkPaint::kStroke_Style); 83 paint.setStrokeWidth(0); 84 canvas->drawPath(path, paint); 85 canvas->restore(); 86 87 canvas->translate(dx, 0); 88 x += dx; 89 dy = SkTMax(dy, postMBounds.height() + kPad + kHeight); 90 } 91 } 92 } 93 // Show where the light is in x,y as a circle (specified in device space). 94 SkMatrix invCanvasM = canvas->getTotalMatrix(); 95 if (invCanvasM.invert(&invCanvasM)) { 96 canvas->save(); 97 canvas->concat(invCanvasM); 98 SkPaint paint; 99 paint.setColor(SK_ColorBLACK); 100 paint.setAntiAlias(true); 101 canvas->drawCircle(kLightPos.fX, kLightPos.fY, kLightR / 10.f, paint); 102 canvas->restore(); 103 } 104} 105