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