1 /*
2 * Copyright 2011 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 "gm/verifiers/gmverifier.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkBlendMode.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkFont.h"
14 #include "include/core/SkFontTypes.h"
15 #include "include/core/SkMatrix.h"
16 #include "include/core/SkPaint.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkShader.h"
19 #include "include/core/SkTileMode.h"
20 #include "include/core/SkTypeface.h"
21 #include "include/gpu/GrRecordingContext.h"
22 #include "src/core/SkCanvasPriv.h"
23 #include "src/core/SkTraceEvent.h"
24 #include "tools/ToolUtils.h"
25
26 #include <stdarg.h>
27
28 using namespace skiagm;
29
draw_failure_message(SkCanvas * canvas,const char format[],...)30 static void draw_failure_message(SkCanvas* canvas, const char format[], ...) {
31 SkString failureMsg;
32
33 va_list argp;
34 va_start(argp, format);
35 failureMsg.appendVAList(format, argp);
36 va_end(argp);
37
38 constexpr SkScalar kOffset = 5.0f;
39 canvas->drawColor(SkColorSetRGB(200,0,0));
40 SkFont font;
41 SkRect bounds;
42 font.measureText(failureMsg.c_str(), failureMsg.size(), SkTextEncoding::kUTF8, &bounds);
43 SkPaint textPaint(SkColors::kWhite);
44 canvas->drawString(failureMsg, kOffset, bounds.height() + kOffset, font, textPaint);
45 }
46
draw_gpu_only_message(SkCanvas * canvas)47 static void draw_gpu_only_message(SkCanvas* canvas) {
48 SkBitmap bmp;
49 bmp.allocN32Pixels(128, 64);
50 SkCanvas bmpCanvas(bmp);
51 bmpCanvas.drawColor(SK_ColorWHITE);
52 SkFont font(ToolUtils::create_portable_typeface(), 20);
53 SkPaint paint(SkColors::kRed);
54 bmpCanvas.drawString("GPU Only", 20, 40, font, paint);
55 SkMatrix localM;
56 localM.setRotate(35.f);
57 localM.postTranslate(10.f, 0.f);
58 paint.setShader(bmp.makeShader(SkTileMode::kMirror, SkTileMode::kMirror,
59 SkSamplingOptions(SkFilterMode::kLinear,
60 SkMipmapMode::kNearest),
61 localM));
62 canvas->drawPaint(paint);
63 }
64
handle_gm_failure(SkCanvas * canvas,DrawResult result,const SkString & errorMsg)65 static void handle_gm_failure(SkCanvas* canvas, DrawResult result, const SkString& errorMsg) {
66 if (DrawResult::kFail == result) {
67 draw_failure_message(canvas, "DRAW FAILED: %s", errorMsg.c_str());
68 } else if (SkString(GM::kErrorMsg_DrawSkippedGpuOnly) == errorMsg) {
69 draw_gpu_only_message(canvas);
70 } else {
71 draw_failure_message(canvas, "DRAW SKIPPED: %s", errorMsg.c_str());
72 }
73 }
74
GM(SkColor bgColor)75 GM::GM(SkColor bgColor) {
76 fMode = kGM_Mode;
77 fBGColor = bgColor;
78 }
79
~GM()80 GM::~GM() {}
81
gpuSetup(GrDirectContext * context,SkCanvas * canvas,SkString * errorMsg)82 DrawResult GM::gpuSetup(GrDirectContext* context, SkCanvas* canvas, SkString* errorMsg) {
83 TRACE_EVENT1("GM", TRACE_FUNC, "name", TRACE_STR_COPY(this->getName()));
84 if (!fGpuSetup) {
85 // When drawn in viewer, gpuSetup will be called multiple times with the same
86 // GrContext.
87 fGpuSetup = true;
88 fGpuSetupResult = this->onGpuSetup(context, errorMsg);
89 }
90 if (DrawResult::kOk != fGpuSetupResult) {
91 handle_gm_failure(canvas, fGpuSetupResult, *errorMsg);
92 }
93
94 return fGpuSetupResult;
95 }
96
gpuTeardown()97 void GM::gpuTeardown() {
98 this->onGpuTeardown();
99
100 // After 'gpuTeardown' a GM can be reused with a different GrContext. Reset the flag
101 // so 'onGpuSetup' will be called.
102 fGpuSetup = false;
103 }
104
draw(SkCanvas * canvas,SkString * errorMsg)105 DrawResult GM::draw(SkCanvas* canvas, SkString* errorMsg) {
106 TRACE_EVENT1("GM", TRACE_FUNC, "name", TRACE_STR_COPY(this->getName()));
107 this->drawBackground(canvas);
108 return this->drawContent(canvas, errorMsg);
109 }
110
drawContent(SkCanvas * canvas,SkString * errorMsg)111 DrawResult GM::drawContent(SkCanvas* canvas, SkString* errorMsg) {
112 TRACE_EVENT0("GM", TRACE_FUNC);
113 this->onceBeforeDraw();
114 SkAutoCanvasRestore acr(canvas, true);
115 DrawResult drawResult = this->onDraw(canvas, errorMsg);
116 if (DrawResult::kOk != drawResult) {
117 handle_gm_failure(canvas, drawResult, *errorMsg);
118 }
119 return drawResult;
120 }
121
drawBackground(SkCanvas * canvas)122 void GM::drawBackground(SkCanvas* canvas) {
123 TRACE_EVENT0("GM", TRACE_FUNC);
124 this->onceBeforeDraw();
125 canvas->drawColor(fBGColor, SkBlendMode::kSrc);
126 }
127
onDraw(SkCanvas * canvas,SkString * errorMsg)128 DrawResult GM::onDraw(SkCanvas* canvas, SkString* errorMsg) {
129 this->onDraw(canvas);
130 return DrawResult::kOk;
131 }
onDraw(SkCanvas *)132 void GM::onDraw(SkCanvas*) { SK_ABORT("Not implemented."); }
133
134
onISize()135 SkISize SimpleGM::onISize() { return fSize; }
onShortName()136 SkString SimpleGM::onShortName() { return fName; }
onDraw(SkCanvas * canvas,SkString * errorMsg)137 DrawResult SimpleGM::onDraw(SkCanvas* canvas, SkString* errorMsg) {
138 return fDrawProc(canvas, errorMsg);
139 }
140
onISize()141 SkISize SimpleGpuGM::onISize() { return fSize; }
onShortName()142 SkString SimpleGpuGM::onShortName() { return fName; }
onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)143 DrawResult SimpleGpuGM::onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) {
144 return fDrawProc(rContext, canvas, errorMsg);
145 }
146
getName()147 const char* GM::getName() {
148 if (fShortName.size() == 0) {
149 fShortName = this->onShortName();
150 }
151 return fShortName.c_str();
152 }
153
setBGColor(SkColor color)154 void GM::setBGColor(SkColor color) {
155 fBGColor = color;
156 }
157
animate(double nanos)158 bool GM::animate(double nanos) { return this->onAnimate(nanos); }
159
runAsBench() const160 bool GM::runAsBench() const { return false; }
modifyGrContextOptions(GrContextOptions * options)161 void GM::modifyGrContextOptions(GrContextOptions* options) {}
162
getVerifiers() const163 std::unique_ptr<verifiers::VerifierList> GM::getVerifiers() const {
164 // No verifiers by default.
165 return nullptr;
166 }
167
onOnceBeforeDraw()168 void GM::onOnceBeforeDraw() {}
169
onAnimate(double)170 bool GM::onAnimate(double /*nanos*/) { return false; }
171
onChar(SkUnichar uni)172 bool GM::onChar(SkUnichar uni) { return false; }
173
onGetControls(SkMetaData *)174 bool GM::onGetControls(SkMetaData*) { return false; }
175
onSetControls(const SkMetaData &)176 void GM::onSetControls(const SkMetaData&) {}
177
178 /////////////////////////////////////////////////////////////////////////////////////////////
179
drawSizeBounds(SkCanvas * canvas,SkColor color)180 void GM::drawSizeBounds(SkCanvas* canvas, SkColor color) {
181 canvas->drawRect(SkRect::Make(this->getISize()), SkPaint(SkColor4f::FromColor(color)));
182 }
183
184 // need to explicitly declare this, or we get some weird infinite loop llist
185 template GMRegistry* GMRegistry::gHead;
186
onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)187 DrawResult GpuGM::onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) {
188 this->onDraw(rContext, canvas);
189 return DrawResult::kOk;
190 }
onDraw(GrRecordingContext *,SkCanvas *)191 void GpuGM::onDraw(GrRecordingContext*, SkCanvas*) {
192 SK_ABORT("Not implemented.");
193 }
194
onDraw(SkCanvas * canvas,SkString * errorMsg)195 DrawResult GpuGM::onDraw(SkCanvas* canvas, SkString* errorMsg) {
196
197 auto rContext = canvas->recordingContext();
198 if (!rContext) {
199 *errorMsg = kErrorMsg_DrawSkippedGpuOnly;
200 return DrawResult::kSkip;
201 }
202 if (rContext->abandoned()) {
203 *errorMsg = "GrContext abandoned.";
204 return DrawResult::kSkip;
205 }
206 return this->onDraw(rContext, canvas, errorMsg);
207 }
208
209 template <typename Fn>
mark(SkCanvas * canvas,SkScalar x,SkScalar y,Fn && fn)210 static void mark(SkCanvas* canvas, SkScalar x, SkScalar y, Fn&& fn) {
211 SkPaint alpha;
212 alpha.setAlpha(0x50);
213 canvas->saveLayer(nullptr, &alpha);
214 canvas->translate(x,y);
215 canvas->scale(2,2);
216 fn();
217 canvas->restore();
218 }
219
MarkGMGood(SkCanvas * canvas,SkScalar x,SkScalar y)220 void MarkGMGood(SkCanvas* canvas, SkScalar x, SkScalar y) {
221 mark(canvas, x,y, [&]{
222 // A green circle.
223 canvas->drawCircle(0, 0, 12, SkPaint(SkColor4f::FromColor(SkColorSetRGB(27, 158, 119))));
224
225 // Cut out a check mark.
226 SkPaint paint(SkColors::kTransparent);
227 paint.setBlendMode(SkBlendMode::kSrc);
228 paint.setStrokeWidth(2);
229 paint.setStyle(SkPaint::kStroke_Style);
230 canvas->drawLine(-6, 0,
231 -1, 5, paint);
232 canvas->drawLine(-1, +5,
233 +7, -5, paint);
234 });
235 }
236
MarkGMBad(SkCanvas * canvas,SkScalar x,SkScalar y)237 void MarkGMBad(SkCanvas* canvas, SkScalar x, SkScalar y) {
238 mark(canvas, x,y, [&] {
239 // A red circle.
240 canvas->drawCircle(0,0, 12, SkPaint(SkColor4f::FromColor(SkColorSetRGB(231, 41, 138))));
241
242 // Cut out an 'X'.
243 SkPaint paint(SkColors::kTransparent);
244 paint.setBlendMode(SkBlendMode::kSrc);
245 paint.setStrokeWidth(2);
246 paint.setStyle(SkPaint::kStroke_Style);
247 canvas->drawLine(-5,-5,
248 +5,+5, paint);
249 canvas->drawLine(+5,-5,
250 -5,+5, paint);
251 });
252 }
253