• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
draw_shadow(SkCanvas * canvas,const SkPath & path,SkScalar height,SkColor color,SkPoint3 lightPos,SkScalar lightR,bool isAmbient,uint32_t flags)14 void draw_shadow(SkCanvas* canvas, const SkPath& path, SkScalar height, SkColor color,
15                  SkPoint3 lightPos, SkScalar lightR, bool isAmbient, uint32_t flags) {
16     SkScalar ambientAlpha = isAmbient ? .5f : 0.f;
17     SkScalar spotAlpha = isAmbient ? 0.f : .5f;
18     SkColor ambientColor = SkColorSetARGB(ambientAlpha*SkColorGetA(color), SkColorGetR(color),
19                                           SkColorGetG(color), SkColorGetB(color));
20     SkColor spotColor = SkColorSetARGB(spotAlpha*SkColorGetA(color), SkColorGetR(color),
21                                        SkColorGetG(color), SkColorGetB(color));
22     SkShadowUtils::DrawShadow(canvas, path, SkPoint3{ 0, 0, height}, lightPos, lightR,
23                               ambientColor, spotColor, flags);
24 }
25 
26 static constexpr int kW = 800;
27 static constexpr int kH = 960;
28 
29 enum ShadowMode {
30     kDebugColorNoOccluders,
31     kDebugColorOccluders,
32     kGrayscale
33 };
34 
draw_paths(SkCanvas * canvas,ShadowMode mode)35 void draw_paths(SkCanvas* canvas, ShadowMode mode) {
36     SkTArray<SkPath> paths;
37     paths.push_back().addRoundRect(SkRect::MakeWH(50, 50), 10, 10);
38     SkRRect oddRRect;
39     oddRRect.setNinePatch(SkRect::MakeWH(50, 50), 9, 13, 6, 16);
40     paths.push_back().addRRect(oddRRect);
41     paths.push_back().addRect(SkRect::MakeWH(50, 50));
42     paths.push_back().addCircle(25, 25, 25);
43     paths.push_back().cubicTo(100, 50, 20, 100, 0, 0);
44     paths.push_back().addOval(SkRect::MakeWH(20, 60));
45 
46     // star
47     SkTArray<SkPath> concavePaths;
48     concavePaths.push_back().moveTo(0.0f, -33.3333f);
49     concavePaths.back().lineTo(9.62f, -16.6667f);
50     concavePaths.back().lineTo(28.867f, -16.6667f);
51     concavePaths.back().lineTo(19.24f, 0.0f);
52     concavePaths.back().lineTo(28.867f, 16.6667f);
53     concavePaths.back().lineTo(9.62f, 16.6667f);
54     concavePaths.back().lineTo(0.0f, 33.3333f);
55     concavePaths.back().lineTo(-9.62f, 16.6667f);
56     concavePaths.back().lineTo(-28.867f, 16.6667f);
57     concavePaths.back().lineTo(-19.24f, 0.0f);
58     concavePaths.back().lineTo(-28.867f, -16.6667f);
59     concavePaths.back().lineTo(-9.62f, -16.6667f);
60     concavePaths.back().close();
61 
62     // dumbbell
63     concavePaths.push_back().moveTo(50, 0);
64     concavePaths.back().cubicTo(100, 25, 60, 50, 50, 0);
65     concavePaths.back().cubicTo(0, -25, 40, -50, 50, 0);
66 
67     static constexpr SkScalar kPad = 15.f;
68     static constexpr SkScalar kLightR = 100.f;
69     static constexpr SkScalar kHeight = 50.f;
70 
71     // transform light position relative to canvas to handle tiling
72     SkPoint lightXY = canvas->getTotalMatrix().mapXY(250, 400);
73     SkPoint3 lightPos = { lightXY.fX, lightXY.fY, 500 };
74 
75     canvas->translate(3 * kPad, 3 * kPad);
76     canvas->save();
77     SkScalar x = 0;
78     SkScalar dy = 0;
79     SkTDArray<SkMatrix> matrices;
80     matrices.push()->reset();
81     SkMatrix* m = matrices.push();
82     m->setRotate(33.f, 25.f, 25.f);
83     m->postScale(1.2f, 0.8f, 25.f, 25.f);
84     for (auto& m : matrices) {
85         for (int flags : { kNone_ShadowFlag, kTransparentOccluder_ShadowFlag }) {
86             for (const auto& path : paths) {
87                 SkRect postMBounds = path.getBounds();
88                 m.mapRect(&postMBounds);
89                 SkScalar w = postMBounds.width() + kHeight;
90                 SkScalar dx = w + kPad;
91                 if (x + dx > kW - 3 * kPad) {
92                     canvas->restore();
93                     canvas->translate(0, dy);
94                     canvas->save();
95                     x = 0;
96                     dy = 0;
97                 }
98 
99                 canvas->save();
100                 canvas->concat(m);
101 
102                 if (kDebugColorNoOccluders == mode || kDebugColorOccluders == mode) {
103                     draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR,
104                                 true, flags);
105                     draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR,
106                                 false, flags);
107                 } else if (kGrayscale == mode) {
108                     SkColor ambientColor = SkColorSetARGB(0.1f * 255, 0, 0, 0);
109                     SkColor spotColor = SkColorSetARGB(0.25f * 255, 0, 0, 0);
110                     SkShadowUtils::DrawShadow(canvas, path, SkPoint3{0, 0, kHeight}, lightPos,
111                                               kLightR, ambientColor, spotColor, flags);
112                 }
113 
114                 SkPaint paint;
115                 paint.setAntiAlias(true);
116                 if (kDebugColorNoOccluders == mode) {
117                     // Draw the path outline in green on top of the ambient and spot shadows.
118                     if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) {
119                         paint.setColor(SK_ColorCYAN);
120                     } else {
121                         paint.setColor(SK_ColorGREEN);
122                     }
123                     paint.setStyle(SkPaint::kStroke_Style);
124                     paint.setStrokeWidth(0);
125                 } else {
126                     paint.setColor(kDebugColorOccluders == mode ? SK_ColorLTGRAY : SK_ColorWHITE);
127                     if (SkToBool(flags & kTransparentOccluder_ShadowFlag)) {
128                         paint.setAlpha(128);
129                     }
130                     paint.setStyle(SkPaint::kFill_Style);
131                 }
132                 canvas->drawPath(path, paint);
133                 canvas->restore();
134 
135                 canvas->translate(dx, 0);
136                 x += dx;
137                 dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
138             }
139         }
140     }
141 
142     // concave paths
143     canvas->restore();
144     canvas->translate(kPad, dy);
145     canvas->save();
146     x = kPad;
147     dy = 0;
148     for (auto& m : matrices) {
149         // for the concave paths we are not clipping, so transparent and opaque are the same
150         for (const auto& path : concavePaths) {
151             SkRect postMBounds = path.getBounds();
152             m.mapRect(&postMBounds);
153             SkScalar w = postMBounds.width() + kHeight;
154             SkScalar dx = w + kPad;
155 
156             canvas->save();
157             canvas->concat(m);
158 
159             if (kDebugColorNoOccluders == mode || kDebugColorOccluders == mode) {
160                 draw_shadow(canvas, path, kHeight, SK_ColorRED, lightPos, kLightR,
161                             true, kNone_ShadowFlag);
162                 draw_shadow(canvas, path, kHeight, SK_ColorBLUE, lightPos, kLightR,
163                             false, kNone_ShadowFlag);
164             } else if (kGrayscale == mode) {
165                 SkColor ambientColor = SkColorSetARGB(0.1f * 255, 0, 0, 0);
166                 SkColor spotColor = SkColorSetARGB(0.25f * 255, 0, 0, 0);
167                 SkShadowUtils::DrawShadow(canvas, path, SkPoint3{ 0, 0, kHeight }, lightPos,
168                                           kLightR, ambientColor, spotColor, kNone_ShadowFlag);
169             }
170 
171             SkPaint paint;
172             paint.setAntiAlias(true);
173             if (kDebugColorNoOccluders == mode) {
174                 // Draw the path outline in green on top of the ambient and spot shadows.
175                 paint.setColor(SK_ColorGREEN);
176                 paint.setStyle(SkPaint::kStroke_Style);
177                 paint.setStrokeWidth(0);
178             } else {
179                 paint.setColor(kDebugColorOccluders == mode ? SK_ColorLTGRAY : SK_ColorWHITE);
180                 paint.setStyle(SkPaint::kFill_Style);
181             }
182             canvas->drawPath(path, paint);
183             canvas->restore();
184 
185             canvas->translate(dx, 0);
186             x += dx;
187             dy = SkTMax(dy, postMBounds.height() + kPad + kHeight);
188         }
189     }
190 
191     // Show where the light is in x,y as a circle (specified in device space).
192     SkMatrix invCanvasM = canvas->getTotalMatrix();
193     if (invCanvasM.invert(&invCanvasM)) {
194         canvas->save();
195         canvas->concat(invCanvasM);
196         SkPaint paint;
197         paint.setColor(SK_ColorBLACK);
198         paint.setAntiAlias(true);
199         canvas->drawCircle(lightPos.fX, lightPos.fY, kLightR / 10.f, paint);
200         canvas->restore();
201     }
202 }
203 
DEF_SIMPLE_GM(shadow_utils,canvas,kW,kH)204 DEF_SIMPLE_GM(shadow_utils, canvas, kW, kH) {
205     draw_paths(canvas, kDebugColorNoOccluders);
206 }
207 
DEF_SIMPLE_GM(shadow_utils_occl,canvas,kW,kH)208 DEF_SIMPLE_GM(shadow_utils_occl, canvas, kW, kH) {
209     draw_paths(canvas, kDebugColorOccluders);
210 }
211 
DEF_SIMPLE_GM(shadow_utils_gray,canvas,kW,kH)212 DEF_SIMPLE_GM(shadow_utils_gray, canvas, kW, kH) {
213     draw_paths(canvas, kGrayscale);
214 }
215