1 /*
2 * Copyright 2016 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/gm.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkFont.h"
13 #include "include/core/SkFontStyle.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPoint3.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkScalar.h"
20 #include "include/core/SkShader.h"
21 #include "include/core/SkSize.h"
22 #include "include/core/SkString.h"
23 #include "include/core/SkTypeface.h"
24 #include "src/core/SkNormalSource.h"
25 #include "src/shaders/SkLightingShader.h"
26 #include "src/shaders/SkLights.h"
27 #include "tools/ToolUtils.h"
28
29 #include <initializer_list>
30 #include <utility>
31
32 // Create a truncated pyramid normal map
make_frustum_normalmap(int texSize)33 static SkBitmap make_frustum_normalmap(int texSize) {
34 SkBitmap frustum;
35 frustum.allocN32Pixels(texSize, texSize);
36
37 ToolUtils::create_frustum_normal_map(&frustum, SkIRect::MakeWH(texSize, texSize));
38 return frustum;
39 }
40
41 namespace skiagm {
42
43 // This GM exercises lighting shaders. Specifically, nullptr arguments, scaling when using
44 // normal maps, paint transparency, zero directional lights, multiple directional lights.
45 class LightingShader2GM : public GM {
46 public:
LightingShader2GM()47 LightingShader2GM() : fRect(SkRect::MakeIWH(kTexSize, kTexSize)) {
48 this->setBGColor(ToolUtils::color_to_565(0xFF0000CC));
49 }
50
51 protected:
onShortName()52 SkString onShortName() override {
53 return SkString("lightingshader2");
54 }
55
onISize()56 SkISize onISize() override {
57 return SkISize::Make(600, 740);
58 }
59
onOnceBeforeDraw()60 void onOnceBeforeDraw() override {
61 // The light direction is towards the light with +Z coming out of the screen
62 const SkVector3 kLightFromUpperRight = SkVector3::Make(0.788f, 0.394f, 0.473f);
63 const SkVector3 kLightFromUpperLeft = SkVector3::Make(-0.788f, 0.394f, 0.473f);
64
65 // Standard set of lights
66 {
67 SkLights::Builder builder;
68 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
69 kLightFromUpperRight));
70 builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
71 fLights = builder.finish();
72 }
73
74 // No directional lights
75 {
76 SkLights::Builder builder;
77 builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
78 fLightsNoDir = builder.finish();
79 }
80
81 // Two directional lights
82 {
83 SkLights::Builder builder;
84 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 0.0f, 0.0f),
85 kLightFromUpperRight));
86 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(0.0f, 1.0f, 0.0f),
87 kLightFromUpperLeft));
88 builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
89 fLightsTwoDir = builder.finish();
90 }
91
92 SkMatrix matrix;
93 SkRect bitmapBounds = SkRect::MakeIWH(kTexSize, kTexSize);
94 matrix.setRectToRect(bitmapBounds, fRect, SkMatrix::kFill_ScaleToFit);
95
96 SkBitmap opaqueDiffuseMap = ToolUtils::create_checkerboard_bitmap(
97 kTexSize, kTexSize, SK_ColorBLACK, 0xFF808080, 8);
98 fOpaqueDiffuse = opaqueDiffuseMap.makeShader(&matrix);
99
100 SkBitmap translucentDiffuseMap =
101 ToolUtils::create_checkerboard_bitmap(kTexSize,
102 kTexSize,
103 SkColorSetARGB(0x55, 0x00, 0x00, 0x00),
104 SkColorSetARGB(0x55, 0x80, 0x80, 0x80),
105 8);
106 fTranslucentDiffuse = translucentDiffuseMap.makeShader(&matrix);
107
108 SkBitmap normalMap = make_frustum_normalmap(kTexSize);
109 fNormalMapShader = normalMap.makeShader(&matrix);
110
111 }
112
113 // Scales shape around origin, rotates shape around origin, then translates shape to origin
positionCTM(SkCanvas * canvas,SkScalar scaleX,SkScalar scaleY,SkScalar rotate) const114 void positionCTM(SkCanvas *canvas, SkScalar scaleX, SkScalar scaleY, SkScalar rotate) const {
115 canvas->translate(kTexSize/2.0f, kTexSize/2.0f);
116 canvas->scale(scaleX, scaleY);
117 canvas->rotate(rotate);
118 canvas->translate(-kTexSize/2.0f, -kTexSize/2.0f);
119 }
120
drawRect(SkCanvas * canvas,SkScalar scaleX,SkScalar scaleY,SkScalar rotate,bool useNormalSource,bool useDiffuseShader,bool useTranslucentPaint,bool useTranslucentShader,sk_sp<SkLights> lights)121 void drawRect(SkCanvas* canvas, SkScalar scaleX, SkScalar scaleY,
122 SkScalar rotate, bool useNormalSource, bool useDiffuseShader,
123 bool useTranslucentPaint, bool useTranslucentShader, sk_sp<SkLights> lights) {
124 canvas->save();
125
126 this->positionCTM(canvas, scaleX, scaleY, rotate);
127
128 const SkMatrix& ctm = canvas->getTotalMatrix();
129
130 SkPaint paint;
131 sk_sp<SkNormalSource> normalSource = nullptr;
132 sk_sp<SkShader> diffuseShader = nullptr;
133
134 if (useNormalSource) {
135 normalSource = SkNormalSource::MakeFromNormalMap(fNormalMapShader, ctm);
136 }
137
138 if (useDiffuseShader) {
139 diffuseShader = (useTranslucentShader) ? fTranslucentDiffuse : fOpaqueDiffuse;
140 } else {
141 paint.setColor(SK_ColorGREEN);
142 }
143
144 if (useTranslucentPaint) {
145 paint.setAlpha(0x99);
146 }
147
148 paint.setShader(SkLightingShader::Make(std::move(diffuseShader), std::move(normalSource),
149 std::move(lights)));
150 canvas->drawRect(fRect, paint);
151
152 canvas->restore();
153 }
154
onDraw(SkCanvas * canvas)155 void onDraw(SkCanvas* canvas) override {
156 SkPaint labelPaint;
157 SkFont font(ToolUtils::create_portable_typeface("sans-serif", SkFontStyle()), kLabelSize);
158
159 int gridNum = 0;
160
161 // Running through all possible bool parameter combinations
162 for (bool useNormalSource : {true, false}) {
163 for (bool useDiffuseShader : {true, false}) {
164 for (bool useTranslucentPaint : {true, false}) {
165 for (bool useTranslucentShader : {true, false}) {
166
167 // Determining position
168 SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
169 SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
170
171 canvas->save();
172
173 canvas->translate(xPos, yPos);
174 this->drawRect(canvas, 1.0f, 1.0f, 0.f, useNormalSource, useDiffuseShader,
175 useTranslucentPaint, useTranslucentShader, fLights);
176 // Drawing labels
177 canvas->translate(0.0f, SkIntToScalar(kTexSize));
178 {
179 canvas->translate(0.0f, kLabelSize);
180 SkString label;
181 label.appendf("useNormalSource: %d", useNormalSource);
182 canvas->drawString(label, 0.0f, 0.0f, font, labelPaint);
183 }
184 {
185 canvas->translate(0.0f, kLabelSize);
186 SkString label;
187 label.appendf("useDiffuseShader: %d", useDiffuseShader);
188 canvas->drawString(label, 0.0f, 0.0f, font, labelPaint);
189 }
190 {
191 canvas->translate(0.0f, kLabelSize);
192 SkString label;
193 label.appendf("useTranslucentPaint: %d", useTranslucentPaint);
194 canvas->drawString(label, 0.0f, 0.0f, font, labelPaint);
195 }
196 {
197 canvas->translate(0.0f, kLabelSize);
198 SkString label;
199 label.appendf("useTranslucentShader: %d", useTranslucentShader);
200 canvas->drawString(label, 0.0f, 0.0f, font, labelPaint);
201 }
202
203 canvas->restore();
204
205 gridNum++;
206 }
207 }
208 }
209 }
210
211
212 // Rotation/scale test
213 {
214 SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
215 SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
216
217 canvas->save();
218 canvas->translate(xPos, yPos);
219 this->drawRect(canvas, 0.6f, 0.6f, 45.0f, true, true, true, true, fLights);
220 canvas->restore();
221
222 gridNum++;
223 }
224
225 // Anisotropic scale test
226 {
227 SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
228 SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
229
230 canvas->save();
231 canvas->translate(xPos, yPos);
232 this->drawRect(canvas, 0.6f, 0.4f, 30.0f, true, true, true, true, fLights);
233 canvas->restore();
234
235 gridNum++;
236 }
237
238 // No directional lights test
239 {
240 SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
241 SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
242
243 canvas->save();
244 canvas->translate(xPos, yPos);
245 this->drawRect(canvas, 1.0f, 1.0f, 0.0f, true, true, false, false, fLightsNoDir);
246 canvas->restore();
247
248 gridNum++;
249 }
250
251 // Two directional lights test
252 {
253 SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
254 SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
255
256 canvas->save();
257 canvas->translate(xPos, yPos);
258 this->drawRect(canvas, 1.0f, 1.0f, 0.0f, true, true, false, false, fLightsTwoDir);
259 canvas->restore();
260
261 gridNum++;
262 }
263 }
264
265 private:
266 static constexpr int kTexSize = 96;
267 static constexpr int kNumBooleanParams = 4;
268 static constexpr SkScalar kLabelSize = 10.0f;
269 static constexpr int kGridColumnNum = 4;
270 static constexpr SkScalar kGridCellWidth = kTexSize + 20.0f + kNumBooleanParams * kLabelSize;
271
272 sk_sp<SkShader> fOpaqueDiffuse;
273 sk_sp<SkShader> fTranslucentDiffuse;
274 sk_sp<SkShader> fNormalMapShader;
275
276 const SkRect fRect;
277 sk_sp<SkLights> fLights;
278 sk_sp<SkLights> fLightsNoDir;
279 sk_sp<SkLights> fLightsTwoDir;
280
281 typedef GM INHERITED;
282 };
283
284 //////////////////////////////////////////////////////////////////////////////
285
286 DEF_GM(return new LightingShader2GM;)
287 }
288