blob: 7052a3ac79244d7ee724ea9f23a900e675ef2b64 [file] [log] [blame]
jvanverth9f372462016-04-06 06:08:59 -07001/*
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
jvanverth34524262016-05-04 13:49:13 -07008#include "Viewer.h"
jvanverth9f372462016-04-06 06:08:59 -07009
jvanverth2bb3b6d2016-04-08 07:24:09 -070010#include "GMSlide.h"
liyuqian6f163d22016-06-13 12:26:45 -070011#include "ImageSlide.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070012#include "SKPSlide.h"
jvanverth9f372462016-04-06 06:08:59 -070013
jvanverth2bb3b6d2016-04-08 07:24:09 -070014#include "SkCanvas.h"
15#include "SkCommonFlags.h"
liyuqian6f163d22016-06-13 12:26:45 -070016#include "SkMetaData.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070017#include "SkOSFile.h"
18#include "SkRandom.h"
19#include "SkStream.h"
jvanverth9f372462016-04-06 06:08:59 -070020
jvanverth34524262016-05-04 13:49:13 -070021using namespace sk_app;
22
jvanverth9f372462016-04-06 06:08:59 -070023Application* Application::Create(int argc, char** argv, void* platformData) {
jvanverth34524262016-05-04 13:49:13 -070024 return new Viewer(argc, argv, platformData);
jvanverth9f372462016-04-06 06:08:59 -070025}
26
jvanverth9f372462016-04-06 06:08:59 -070027static void on_paint_handler(SkCanvas* canvas, void* userData) {
jvanverth34524262016-05-04 13:49:13 -070028 Viewer* vv = reinterpret_cast<Viewer*>(userData);
jvanverth9f372462016-04-06 06:08:59 -070029
30 return vv->onPaint(canvas);
31}
32
jvanverth814e38d2016-06-06 08:48:47 -070033static bool on_touch_handler(intptr_t owner, Window::InputState state, float x, float y, void* userData)
liyuqiand3cdbca2016-05-17 12:44:20 -070034{
35 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
36
37 return viewer->onTouch(owner, state, x, y);
38}
39
liyuqiane5a6cd92016-05-27 08:52:52 -070040static void on_ui_state_changed_handler(const SkString& stateName, const SkString& stateValue, void* userData) {
41 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
42
43 return viewer->onUIStateChanged(stateName, stateValue);
44}
45
46DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
egdanielf533f112016-06-13 11:30:10 -070047DEFINE_string(key, "", "Space-separated key/value pairs to add to JSON identifying this builder.");
jvanverth2bb3b6d2016-04-08 07:24:09 -070048DEFINE_string2(match, m, nullptr,
49 "[~][^]substring[$] [...] of bench name to run.\n"
50 "Multiple matches may be separated by spaces.\n"
51 "~ causes a matching bench to always be skipped\n"
52 "^ requires the start of the bench to match\n"
53 "$ requires the end of the bench to match\n"
54 "^ and $ requires an exact match\n"
55 "If a bench does not match any list entry,\n"
56 "it is skipped unless some list entry starts with ~");
liyuqian71491dc2016-06-09 12:02:34 -070057#ifdef SK_BUILD_FOR_ANDROID
liyuqian6f163d22016-06-13 12:26:45 -070058DEFINE_string(skps, "/data/local/tmp/skia", "Directory to read skps from.");
59DEFINE_string(jpgs, "/data/local/tmp/skia", "Directory to read jpgs from.");
liyuqian71491dc2016-06-09 12:02:34 -070060DEFINE_bool(vulkan, false, "Run with Vulkan.");
61#else
liyuqian6f163d22016-06-13 12:26:45 -070062DEFINE_string(skps, "skps", "Directory to read skps from.");
63DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
jvanverth85f758c2016-05-27 06:47:08 -070064DEFINE_bool(vulkan, true, "Run with Vulkan.");
liyuqian71491dc2016-06-09 12:02:34 -070065#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -070066
jvanverthaf236b52016-05-20 06:01:06 -070067const char *kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
68 " [OpenGL]",
liyuqiand94ad582016-06-07 14:22:37 -070069 " [Vulkan]",
70 " [Raster]"
jvanverthaf236b52016-05-20 06:01:06 -070071};
72
liyuqiane5a6cd92016-05-27 08:52:52 -070073const char* kName = "name";
74const char* kValue = "value";
75const char* kOptions = "options";
76const char* kSlideStateName = "Slide";
77const char* kBackendStateName = "Backend";
liyuqianb73c24b2016-06-03 08:47:23 -070078const char* kSoftkeyStateName = "Softkey";
79const char* kSoftkeyHint = "Please select a softkey";
liyuqian1f508fd2016-06-07 06:57:40 -070080const char* kFpsStateName = "FPS";
liyuqian6f163d22016-06-13 12:26:45 -070081const char* kSplitScreenStateName = "Split screen";
82const char* kON = "ON";
83const char* kOFF = "OFF";
liyuqiane5a6cd92016-05-27 08:52:52 -070084
jvanverth34524262016-05-04 13:49:13 -070085Viewer::Viewer(int argc, char** argv, void* platformData)
jvanverthc265a922016-04-08 12:51:45 -070086 : fCurrentMeasurement(0)
87 , fDisplayStats(false)
liyuqian6f163d22016-06-13 12:26:45 -070088 , fSplitScreen(false)
egdaniel963632f2016-06-15 14:23:40 -070089 , fBackendType(sk_app::Window::kVulkan_BackendType)
egdaniel2a0bb0a2016-04-11 08:30:40 -070090 , fZoomCenterX(0.0f)
91 , fZoomCenterY(0.0f)
92 , fZoomLevel(0.0f)
93 , fZoomScale(SK_Scalar1)
jvanverthc265a922016-04-08 12:51:45 -070094{
jvanverth3d6ed3a2016-04-07 11:09:51 -070095 memset(fMeasurements, 0, sizeof(fMeasurements));
jvanverth9f372462016-04-06 06:08:59 -070096
jvanverth2bb3b6d2016-04-08 07:24:09 -070097 SkDebugf("Command line arguments: ");
98 for (int i = 1; i < argc; ++i) {
99 SkDebugf("%s ", argv[i]);
100 }
101 SkDebugf("\n");
102
103 SkCommandLineFlags::Parse(argc, argv);
104
jvanverth85f758c2016-05-27 06:47:08 -0700105 fBackendType = FLAGS_vulkan ? sk_app::Window::kVulkan_BackendType
106 : sk_app::Window::kNativeGL_BackendType;
egdaniel963632f2016-06-15 14:23:40 -0700107
jvanverth9f372462016-04-06 06:08:59 -0700108 fWindow = Window::CreateNativeWindow(platformData);
jvanverthaf236b52016-05-20 06:01:06 -0700109 fWindow->attach(fBackendType, DisplayParams());
jvanverth9f372462016-04-06 06:08:59 -0700110
111 // register callbacks
brianosman622c8d52016-05-10 06:50:49 -0700112 fCommands.attach(fWindow);
jvanverth9f372462016-04-06 06:08:59 -0700113 fWindow->registerPaintFunc(on_paint_handler, this);
liyuqiand3cdbca2016-05-17 12:44:20 -0700114 fWindow->registerTouchFunc(on_touch_handler, this);
liyuqiane5a6cd92016-05-27 08:52:52 -0700115 fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
jvanverth9f372462016-04-06 06:08:59 -0700116
brianosman622c8d52016-05-10 06:50:49 -0700117 // add key-bindings
118 fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
119 this->fDisplayStats = !this->fDisplayStats;
120 fWindow->inval();
121 });
122 fCommands.addCommand('c', "Modes", "Toggle sRGB color mode", [this]() {
123 DisplayParams params = fWindow->getDisplayParams();
124 params.fProfileType = (kLinear_SkColorProfileType == params.fProfileType)
125 ? kSRGB_SkColorProfileType : kLinear_SkColorProfileType;
126 fWindow->setDisplayParams(params);
127 this->updateTitle();
128 fWindow->inval();
129 });
130 fCommands.addCommand(Window::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
131 int previousSlide = fCurrentSlide;
132 fCurrentSlide++;
133 if (fCurrentSlide >= fSlides.count()) {
134 fCurrentSlide = 0;
135 }
136 this->setupCurrentSlide(previousSlide);
137 });
138 fCommands.addCommand(Window::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
139 int previousSlide = fCurrentSlide;
140 fCurrentSlide--;
141 if (fCurrentSlide < 0) {
142 fCurrentSlide = fSlides.count() - 1;
143 }
144 this->setupCurrentSlide(previousSlide);
145 });
146 fCommands.addCommand(Window::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
147 this->changeZoomLevel(1.f / 32.f);
148 fWindow->inval();
149 });
150 fCommands.addCommand(Window::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
151 this->changeZoomLevel(-1.f / 32.f);
152 fWindow->inval();
153 });
jvanverth85f758c2016-05-27 06:47:08 -0700154#if 0 // this doesn't seem to work on any platform right now
jvanverthaf236b52016-05-20 06:01:06 -0700155#ifndef SK_BUILD_FOR_ANDROID
156 fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
157 fWindow->detach();
158
159 if (sk_app::Window::kVulkan_BackendType == fBackendType) {
160 fBackendType = sk_app::Window::kNativeGL_BackendType;
161 }
jvanverth85f758c2016-05-27 06:47:08 -0700162 // TODO: get Vulkan -> OpenGL working on Windows without swapchain creation failure
jvanverthaf236b52016-05-20 06:01:06 -0700163 //else if (sk_app::Window::kNativeGL_BackendType == fBackendType) {
164 // fBackendType = sk_app::Window::kVulkan_BackendType;
165 //}
166
167 fWindow->attach(fBackendType, DisplayParams());
168 this->updateTitle();
jvanverth85f758c2016-05-27 06:47:08 -0700169 fWindow->inval();
jvanverthaf236b52016-05-20 06:01:06 -0700170 });
171#endif
jvanverth85f758c2016-05-27 06:47:08 -0700172#endif
brianosman622c8d52016-05-10 06:50:49 -0700173
jvanverth2bb3b6d2016-04-08 07:24:09 -0700174 // set up slides
175 this->initSlides();
176
djsollen12d62a72016-04-21 07:59:44 -0700177 fAnimTimer.run();
178
jvanverth2bb3b6d2016-04-08 07:24:09 -0700179 // set up first frame
jvanverth2bb3b6d2016-04-08 07:24:09 -0700180 fCurrentSlide = 0;
jvanverthc265a922016-04-08 12:51:45 -0700181 setupCurrentSlide(-1);
jvanverthc265a922016-04-08 12:51:45 -0700182
jvanverth9f372462016-04-06 06:08:59 -0700183 fWindow->show();
184}
185
jvanverth34524262016-05-04 13:49:13 -0700186void Viewer::initSlides() {
liyuqian1f508fd2016-06-07 06:57:40 -0700187 fAllSlideNames = Json::Value(Json::arrayValue);
188
jvanverth2bb3b6d2016-04-08 07:24:09 -0700189 const skiagm::GMRegistry* gms(skiagm::GMRegistry::Head());
190 while (gms) {
191 SkAutoTDelete<skiagm::GM> gm(gms->factory()(nullptr));
192
193 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
194 sk_sp<Slide> slide(new GMSlide(gm.release()));
195 fSlides.push_back(slide);
196 }
197
198 gms = gms->next();
199 }
200
201 // reverse array
202 for (int i = 0; i < fSlides.count()/2; ++i) {
203 sk_sp<Slide> temp = fSlides[i];
204 fSlides[i] = fSlides[fSlides.count() - i - 1];
205 fSlides[fSlides.count() - i - 1] = temp;
206 }
207
208 // SKPs
209 for (int i = 0; i < FLAGS_skps.count(); i++) {
210 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
jvanverthc265a922016-04-08 12:51:45 -0700211 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, FLAGS_skps[i])) {
212 continue;
213 }
214
jvanverth2bb3b6d2016-04-08 07:24:09 -0700215 SkString path(FLAGS_skps[i]);
jvanverthc265a922016-04-08 12:51:45 -0700216 sk_sp<SKPSlide> slide(new SKPSlide(SkOSPath::Basename(path.c_str()), path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700217 if (slide) {
218 fSlides.push_back(slide);
219 }
220 } else {
221 SkOSFile::Iter it(FLAGS_skps[i], ".skp");
jvanverthc265a922016-04-08 12:51:45 -0700222 SkString skpName;
223 while (it.next(&skpName)) {
224 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, skpName.c_str())) {
225 continue;
226 }
227
228 SkString path = SkOSPath::Join(FLAGS_skps[i], skpName.c_str());
229 sk_sp<SKPSlide> slide(new SKPSlide(skpName, path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700230 if (slide) {
231 fSlides.push_back(slide);
232 }
233 }
234 }
235 }
liyuqian6f163d22016-06-13 12:26:45 -0700236
237 // JPGs
238 for (int i = 0; i < FLAGS_jpgs.count(); i++) {
239 SkOSFile::Iter it(FLAGS_jpgs[i], ".jpg");
240 SkString jpgName;
241 while (it.next(&jpgName)) {
242 SkString path = SkOSPath::Join(FLAGS_jpgs[i], jpgName.c_str());
243 sk_sp<ImageSlide> slide(new ImageSlide(jpgName, path));
244 if (slide) {
245 fSlides.push_back(slide);
246 }
247 }
248 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700249}
250
251
jvanverth34524262016-05-04 13:49:13 -0700252Viewer::~Viewer() {
jvanverth9f372462016-04-06 06:08:59 -0700253 fWindow->detach();
254 delete fWindow;
255}
256
brianosman05de2162016-05-06 13:28:57 -0700257void Viewer::updateTitle() {
jvanverth34524262016-05-04 13:49:13 -0700258 SkString title("Viewer: ");
jvanverthc265a922016-04-08 12:51:45 -0700259 title.append(fSlides[fCurrentSlide]->getName());
brianosman05de2162016-05-06 13:28:57 -0700260 if (kSRGB_SkColorProfileType == fWindow->getDisplayParams().fProfileType) {
261 title.append(" sRGB");
262 }
jvanverthaf236b52016-05-20 06:01:06 -0700263 title.append(kBackendTypeStrings[fBackendType]);
brianosman05de2162016-05-06 13:28:57 -0700264 fWindow->setTitle(title.c_str());
265}
266
267void Viewer::setupCurrentSlide(int previousSlide) {
liyuqiane5a6cd92016-05-27 08:52:52 -0700268 if (fCurrentSlide == previousSlide) {
269 return; // no change; do nothing
270 }
271
liyuqian6f163d22016-06-13 12:26:45 -0700272 // prepare dimensions for image slides
273 fSlides[fCurrentSlide]->load();
274
liyuqiane46e4f02016-05-20 07:32:19 -0700275 fGesture.reset();
276 fDefaultMatrix.reset();
277 fDefaultMatrixInv.reset();
278
279 if (fWindow->supportsContentRect() && fWindow->scaleContentToFit()) {
280 const SkRect contentRect = fWindow->getContentRect();
281 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
282 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
283 if (contentRect.width() > 0 && contentRect.height() > 0) {
284 fDefaultMatrix.setRectToRect(slideBounds, contentRect, SkMatrix::kStart_ScaleToFit);
liyuqianbeb1c672016-05-20 11:41:01 -0700285 SkAssertResult(fDefaultMatrix.invert(&fDefaultMatrixInv));
liyuqiane46e4f02016-05-20 07:32:19 -0700286 }
287 }
288
289 if (fWindow->supportsContentRect()) {
290 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
291 SkRect windowRect = fWindow->getContentRect();
292 fDefaultMatrixInv.mapRect(&windowRect);
jvanverth1e305ba2016-06-01 09:39:15 -0700293 fGesture.setTransLimit(SkRect::MakeWH(SkIntToScalar(slideSize.width()),
294 SkIntToScalar(slideSize.height())),
295 windowRect);
liyuqiane46e4f02016-05-20 07:32:19 -0700296 }
297
brianosman05de2162016-05-06 13:28:57 -0700298 this->updateTitle();
liyuqiane5a6cd92016-05-27 08:52:52 -0700299 this->updateUIState();
jvanverthc265a922016-04-08 12:51:45 -0700300 if (previousSlide >= 0) {
301 fSlides[previousSlide]->unload();
302 }
jvanverthc265a922016-04-08 12:51:45 -0700303 fWindow->inval();
304}
305
306#define MAX_ZOOM_LEVEL 8
307#define MIN_ZOOM_LEVEL -8
308
jvanverth34524262016-05-04 13:49:13 -0700309void Viewer::changeZoomLevel(float delta) {
jvanverthc265a922016-04-08 12:51:45 -0700310 fZoomLevel += delta;
311 if (fZoomLevel > 0) {
312 fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
313 fZoomScale = fZoomLevel + SK_Scalar1;
314 } else if (fZoomLevel < 0) {
315 fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
316 fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
317 } else {
318 fZoomScale = SK_Scalar1;
319 }
jvanverthc265a922016-04-08 12:51:45 -0700320}
321
liyuqiand3cdbca2016-05-17 12:44:20 -0700322SkMatrix Viewer::computeMatrix() {
jvanverthc265a922016-04-08 12:51:45 -0700323 SkMatrix m;
324 m.reset();
325
326 if (fZoomLevel) {
327 SkPoint center;
328 //m = this->getLocalMatrix();//.invert(&m);
329 m.mapXY(fZoomCenterX, fZoomCenterY, &center);
330 SkScalar cx = center.fX;
331 SkScalar cy = center.fY;
332
333 m.setTranslate(-cx, -cy);
334 m.postScale(fZoomScale, fZoomScale);
335 m.postTranslate(cx, cy);
336 }
337
liyuqiand3cdbca2016-05-17 12:44:20 -0700338 m.preConcat(fGesture.localM());
339 m.preConcat(fGesture.globalM());
jvanverthc265a922016-04-08 12:51:45 -0700340
liyuqiand3cdbca2016-05-17 12:44:20 -0700341 return m;
jvanverthc265a922016-04-08 12:51:45 -0700342}
343
liyuqian6f163d22016-06-13 12:26:45 -0700344void Viewer::drawSlide(SkCanvas* canvas, bool inSplitScreen) {
345 SkASSERT(!inSplitScreen || fWindow->supportsContentRect());
346
jvanverthc265a922016-04-08 12:51:45 -0700347 int count = canvas->save();
djsollen12d62a72016-04-21 07:59:44 -0700348
349 if (fWindow->supportsContentRect()) {
350 SkRect contentRect = fWindow->getContentRect();
liyuqian6f163d22016-06-13 12:26:45 -0700351 // If inSplitScreen, translate the image half screen to the right.
352 // Thus we have two copies of the image on each half of the screen.
liyuqian401cf482016-06-13 14:38:35 -0700353 contentRect.fLeft +=
354 inSplitScreen ? (contentRect.fRight - contentRect.fLeft) * 0.5f : 0.0f;
djsollen12d62a72016-04-21 07:59:44 -0700355 canvas->clipRect(contentRect);
356 canvas->translate(contentRect.fLeft, contentRect.fTop);
357 }
358
359 canvas->clear(SK_ColorWHITE);
liyuqiane46e4f02016-05-20 07:32:19 -0700360 canvas->concat(fDefaultMatrix);
liyuqiand3cdbca2016-05-17 12:44:20 -0700361 canvas->concat(computeMatrix());
jvanverth3d6ed3a2016-04-07 11:09:51 -0700362
liyuqian6f163d22016-06-13 12:26:45 -0700363 canvas->getMetaData().setBool(kImageColorXformMetaData, inSplitScreen);
jvanverthc265a922016-04-08 12:51:45 -0700364 fSlides[fCurrentSlide]->draw(canvas);
365 canvas->restoreToCount(count);
liyuqian6f163d22016-06-13 12:26:45 -0700366}
367
368void Viewer::onPaint(SkCanvas* canvas) {
369 drawSlide(canvas, false);
370 if (fSplitScreen && fWindow->supportsContentRect()) {
371 drawSlide(canvas, true);
372 }
jvanverthc265a922016-04-08 12:51:45 -0700373
374 if (fDisplayStats) {
375 drawStats(canvas);
376 }
brianosman622c8d52016-05-10 06:50:49 -0700377 fCommands.drawHelp(canvas);
jvanverth3d6ed3a2016-04-07 11:09:51 -0700378}
379
jvanverth814e38d2016-06-06 08:48:47 -0700380bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) {
liyuqiand3cdbca2016-05-17 12:44:20 -0700381 void* castedOwner = reinterpret_cast<void*>(owner);
liyuqiane46e4f02016-05-20 07:32:19 -0700382 SkPoint touchPoint = fDefaultMatrixInv.mapXY(x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -0700383 switch (state) {
384 case Window::kUp_InputState: {
385 fGesture.touchEnd(castedOwner);
386 break;
387 }
388 case Window::kDown_InputState: {
liyuqiane46e4f02016-05-20 07:32:19 -0700389 fGesture.touchBegin(castedOwner, touchPoint.fX, touchPoint.fY);
liyuqiand3cdbca2016-05-17 12:44:20 -0700390 break;
391 }
392 case Window::kMove_InputState: {
liyuqiane46e4f02016-05-20 07:32:19 -0700393 fGesture.touchMoved(castedOwner, touchPoint.fX, touchPoint.fY);
liyuqiand3cdbca2016-05-17 12:44:20 -0700394 break;
395 }
396 }
397 fWindow->inval();
398 return true;
399}
400
jvanverth34524262016-05-04 13:49:13 -0700401void Viewer::drawStats(SkCanvas* canvas) {
jvanverth3d6ed3a2016-04-07 11:09:51 -0700402 static const float kPixelPerMS = 2.0f;
403 static const int kDisplayWidth = 130;
404 static const int kDisplayHeight = 100;
405 static const int kDisplayPadding = 10;
406 static const int kGraphPadding = 3;
407 static const SkScalar kBaseMS = 1000.f / 60.f; // ms/frame to hit 60 fps
408
409 SkISize canvasSize = canvas->getDeviceSize();
410 SkRect rect = SkRect::MakeXYWH(SkIntToScalar(canvasSize.fWidth-kDisplayWidth-kDisplayPadding),
411 SkIntToScalar(kDisplayPadding),
412 SkIntToScalar(kDisplayWidth), SkIntToScalar(kDisplayHeight));
413 SkPaint paint;
414 canvas->save();
415
djsollen12d62a72016-04-21 07:59:44 -0700416 if (fWindow->supportsContentRect()) {
417 SkRect contentRect = fWindow->getContentRect();
418 canvas->clipRect(contentRect);
419 canvas->translate(contentRect.fLeft, contentRect.fTop);
420 }
421
jvanverth3d6ed3a2016-04-07 11:09:51 -0700422 canvas->clipRect(rect);
423 paint.setColor(SK_ColorBLACK);
424 canvas->drawRect(rect, paint);
425 // draw the 16ms line
426 paint.setColor(SK_ColorLTGRAY);
427 canvas->drawLine(rect.fLeft, rect.fBottom - kBaseMS*kPixelPerMS,
428 rect.fRight, rect.fBottom - kBaseMS*kPixelPerMS, paint);
429 paint.setColor(SK_ColorRED);
430 paint.setStyle(SkPaint::kStroke_Style);
431 canvas->drawRect(rect, paint);
432
433 int x = SkScalarTruncToInt(rect.fLeft) + kGraphPadding;
434 const int xStep = 2;
435 const int startY = SkScalarTruncToInt(rect.fBottom);
436 int i = fCurrentMeasurement;
437 do {
438 int endY = startY - (int)(fMeasurements[i] * kPixelPerMS + 0.5); // round to nearest value
439 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
440 SkIntToScalar(x), SkIntToScalar(endY), paint);
441 i++;
442 i &= (kMeasurementCount - 1); // fast mod
443 x += xStep;
444 } while (i != fCurrentMeasurement);
jvanverth9f372462016-04-06 06:08:59 -0700445
446 canvas->restore();
447}
448
jvanverth34524262016-05-04 13:49:13 -0700449void Viewer::onIdle(double ms) {
jvanverth3d6ed3a2016-04-07 11:09:51 -0700450 // Record measurements
451 fMeasurements[fCurrentMeasurement++] = ms;
452 fCurrentMeasurement &= (kMeasurementCount - 1); // fast mod
453 SkASSERT(fCurrentMeasurement < kMeasurementCount);
454
jvanverthc265a922016-04-08 12:51:45 -0700455 fAnimTimer.updateTime();
jvanverth9d5e47f2016-04-26 08:01:33 -0700456 if (fSlides[fCurrentSlide]->animate(fAnimTimer) || fDisplayStats) {
jvanverthc265a922016-04-08 12:51:45 -0700457 fWindow->inval();
liyuqian1f508fd2016-06-07 06:57:40 -0700458 updateUIState(); // Update the FPS
jvanverthc265a922016-04-08 12:51:45 -0700459 }
jvanverth9f372462016-04-06 06:08:59 -0700460}
liyuqiane5a6cd92016-05-27 08:52:52 -0700461
462void Viewer::updateUIState() {
liyuqianb73c24b2016-06-03 08:47:23 -0700463 // Slide state
liyuqiane5a6cd92016-05-27 08:52:52 -0700464 Json::Value slideState(Json::objectValue);
465 slideState[kName] = kSlideStateName;
466 slideState[kValue] = fSlides[fCurrentSlide]->getName().c_str();
liyuqian1f508fd2016-06-07 06:57:40 -0700467 if (fAllSlideNames.size() == 0) {
468 for(auto slide : fSlides) {
469 fAllSlideNames.append(Json::Value(slide->getName().c_str()));
470 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700471 }
liyuqian1f508fd2016-06-07 06:57:40 -0700472 slideState[kOptions] = fAllSlideNames;
liyuqiane5a6cd92016-05-27 08:52:52 -0700473
liyuqianb73c24b2016-06-03 08:47:23 -0700474 // Backend state
liyuqiane5a6cd92016-05-27 08:52:52 -0700475 Json::Value backendState(Json::objectValue);
476 backendState[kName] = kBackendStateName;
liyuqian6cb70252016-06-02 12:16:25 -0700477 backendState[kValue] = kBackendTypeStrings[fBackendType];
liyuqiane5a6cd92016-05-27 08:52:52 -0700478 backendState[kOptions] = Json::Value(Json::arrayValue);
liyuqianb73c24b2016-06-03 08:47:23 -0700479 for (auto str : kBackendTypeStrings) {
liyuqian6cb70252016-06-02 12:16:25 -0700480 backendState[kOptions].append(Json::Value(str));
481 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700482
liyuqianb73c24b2016-06-03 08:47:23 -0700483 // Softkey state
484 Json::Value softkeyState(Json::objectValue);
485 softkeyState[kName] = kSoftkeyStateName;
486 softkeyState[kValue] = kSoftkeyHint;
487 softkeyState[kOptions] = Json::Value(Json::arrayValue);
488 softkeyState[kOptions].append(kSoftkeyHint);
489 for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
490 softkeyState[kOptions].append(Json::Value(softkey.c_str()));
491 }
492
liyuqian1f508fd2016-06-07 06:57:40 -0700493 // FPS state
494 Json::Value fpsState(Json::objectValue);
495 fpsState[kName] = kFpsStateName;
496 double measurement = fMeasurements[
497 (fCurrentMeasurement + (kMeasurementCount-1)) % kMeasurementCount
498 ];
499 fpsState[kValue] = SkStringPrintf("%8.3lf ms", measurement).c_str();
500 fpsState[kOptions] = Json::Value(Json::arrayValue);
501
liyuqian6f163d22016-06-13 12:26:45 -0700502 // Split screen state
503 Json::Value splitScreenState(Json::objectValue);
504 splitScreenState[kName] = kSplitScreenStateName;
505 splitScreenState[kValue] = fSplitScreen ? kON : kOFF;
506 splitScreenState[kOptions] = Json::Value(Json::arrayValue);
507 splitScreenState[kOptions].append(kON);
508 splitScreenState[kOptions].append(kOFF);
509
liyuqiane5a6cd92016-05-27 08:52:52 -0700510 Json::Value state(Json::arrayValue);
511 state.append(slideState);
512 state.append(backendState);
liyuqianb73c24b2016-06-03 08:47:23 -0700513 state.append(softkeyState);
liyuqian1f508fd2016-06-07 06:57:40 -0700514 state.append(fpsState);
liyuqian6f163d22016-06-13 12:26:45 -0700515 state.append(splitScreenState);
liyuqiane5a6cd92016-05-27 08:52:52 -0700516
517 fWindow->setUIState(state);
518}
519
520void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
liyuqian6cb70252016-06-02 12:16:25 -0700521 // For those who will add more features to handle the state change in this function:
522 // After the change, please call updateUIState no notify the frontend (e.g., Android app).
523 // For example, after slide change, updateUIState is called inside setupCurrentSlide;
524 // after backend change, updateUIState is called in this function.
liyuqiane5a6cd92016-05-27 08:52:52 -0700525 if (stateName.equals(kSlideStateName)) {
526 int previousSlide = fCurrentSlide;
527 fCurrentSlide = 0;
528 for(auto slide : fSlides) {
529 if (slide->getName().equals(stateValue)) {
530 setupCurrentSlide(previousSlide);
531 break;
532 }
533 fCurrentSlide++;
534 }
535 if (fCurrentSlide >= fSlides.count()) {
536 fCurrentSlide = previousSlide;
537 SkDebugf("Slide not found: %s", stateValue.c_str());
538 }
liyuqian6cb70252016-06-02 12:16:25 -0700539 } else if (stateName.equals(kBackendStateName)) {
540 for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
541 if (stateValue.equals(kBackendTypeStrings[i])) {
542 if (fBackendType != i) {
543 fBackendType = (sk_app::Window::BackendType)i;
544 fWindow->detach();
545 fWindow->attach(fBackendType, DisplayParams());
546 fWindow->inval();
547 updateTitle();
548 updateUIState();
549 }
550 break;
551 }
552 }
liyuqianb73c24b2016-06-03 08:47:23 -0700553 } else if (stateName.equals(kSoftkeyStateName)) {
554 if (!stateValue.equals(kSoftkeyHint)) {
555 fCommands.onSoftkey(stateValue);
556 updateUIState(); // This is still needed to reset the value to kSoftkeyHint
557 }
liyuqian6f163d22016-06-13 12:26:45 -0700558 } else if (stateName.equals(kSplitScreenStateName)) {
559 bool newSplitScreen = stateValue.equals(kON);
560 if (newSplitScreen != fSplitScreen) {
561 fSplitScreen = newSplitScreen;
562 fWindow->inval();
563 updateUIState();
564 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700565 } else {
566 SkDebugf("Unknown stateName: %s", stateName.c_str());
567 }
568}