blob: 9aec01f3ecc327ba1c1b7b9de5b948ae13fc7110 [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"
jvanverthc7027ab2016-06-16 09:52:35 -070012#include "SampleSlide.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070013#include "SKPSlide.h"
jvanverth9f372462016-04-06 06:08:59 -070014
jvanverth2bb3b6d2016-04-08 07:24:09 -070015#include "SkCanvas.h"
16#include "SkCommonFlags.h"
liyuqian6f163d22016-06-13 12:26:45 -070017#include "SkMetaData.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070018#include "SkOSFile.h"
19#include "SkRandom.h"
20#include "SkStream.h"
jvanverth9f372462016-04-06 06:08:59 -070021
jvanverth34524262016-05-04 13:49:13 -070022using namespace sk_app;
23
jvanverth9f372462016-04-06 06:08:59 -070024Application* Application::Create(int argc, char** argv, void* platformData) {
jvanverth34524262016-05-04 13:49:13 -070025 return new Viewer(argc, argv, platformData);
jvanverth9f372462016-04-06 06:08:59 -070026}
27
jvanverth9f372462016-04-06 06:08:59 -070028static void on_paint_handler(SkCanvas* canvas, void* userData) {
jvanverth34524262016-05-04 13:49:13 -070029 Viewer* vv = reinterpret_cast<Viewer*>(userData);
jvanverth9f372462016-04-06 06:08:59 -070030
31 return vv->onPaint(canvas);
32}
33
jvanverth814e38d2016-06-06 08:48:47 -070034static bool on_touch_handler(intptr_t owner, Window::InputState state, float x, float y, void* userData)
liyuqiand3cdbca2016-05-17 12:44:20 -070035{
36 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
37
38 return viewer->onTouch(owner, state, x, y);
39}
40
liyuqiane5a6cd92016-05-27 08:52:52 -070041static void on_ui_state_changed_handler(const SkString& stateName, const SkString& stateValue, void* userData) {
42 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
43
44 return viewer->onUIStateChanged(stateName, stateValue);
45}
46
47DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
egdanielf533f112016-06-13 11:30:10 -070048DEFINE_string(key, "", "Space-separated key/value pairs to add to JSON identifying this builder.");
jvanverth2bb3b6d2016-04-08 07:24:09 -070049DEFINE_string2(match, m, nullptr,
50 "[~][^]substring[$] [...] of bench name to run.\n"
51 "Multiple matches may be separated by spaces.\n"
52 "~ causes a matching bench to always be skipped\n"
53 "^ requires the start of the bench to match\n"
54 "$ requires the end of the bench to match\n"
55 "^ and $ requires an exact match\n"
56 "If a bench does not match any list entry,\n"
57 "it is skipped unless some list entry starts with ~");
liyuqian71491dc2016-06-09 12:02:34 -070058#ifdef SK_BUILD_FOR_ANDROID
liyuqian6f163d22016-06-13 12:26:45 -070059DEFINE_string(skps, "/data/local/tmp/skia", "Directory to read skps from.");
60DEFINE_string(jpgs, "/data/local/tmp/skia", "Directory to read jpgs from.");
liyuqian71491dc2016-06-09 12:02:34 -070061DEFINE_bool(vulkan, false, "Run with Vulkan.");
62#else
liyuqian6f163d22016-06-13 12:26:45 -070063DEFINE_string(skps, "skps", "Directory to read skps from.");
64DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
jvanverth85f758c2016-05-27 06:47:08 -070065DEFINE_bool(vulkan, true, "Run with Vulkan.");
liyuqian71491dc2016-06-09 12:02:34 -070066#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -070067
jvanverthaf236b52016-05-20 06:01:06 -070068const char *kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
69 " [OpenGL]",
liyuqiand94ad582016-06-07 14:22:37 -070070 " [Vulkan]",
71 " [Raster]"
jvanverthaf236b52016-05-20 06:01:06 -070072};
73
liyuqiane5a6cd92016-05-27 08:52:52 -070074const char* kName = "name";
75const char* kValue = "value";
76const char* kOptions = "options";
77const char* kSlideStateName = "Slide";
78const char* kBackendStateName = "Backend";
liyuqianb73c24b2016-06-03 08:47:23 -070079const char* kSoftkeyStateName = "Softkey";
80const char* kSoftkeyHint = "Please select a softkey";
liyuqian1f508fd2016-06-07 06:57:40 -070081const char* kFpsStateName = "FPS";
liyuqian6f163d22016-06-13 12:26:45 -070082const char* kSplitScreenStateName = "Split screen";
83const char* kON = "ON";
84const char* kOFF = "OFF";
liyuqiane5a6cd92016-05-27 08:52:52 -070085
jvanverth34524262016-05-04 13:49:13 -070086Viewer::Viewer(int argc, char** argv, void* platformData)
jvanverthc265a922016-04-08 12:51:45 -070087 : fCurrentMeasurement(0)
88 , fDisplayStats(false)
liyuqian6f163d22016-06-13 12:26:45 -070089 , fSplitScreen(false)
egdaniel963632f2016-06-15 14:23:40 -070090 , fBackendType(sk_app::Window::kVulkan_BackendType)
egdaniel2a0bb0a2016-04-11 08:30:40 -070091 , fZoomCenterX(0.0f)
92 , fZoomCenterY(0.0f)
93 , fZoomLevel(0.0f)
94 , fZoomScale(SK_Scalar1)
jvanverthc265a922016-04-08 12:51:45 -070095{
jvanverth3d6ed3a2016-04-07 11:09:51 -070096 memset(fMeasurements, 0, sizeof(fMeasurements));
jvanverth9f372462016-04-06 06:08:59 -070097
jvanverth2bb3b6d2016-04-08 07:24:09 -070098 SkDebugf("Command line arguments: ");
99 for (int i = 1; i < argc; ++i) {
100 SkDebugf("%s ", argv[i]);
101 }
102 SkDebugf("\n");
103
104 SkCommandLineFlags::Parse(argc, argv);
105
jvanverth85f758c2016-05-27 06:47:08 -0700106 fBackendType = FLAGS_vulkan ? sk_app::Window::kVulkan_BackendType
107 : sk_app::Window::kNativeGL_BackendType;
egdaniel963632f2016-06-15 14:23:40 -0700108
jvanverth9f372462016-04-06 06:08:59 -0700109 fWindow = Window::CreateNativeWindow(platformData);
jvanverthaf236b52016-05-20 06:01:06 -0700110 fWindow->attach(fBackendType, DisplayParams());
jvanverth9f372462016-04-06 06:08:59 -0700111
112 // register callbacks
brianosman622c8d52016-05-10 06:50:49 -0700113 fCommands.attach(fWindow);
jvanverth9f372462016-04-06 06:08:59 -0700114 fWindow->registerPaintFunc(on_paint_handler, this);
liyuqiand3cdbca2016-05-17 12:44:20 -0700115 fWindow->registerTouchFunc(on_touch_handler, this);
liyuqiane5a6cd92016-05-27 08:52:52 -0700116 fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
jvanverth9f372462016-04-06 06:08:59 -0700117
brianosman622c8d52016-05-10 06:50:49 -0700118 // add key-bindings
119 fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
120 this->fDisplayStats = !this->fDisplayStats;
121 fWindow->inval();
122 });
123 fCommands.addCommand('c', "Modes", "Toggle sRGB color mode", [this]() {
124 DisplayParams params = fWindow->getDisplayParams();
brianosman6a61a872016-06-16 11:10:03 -0700125 params.fColorSpace = (nullptr == params.fColorSpace)
126 ? SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) : nullptr;
brianosman622c8d52016-05-10 06:50:49 -0700127 fWindow->setDisplayParams(params);
128 this->updateTitle();
129 fWindow->inval();
130 });
131 fCommands.addCommand(Window::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
132 int previousSlide = fCurrentSlide;
133 fCurrentSlide++;
134 if (fCurrentSlide >= fSlides.count()) {
135 fCurrentSlide = 0;
136 }
137 this->setupCurrentSlide(previousSlide);
138 });
139 fCommands.addCommand(Window::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
140 int previousSlide = fCurrentSlide;
141 fCurrentSlide--;
142 if (fCurrentSlide < 0) {
143 fCurrentSlide = fSlides.count() - 1;
144 }
145 this->setupCurrentSlide(previousSlide);
146 });
147 fCommands.addCommand(Window::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
148 this->changeZoomLevel(1.f / 32.f);
149 fWindow->inval();
150 });
151 fCommands.addCommand(Window::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
152 this->changeZoomLevel(-1.f / 32.f);
153 fWindow->inval();
154 });
jvanverth85f758c2016-05-27 06:47:08 -0700155#if 0 // this doesn't seem to work on any platform right now
jvanverthaf236b52016-05-20 06:01:06 -0700156#ifndef SK_BUILD_FOR_ANDROID
157 fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
158 fWindow->detach();
159
160 if (sk_app::Window::kVulkan_BackendType == fBackendType) {
161 fBackendType = sk_app::Window::kNativeGL_BackendType;
162 }
jvanverth85f758c2016-05-27 06:47:08 -0700163 // TODO: get Vulkan -> OpenGL working on Windows without swapchain creation failure
jvanverthaf236b52016-05-20 06:01:06 -0700164 //else if (sk_app::Window::kNativeGL_BackendType == fBackendType) {
165 // fBackendType = sk_app::Window::kVulkan_BackendType;
166 //}
167
168 fWindow->attach(fBackendType, DisplayParams());
169 this->updateTitle();
jvanverth85f758c2016-05-27 06:47:08 -0700170 fWindow->inval();
jvanverthaf236b52016-05-20 06:01:06 -0700171 });
172#endif
jvanverth85f758c2016-05-27 06:47:08 -0700173#endif
brianosman622c8d52016-05-10 06:50:49 -0700174
jvanverth2bb3b6d2016-04-08 07:24:09 -0700175 // set up slides
176 this->initSlides();
177
djsollen12d62a72016-04-21 07:59:44 -0700178 fAnimTimer.run();
179
jvanverth2bb3b6d2016-04-08 07:24:09 -0700180 // set up first frame
jvanverth2bb3b6d2016-04-08 07:24:09 -0700181 fCurrentSlide = 0;
jvanverthc265a922016-04-08 12:51:45 -0700182 setupCurrentSlide(-1);
jvanverthc265a922016-04-08 12:51:45 -0700183
jvanverth9f372462016-04-06 06:08:59 -0700184 fWindow->show();
185}
186
jvanverth34524262016-05-04 13:49:13 -0700187void Viewer::initSlides() {
liyuqian1f508fd2016-06-07 06:57:40 -0700188 fAllSlideNames = Json::Value(Json::arrayValue);
189
jvanverth2bb3b6d2016-04-08 07:24:09 -0700190 const skiagm::GMRegistry* gms(skiagm::GMRegistry::Head());
191 while (gms) {
192 SkAutoTDelete<skiagm::GM> gm(gms->factory()(nullptr));
193
194 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
195 sk_sp<Slide> slide(new GMSlide(gm.release()));
196 fSlides.push_back(slide);
197 }
198
199 gms = gms->next();
200 }
201
202 // reverse array
203 for (int i = 0; i < fSlides.count()/2; ++i) {
204 sk_sp<Slide> temp = fSlides[i];
205 fSlides[i] = fSlides[fSlides.count() - i - 1];
206 fSlides[fSlides.count() - i - 1] = temp;
207 }
208
jvanverthc7027ab2016-06-16 09:52:35 -0700209 // samples
210 const SkViewRegister* reg = SkViewRegister::Head();
211 while (reg) {
212 sk_sp<Slide> slide(new SampleSlide(reg->factory()));
213 fSlides.push_back(slide);
214 reg = reg->next();
215 }
216
jvanverth2bb3b6d2016-04-08 07:24:09 -0700217 // SKPs
218 for (int i = 0; i < FLAGS_skps.count(); i++) {
219 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
jvanverthc265a922016-04-08 12:51:45 -0700220 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, FLAGS_skps[i])) {
221 continue;
222 }
223
jvanverth2bb3b6d2016-04-08 07:24:09 -0700224 SkString path(FLAGS_skps[i]);
jvanverthc265a922016-04-08 12:51:45 -0700225 sk_sp<SKPSlide> slide(new SKPSlide(SkOSPath::Basename(path.c_str()), path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700226 if (slide) {
227 fSlides.push_back(slide);
228 }
229 } else {
230 SkOSFile::Iter it(FLAGS_skps[i], ".skp");
jvanverthc265a922016-04-08 12:51:45 -0700231 SkString skpName;
232 while (it.next(&skpName)) {
233 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, skpName.c_str())) {
234 continue;
235 }
236
237 SkString path = SkOSPath::Join(FLAGS_skps[i], skpName.c_str());
238 sk_sp<SKPSlide> slide(new SKPSlide(skpName, path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700239 if (slide) {
240 fSlides.push_back(slide);
241 }
242 }
243 }
244 }
liyuqian6f163d22016-06-13 12:26:45 -0700245
246 // JPGs
247 for (int i = 0; i < FLAGS_jpgs.count(); i++) {
248 SkOSFile::Iter it(FLAGS_jpgs[i], ".jpg");
249 SkString jpgName;
250 while (it.next(&jpgName)) {
251 SkString path = SkOSPath::Join(FLAGS_jpgs[i], jpgName.c_str());
252 sk_sp<ImageSlide> slide(new ImageSlide(jpgName, path));
253 if (slide) {
254 fSlides.push_back(slide);
255 }
256 }
257 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700258}
259
260
jvanverth34524262016-05-04 13:49:13 -0700261Viewer::~Viewer() {
jvanverth9f372462016-04-06 06:08:59 -0700262 fWindow->detach();
263 delete fWindow;
264}
265
brianosman05de2162016-05-06 13:28:57 -0700266void Viewer::updateTitle() {
jvanverth34524262016-05-04 13:49:13 -0700267 SkString title("Viewer: ");
jvanverthc265a922016-04-08 12:51:45 -0700268 title.append(fSlides[fCurrentSlide]->getName());
brianosman6a61a872016-06-16 11:10:03 -0700269
270 // TODO: For now, any color-space on the window means sRGB
271 if (fWindow->getDisplayParams().fColorSpace) {
brianosman05de2162016-05-06 13:28:57 -0700272 title.append(" sRGB");
273 }
jvanverthaf236b52016-05-20 06:01:06 -0700274 title.append(kBackendTypeStrings[fBackendType]);
brianosman05de2162016-05-06 13:28:57 -0700275 fWindow->setTitle(title.c_str());
276}
277
278void Viewer::setupCurrentSlide(int previousSlide) {
liyuqiane5a6cd92016-05-27 08:52:52 -0700279 if (fCurrentSlide == previousSlide) {
280 return; // no change; do nothing
281 }
282
liyuqian6f163d22016-06-13 12:26:45 -0700283 // prepare dimensions for image slides
jvanverthc7027ab2016-06-16 09:52:35 -0700284 fSlides[fCurrentSlide]->load(SkIntToScalar(fWindow->width()), SkIntToScalar(fWindow->height()));
liyuqian6f163d22016-06-13 12:26:45 -0700285
liyuqiane46e4f02016-05-20 07:32:19 -0700286 fGesture.reset();
287 fDefaultMatrix.reset();
288 fDefaultMatrixInv.reset();
289
290 if (fWindow->supportsContentRect() && fWindow->scaleContentToFit()) {
291 const SkRect contentRect = fWindow->getContentRect();
292 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
293 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
294 if (contentRect.width() > 0 && contentRect.height() > 0) {
295 fDefaultMatrix.setRectToRect(slideBounds, contentRect, SkMatrix::kStart_ScaleToFit);
liyuqianbeb1c672016-05-20 11:41:01 -0700296 SkAssertResult(fDefaultMatrix.invert(&fDefaultMatrixInv));
liyuqiane46e4f02016-05-20 07:32:19 -0700297 }
298 }
299
300 if (fWindow->supportsContentRect()) {
301 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
302 SkRect windowRect = fWindow->getContentRect();
303 fDefaultMatrixInv.mapRect(&windowRect);
jvanverth1e305ba2016-06-01 09:39:15 -0700304 fGesture.setTransLimit(SkRect::MakeWH(SkIntToScalar(slideSize.width()),
305 SkIntToScalar(slideSize.height())),
306 windowRect);
liyuqiane46e4f02016-05-20 07:32:19 -0700307 }
308
brianosman05de2162016-05-06 13:28:57 -0700309 this->updateTitle();
liyuqiane5a6cd92016-05-27 08:52:52 -0700310 this->updateUIState();
jvanverthc265a922016-04-08 12:51:45 -0700311 if (previousSlide >= 0) {
312 fSlides[previousSlide]->unload();
313 }
jvanverthc265a922016-04-08 12:51:45 -0700314 fWindow->inval();
315}
316
317#define MAX_ZOOM_LEVEL 8
318#define MIN_ZOOM_LEVEL -8
319
jvanverth34524262016-05-04 13:49:13 -0700320void Viewer::changeZoomLevel(float delta) {
jvanverthc265a922016-04-08 12:51:45 -0700321 fZoomLevel += delta;
322 if (fZoomLevel > 0) {
323 fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
324 fZoomScale = fZoomLevel + SK_Scalar1;
325 } else if (fZoomLevel < 0) {
326 fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
327 fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
328 } else {
329 fZoomScale = SK_Scalar1;
330 }
jvanverthc265a922016-04-08 12:51:45 -0700331}
332
liyuqiand3cdbca2016-05-17 12:44:20 -0700333SkMatrix Viewer::computeMatrix() {
jvanverthc265a922016-04-08 12:51:45 -0700334 SkMatrix m;
335 m.reset();
336
337 if (fZoomLevel) {
338 SkPoint center;
339 //m = this->getLocalMatrix();//.invert(&m);
340 m.mapXY(fZoomCenterX, fZoomCenterY, &center);
341 SkScalar cx = center.fX;
342 SkScalar cy = center.fY;
343
344 m.setTranslate(-cx, -cy);
345 m.postScale(fZoomScale, fZoomScale);
346 m.postTranslate(cx, cy);
347 }
348
liyuqiand3cdbca2016-05-17 12:44:20 -0700349 m.preConcat(fGesture.localM());
350 m.preConcat(fGesture.globalM());
jvanverthc265a922016-04-08 12:51:45 -0700351
liyuqiand3cdbca2016-05-17 12:44:20 -0700352 return m;
jvanverthc265a922016-04-08 12:51:45 -0700353}
354
liyuqian6f163d22016-06-13 12:26:45 -0700355void Viewer::drawSlide(SkCanvas* canvas, bool inSplitScreen) {
356 SkASSERT(!inSplitScreen || fWindow->supportsContentRect());
357
jvanverthc265a922016-04-08 12:51:45 -0700358 int count = canvas->save();
djsollen12d62a72016-04-21 07:59:44 -0700359
360 if (fWindow->supportsContentRect()) {
361 SkRect contentRect = fWindow->getContentRect();
liyuqian6f163d22016-06-13 12:26:45 -0700362 // If inSplitScreen, translate the image half screen to the right.
363 // Thus we have two copies of the image on each half of the screen.
liyuqian401cf482016-06-13 14:38:35 -0700364 contentRect.fLeft +=
365 inSplitScreen ? (contentRect.fRight - contentRect.fLeft) * 0.5f : 0.0f;
djsollen12d62a72016-04-21 07:59:44 -0700366 canvas->clipRect(contentRect);
367 canvas->translate(contentRect.fLeft, contentRect.fTop);
368 }
369
370 canvas->clear(SK_ColorWHITE);
liyuqiane46e4f02016-05-20 07:32:19 -0700371 canvas->concat(fDefaultMatrix);
liyuqiand3cdbca2016-05-17 12:44:20 -0700372 canvas->concat(computeMatrix());
jvanverth3d6ed3a2016-04-07 11:09:51 -0700373
liyuqian6f163d22016-06-13 12:26:45 -0700374 canvas->getMetaData().setBool(kImageColorXformMetaData, inSplitScreen);
jvanverthc265a922016-04-08 12:51:45 -0700375 fSlides[fCurrentSlide]->draw(canvas);
376 canvas->restoreToCount(count);
liyuqian6f163d22016-06-13 12:26:45 -0700377}
378
379void Viewer::onPaint(SkCanvas* canvas) {
380 drawSlide(canvas, false);
381 if (fSplitScreen && fWindow->supportsContentRect()) {
382 drawSlide(canvas, true);
383 }
jvanverthc265a922016-04-08 12:51:45 -0700384
385 if (fDisplayStats) {
386 drawStats(canvas);
387 }
brianosman622c8d52016-05-10 06:50:49 -0700388 fCommands.drawHelp(canvas);
jvanverth3d6ed3a2016-04-07 11:09:51 -0700389}
390
jvanverth814e38d2016-06-06 08:48:47 -0700391bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) {
liyuqiand3cdbca2016-05-17 12:44:20 -0700392 void* castedOwner = reinterpret_cast<void*>(owner);
liyuqiane46e4f02016-05-20 07:32:19 -0700393 SkPoint touchPoint = fDefaultMatrixInv.mapXY(x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -0700394 switch (state) {
395 case Window::kUp_InputState: {
396 fGesture.touchEnd(castedOwner);
397 break;
398 }
399 case Window::kDown_InputState: {
liyuqiane46e4f02016-05-20 07:32:19 -0700400 fGesture.touchBegin(castedOwner, touchPoint.fX, touchPoint.fY);
liyuqiand3cdbca2016-05-17 12:44:20 -0700401 break;
402 }
403 case Window::kMove_InputState: {
liyuqiane46e4f02016-05-20 07:32:19 -0700404 fGesture.touchMoved(castedOwner, touchPoint.fX, touchPoint.fY);
liyuqiand3cdbca2016-05-17 12:44:20 -0700405 break;
406 }
407 }
408 fWindow->inval();
409 return true;
410}
411
jvanverth34524262016-05-04 13:49:13 -0700412void Viewer::drawStats(SkCanvas* canvas) {
jvanverth3d6ed3a2016-04-07 11:09:51 -0700413 static const float kPixelPerMS = 2.0f;
414 static const int kDisplayWidth = 130;
415 static const int kDisplayHeight = 100;
416 static const int kDisplayPadding = 10;
417 static const int kGraphPadding = 3;
418 static const SkScalar kBaseMS = 1000.f / 60.f; // ms/frame to hit 60 fps
419
420 SkISize canvasSize = canvas->getDeviceSize();
421 SkRect rect = SkRect::MakeXYWH(SkIntToScalar(canvasSize.fWidth-kDisplayWidth-kDisplayPadding),
422 SkIntToScalar(kDisplayPadding),
423 SkIntToScalar(kDisplayWidth), SkIntToScalar(kDisplayHeight));
424 SkPaint paint;
425 canvas->save();
426
djsollen12d62a72016-04-21 07:59:44 -0700427 if (fWindow->supportsContentRect()) {
428 SkRect contentRect = fWindow->getContentRect();
429 canvas->clipRect(contentRect);
430 canvas->translate(contentRect.fLeft, contentRect.fTop);
431 }
432
jvanverth3d6ed3a2016-04-07 11:09:51 -0700433 canvas->clipRect(rect);
434 paint.setColor(SK_ColorBLACK);
435 canvas->drawRect(rect, paint);
436 // draw the 16ms line
437 paint.setColor(SK_ColorLTGRAY);
438 canvas->drawLine(rect.fLeft, rect.fBottom - kBaseMS*kPixelPerMS,
439 rect.fRight, rect.fBottom - kBaseMS*kPixelPerMS, paint);
440 paint.setColor(SK_ColorRED);
441 paint.setStyle(SkPaint::kStroke_Style);
442 canvas->drawRect(rect, paint);
443
444 int x = SkScalarTruncToInt(rect.fLeft) + kGraphPadding;
445 const int xStep = 2;
446 const int startY = SkScalarTruncToInt(rect.fBottom);
447 int i = fCurrentMeasurement;
448 do {
449 int endY = startY - (int)(fMeasurements[i] * kPixelPerMS + 0.5); // round to nearest value
450 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
451 SkIntToScalar(x), SkIntToScalar(endY), paint);
452 i++;
453 i &= (kMeasurementCount - 1); // fast mod
454 x += xStep;
455 } while (i != fCurrentMeasurement);
jvanverth9f372462016-04-06 06:08:59 -0700456
457 canvas->restore();
458}
459
jvanverth34524262016-05-04 13:49:13 -0700460void Viewer::onIdle(double ms) {
jvanverth3d6ed3a2016-04-07 11:09:51 -0700461 // Record measurements
462 fMeasurements[fCurrentMeasurement++] = ms;
463 fCurrentMeasurement &= (kMeasurementCount - 1); // fast mod
464 SkASSERT(fCurrentMeasurement < kMeasurementCount);
465
jvanverthc265a922016-04-08 12:51:45 -0700466 fAnimTimer.updateTime();
jvanverth9d5e47f2016-04-26 08:01:33 -0700467 if (fSlides[fCurrentSlide]->animate(fAnimTimer) || fDisplayStats) {
jvanverthc265a922016-04-08 12:51:45 -0700468 fWindow->inval();
liyuqian1f508fd2016-06-07 06:57:40 -0700469 updateUIState(); // Update the FPS
jvanverthc265a922016-04-08 12:51:45 -0700470 }
jvanverth9f372462016-04-06 06:08:59 -0700471}
liyuqiane5a6cd92016-05-27 08:52:52 -0700472
473void Viewer::updateUIState() {
liyuqianb73c24b2016-06-03 08:47:23 -0700474 // Slide state
liyuqiane5a6cd92016-05-27 08:52:52 -0700475 Json::Value slideState(Json::objectValue);
476 slideState[kName] = kSlideStateName;
477 slideState[kValue] = fSlides[fCurrentSlide]->getName().c_str();
liyuqian1f508fd2016-06-07 06:57:40 -0700478 if (fAllSlideNames.size() == 0) {
479 for(auto slide : fSlides) {
480 fAllSlideNames.append(Json::Value(slide->getName().c_str()));
481 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700482 }
liyuqian1f508fd2016-06-07 06:57:40 -0700483 slideState[kOptions] = fAllSlideNames;
liyuqiane5a6cd92016-05-27 08:52:52 -0700484
liyuqianb73c24b2016-06-03 08:47:23 -0700485 // Backend state
liyuqiane5a6cd92016-05-27 08:52:52 -0700486 Json::Value backendState(Json::objectValue);
487 backendState[kName] = kBackendStateName;
liyuqian6cb70252016-06-02 12:16:25 -0700488 backendState[kValue] = kBackendTypeStrings[fBackendType];
liyuqiane5a6cd92016-05-27 08:52:52 -0700489 backendState[kOptions] = Json::Value(Json::arrayValue);
liyuqianb73c24b2016-06-03 08:47:23 -0700490 for (auto str : kBackendTypeStrings) {
liyuqian6cb70252016-06-02 12:16:25 -0700491 backendState[kOptions].append(Json::Value(str));
492 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700493
liyuqianb73c24b2016-06-03 08:47:23 -0700494 // Softkey state
495 Json::Value softkeyState(Json::objectValue);
496 softkeyState[kName] = kSoftkeyStateName;
497 softkeyState[kValue] = kSoftkeyHint;
498 softkeyState[kOptions] = Json::Value(Json::arrayValue);
499 softkeyState[kOptions].append(kSoftkeyHint);
500 for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
501 softkeyState[kOptions].append(Json::Value(softkey.c_str()));
502 }
503
liyuqian1f508fd2016-06-07 06:57:40 -0700504 // FPS state
505 Json::Value fpsState(Json::objectValue);
506 fpsState[kName] = kFpsStateName;
507 double measurement = fMeasurements[
508 (fCurrentMeasurement + (kMeasurementCount-1)) % kMeasurementCount
509 ];
510 fpsState[kValue] = SkStringPrintf("%8.3lf ms", measurement).c_str();
511 fpsState[kOptions] = Json::Value(Json::arrayValue);
512
liyuqian6f163d22016-06-13 12:26:45 -0700513 // Split screen state
514 Json::Value splitScreenState(Json::objectValue);
515 splitScreenState[kName] = kSplitScreenStateName;
516 splitScreenState[kValue] = fSplitScreen ? kON : kOFF;
517 splitScreenState[kOptions] = Json::Value(Json::arrayValue);
518 splitScreenState[kOptions].append(kON);
519 splitScreenState[kOptions].append(kOFF);
520
liyuqiane5a6cd92016-05-27 08:52:52 -0700521 Json::Value state(Json::arrayValue);
522 state.append(slideState);
523 state.append(backendState);
liyuqianb73c24b2016-06-03 08:47:23 -0700524 state.append(softkeyState);
liyuqian1f508fd2016-06-07 06:57:40 -0700525 state.append(fpsState);
liyuqian6f163d22016-06-13 12:26:45 -0700526 state.append(splitScreenState);
liyuqiane5a6cd92016-05-27 08:52:52 -0700527
528 fWindow->setUIState(state);
529}
530
531void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
liyuqian6cb70252016-06-02 12:16:25 -0700532 // For those who will add more features to handle the state change in this function:
533 // After the change, please call updateUIState no notify the frontend (e.g., Android app).
534 // For example, after slide change, updateUIState is called inside setupCurrentSlide;
535 // after backend change, updateUIState is called in this function.
liyuqiane5a6cd92016-05-27 08:52:52 -0700536 if (stateName.equals(kSlideStateName)) {
537 int previousSlide = fCurrentSlide;
538 fCurrentSlide = 0;
539 for(auto slide : fSlides) {
540 if (slide->getName().equals(stateValue)) {
541 setupCurrentSlide(previousSlide);
542 break;
543 }
544 fCurrentSlide++;
545 }
546 if (fCurrentSlide >= fSlides.count()) {
547 fCurrentSlide = previousSlide;
548 SkDebugf("Slide not found: %s", stateValue.c_str());
549 }
liyuqian6cb70252016-06-02 12:16:25 -0700550 } else if (stateName.equals(kBackendStateName)) {
551 for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
552 if (stateValue.equals(kBackendTypeStrings[i])) {
553 if (fBackendType != i) {
554 fBackendType = (sk_app::Window::BackendType)i;
555 fWindow->detach();
556 fWindow->attach(fBackendType, DisplayParams());
557 fWindow->inval();
558 updateTitle();
559 updateUIState();
560 }
561 break;
562 }
563 }
liyuqianb73c24b2016-06-03 08:47:23 -0700564 } else if (stateName.equals(kSoftkeyStateName)) {
565 if (!stateValue.equals(kSoftkeyHint)) {
566 fCommands.onSoftkey(stateValue);
567 updateUIState(); // This is still needed to reset the value to kSoftkeyHint
568 }
liyuqian6f163d22016-06-13 12:26:45 -0700569 } else if (stateName.equals(kSplitScreenStateName)) {
570 bool newSplitScreen = stateValue.equals(kON);
571 if (newSplitScreen != fSplitScreen) {
572 fSplitScreen = newSplitScreen;
573 fWindow->inval();
574 updateUIState();
575 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700576 } else {
577 SkDebugf("Unknown stateName: %s", stateName.c_str());
578 }
579}