blob: 1d5e033dc9ba6a5b36670f51cc097c5021949a0e [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"
Greg Daniel9fcc7432016-11-29 16:35:19 -050012#include "Resources.h"
jvanverthc7027ab2016-06-16 09:52:35 -070013#include "SampleSlide.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070014#include "SKPSlide.h"
jvanverth9f372462016-04-06 06:08:59 -070015
Greg Daniel285db442016-10-14 09:12:53 -040016#include "SkATrace.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070017#include "SkCanvas.h"
Brian Osman2dd96932016-10-18 15:33:53 -040018#include "SkCommandLineFlags.h"
liyuqian74959a12016-06-16 14:10:34 -070019#include "SkDashPathEffect.h"
Greg Daniel285db442016-10-14 09:12:53 -040020#include "SkGraphics.h"
Brian Osmanf750fbc2017-02-08 10:47:28 -050021#include "SkImagePriv.h"
liyuqian6f163d22016-06-13 12:26:45 -070022#include "SkMetaData.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070023#include "SkOSFile.h"
Ben Wagnerbf111d72016-11-07 18:05:29 -050024#include "SkOSPath.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070025#include "SkRandom.h"
26#include "SkStream.h"
liyuqian74959a12016-06-16 14:10:34 -070027#include "SkSurface.h"
Brian Osman79086b92017-02-10 13:36:16 -050028#include "SkSwizzle.h"
csmartdalton29d87152017-02-10 17:05:14 -050029#include "SkTaskGroup.h"
liyuqian2edb0f42016-07-06 14:11:32 -070030#include "SkTime.h"
jvanverth9f372462016-04-06 06:08:59 -070031
Brian Osman79086b92017-02-10 13:36:16 -050032#include "imgui.h"
33
jvanverth34524262016-05-04 13:49:13 -070034using namespace sk_app;
35
jvanverth9f372462016-04-06 06:08:59 -070036Application* Application::Create(int argc, char** argv, void* platformData) {
jvanverth34524262016-05-04 13:49:13 -070037 return new Viewer(argc, argv, platformData);
jvanverth9f372462016-04-06 06:08:59 -070038}
39
jvanverth9f372462016-04-06 06:08:59 -070040static void on_paint_handler(SkCanvas* canvas, void* userData) {
jvanverth34524262016-05-04 13:49:13 -070041 Viewer* vv = reinterpret_cast<Viewer*>(userData);
jvanverth9f372462016-04-06 06:08:59 -070042
43 return vv->onPaint(canvas);
44}
45
jvanverth814e38d2016-06-06 08:48:47 -070046static bool on_touch_handler(intptr_t owner, Window::InputState state, float x, float y, void* userData)
liyuqiand3cdbca2016-05-17 12:44:20 -070047{
48 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
49
50 return viewer->onTouch(owner, state, x, y);
51}
52
liyuqiane5a6cd92016-05-27 08:52:52 -070053static void on_ui_state_changed_handler(const SkString& stateName, const SkString& stateValue, void* userData) {
54 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
55
56 return viewer->onUIStateChanged(stateName, stateValue);
57}
58
Brian Osman79086b92017-02-10 13:36:16 -050059static bool on_mouse_handler(int x, int y, Window::InputState state, uint32_t modifiers,
60 void* userData) {
61 ImGuiIO& io = ImGui::GetIO();
62 io.MousePos.x = static_cast<float>(x);
63 io.MousePos.y = static_cast<float>(y);
64 if (Window::kDown_InputState == state) {
65 io.MouseDown[0] = true;
66 } else if (Window::kUp_InputState == state) {
67 io.MouseDown[0] = false;
68 }
69 return true;
70}
71
72static bool on_mouse_wheel_handler(float delta, uint32_t modifiers, void* userData) {
73 ImGuiIO& io = ImGui::GetIO();
74 io.MouseWheel += delta;
75 return true;
76}
77
78static bool on_key_handler(Window::Key key, Window::InputState state, uint32_t modifiers,
79 void* userData) {
80 ImGuiIO& io = ImGui::GetIO();
81 io.KeysDown[static_cast<int>(key)] = (Window::kDown_InputState == state);
82
83 if (io.WantCaptureKeyboard) {
84 return true;
85 } else {
86 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
87 return viewer->onKey(key, state, modifiers);
88 }
89}
90
91static bool on_char_handler(SkUnichar c, uint32_t modifiers, void* userData) {
92 ImGuiIO& io = ImGui::GetIO();
93 if (io.WantTextInput) {
94 if (c > 0 && c < 0x10000) {
95 io.AddInputCharacter(c);
96 }
97 return true;
98 } else {
99 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
100 return viewer->onChar(c, modifiers);
101 }
102}
103
Brian Osman2dd96932016-10-18 15:33:53 -0400104static DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
Brian Osman16adfa32016-10-18 14:42:44 -0400105
Brian Osman2dd96932016-10-18 15:33:53 -0400106static DEFINE_string2(match, m, nullptr,
jvanverth2bb3b6d2016-04-08 07:24:09 -0700107 "[~][^]substring[$] [...] of bench name to run.\n"
108 "Multiple matches may be separated by spaces.\n"
109 "~ causes a matching bench to always be skipped\n"
110 "^ requires the start of the bench to match\n"
111 "$ requires the end of the bench to match\n"
112 "^ and $ requires an exact match\n"
113 "If a bench does not match any list entry,\n"
114 "it is skipped unless some list entry starts with ~");
bsalomon6c471f72016-07-26 12:56:32 -0700115
116#ifdef SK_VULKAN
jvanverthb8794cc2016-07-27 14:29:18 -0700117# define BACKENDS_STR "\"sw\", \"gl\", and \"vk\""
bsalomon6c471f72016-07-26 12:56:32 -0700118#else
119# define BACKENDS_STR "\"sw\" and \"gl\""
120#endif
121
liyuqian71491dc2016-06-09 12:02:34 -0700122#ifdef SK_BUILD_FOR_ANDROID
Brian Osman2dd96932016-10-18 15:33:53 -0400123static DEFINE_string(skps, "/data/local/tmp/skia", "Directory to read skps from.");
124static DEFINE_string(jpgs, "/data/local/tmp/skia", "Directory to read jpgs from.");
liyuqian71491dc2016-06-09 12:02:34 -0700125#else
Brian Osman2dd96932016-10-18 15:33:53 -0400126static DEFINE_string(skps, "skps", "Directory to read skps from.");
127static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
liyuqian71491dc2016-06-09 12:02:34 -0700128#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -0700129
Brian Osman2dd96932016-10-18 15:33:53 -0400130static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR ".");
bsalomon6c471f72016-07-26 12:56:32 -0700131
Brian Osman2dd96932016-10-18 15:33:53 -0400132static DEFINE_bool(atrace, false, "Enable support for using ATrace. ATrace is only supported on Android.");
Greg Daniel285db442016-10-14 09:12:53 -0400133
jvanverthaf236b52016-05-20 06:01:06 -0700134const char *kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
135 " [OpenGL]",
jvanverth063ece72016-06-17 09:29:14 -0700136#ifdef SK_VULKAN
liyuqiand94ad582016-06-07 14:22:37 -0700137 " [Vulkan]",
jvanverth063ece72016-06-17 09:29:14 -0700138#endif
liyuqiand94ad582016-06-07 14:22:37 -0700139 " [Raster]"
jvanverthaf236b52016-05-20 06:01:06 -0700140};
141
bsalomon6c471f72016-07-26 12:56:32 -0700142static sk_app::Window::BackendType get_backend_type(const char* str) {
143#ifdef SK_VULKAN
144 if (0 == strcmp(str, "vk")) {
145 return sk_app::Window::kVulkan_BackendType;
146 } else
147#endif
148 if (0 == strcmp(str, "gl")) {
149 return sk_app::Window::kNativeGL_BackendType;
150 } else if (0 == strcmp(str, "sw")) {
151 return sk_app::Window::kRaster_BackendType;
152 } else {
153 SkDebugf("Unknown backend type, %s, defaulting to sw.", str);
154 return sk_app::Window::kRaster_BackendType;
155 }
156}
157
liyuqiane5a6cd92016-05-27 08:52:52 -0700158const char* kName = "name";
159const char* kValue = "value";
160const char* kOptions = "options";
161const char* kSlideStateName = "Slide";
162const char* kBackendStateName = "Backend";
liyuqianb73c24b2016-06-03 08:47:23 -0700163const char* kSoftkeyStateName = "Softkey";
164const char* kSoftkeyHint = "Please select a softkey";
liyuqian1f508fd2016-06-07 06:57:40 -0700165const char* kFpsStateName = "FPS";
liyuqian6f163d22016-06-13 12:26:45 -0700166const char* kON = "ON";
167const char* kOFF = "OFF";
liyuqian2edb0f42016-07-06 14:11:32 -0700168const char* kRefreshStateName = "Refresh";
liyuqiane5a6cd92016-05-27 08:52:52 -0700169
jvanverth34524262016-05-04 13:49:13 -0700170Viewer::Viewer(int argc, char** argv, void* platformData)
jvanverthc265a922016-04-08 12:51:45 -0700171 : fCurrentMeasurement(0)
172 , fDisplayStats(false)
liyuqian2edb0f42016-07-06 14:11:32 -0700173 , fRefresh(false)
Brian Osman79086b92017-02-10 13:36:16 -0500174 , fShowImGuiDebugWindow(false)
175 , fShowImGuiTestWindow(false)
Brian Osmanf6877092017-02-13 09:39:57 -0500176 , fShowZoomWindow(false)
177 , fLastImage(nullptr)
jvanverth063ece72016-06-17 09:29:14 -0700178 , fBackendType(sk_app::Window::kNativeGL_BackendType)
Brian Osmanf750fbc2017-02-08 10:47:28 -0500179 , fColorType(kN32_SkColorType)
180 , fColorSpace(nullptr)
egdaniel2a0bb0a2016-04-11 08:30:40 -0700181 , fZoomCenterX(0.0f)
182 , fZoomCenterY(0.0f)
183 , fZoomLevel(0.0f)
184 , fZoomScale(SK_Scalar1)
jvanverthc265a922016-04-08 12:51:45 -0700185{
csmartdalton29d87152017-02-10 17:05:14 -0500186 static SkTaskGroup::Enabler kTaskGroupEnabler;
Greg Daniel285db442016-10-14 09:12:53 -0400187 SkGraphics::Init();
Brian Osman1df161a2017-02-09 12:10:20 -0500188 memset(fPaintTimes, 0, sizeof(fPaintTimes));
189 memset(fFlushTimes, 0, sizeof(fFlushTimes));
190 memset(fAnimateTimes, 0, sizeof(fAnimateTimes));
jvanverth9f372462016-04-06 06:08:59 -0700191
jvanverth2bb3b6d2016-04-08 07:24:09 -0700192 SkDebugf("Command line arguments: ");
193 for (int i = 1; i < argc; ++i) {
194 SkDebugf("%s ", argv[i]);
195 }
196 SkDebugf("\n");
197
198 SkCommandLineFlags::Parse(argc, argv);
Greg Daniel9fcc7432016-11-29 16:35:19 -0500199#ifdef SK_BUILD_FOR_ANDROID
200 SetResourcePath("/data/local/tmp/skia");
201#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -0700202
Greg Daniel285db442016-10-14 09:12:53 -0400203 if (FLAGS_atrace) {
204 SkEventTracer::SetInstance(new SkATrace());
205 }
206
bsalomon6c471f72016-07-26 12:56:32 -0700207 fBackendType = get_backend_type(FLAGS_backend[0]);
jvanverth9f372462016-04-06 06:08:59 -0700208 fWindow = Window::CreateNativeWindow(platformData);
jvanverthaf236b52016-05-20 06:01:06 -0700209 fWindow->attach(fBackendType, DisplayParams());
jvanverth9f372462016-04-06 06:08:59 -0700210
211 // register callbacks
brianosman622c8d52016-05-10 06:50:49 -0700212 fCommands.attach(fWindow);
jvanverth9f372462016-04-06 06:08:59 -0700213 fWindow->registerPaintFunc(on_paint_handler, this);
liyuqiand3cdbca2016-05-17 12:44:20 -0700214 fWindow->registerTouchFunc(on_touch_handler, this);
liyuqiane5a6cd92016-05-27 08:52:52 -0700215 fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
Brian Osman79086b92017-02-10 13:36:16 -0500216 fWindow->registerMouseFunc(on_mouse_handler, this);
217 fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this);
218 fWindow->registerKeyFunc(on_key_handler, this);
219 fWindow->registerCharFunc(on_char_handler, this);
jvanverth9f372462016-04-06 06:08:59 -0700220
brianosman622c8d52016-05-10 06:50:49 -0700221 // add key-bindings
Brian Osman79086b92017-02-10 13:36:16 -0500222 fCommands.addCommand(' ', "GUI", "Toggle Debug GUI", [this]() {
223 this->fShowImGuiDebugWindow = !this->fShowImGuiDebugWindow;
224 fWindow->inval();
225 });
226 fCommands.addCommand('g', "GUI", "Toggle GUI Demo", [this]() {
227 this->fShowImGuiTestWindow = !this->fShowImGuiTestWindow;
228 fWindow->inval();
229 });
Brian Osmanf6877092017-02-13 09:39:57 -0500230 fCommands.addCommand('z', "GUI", "Toggle zoom window", [this]() {
231 this->fShowZoomWindow = !this->fShowZoomWindow;
232 fWindow->inval();
233 });
brianosman622c8d52016-05-10 06:50:49 -0700234 fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
235 this->fDisplayStats = !this->fDisplayStats;
236 fWindow->inval();
237 });
Brian Osmanf750fbc2017-02-08 10:47:28 -0500238 fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() {
239 if (!fColorSpace) {
240 // Legacy -> sRGB
241 this->setColorMode(kN32_SkColorType, SkColorSpace::MakeSRGB());
242 } else if (kN32_SkColorType == fColorType) {
243 // sRGB -> F16 sRGB
244 this->setColorMode(kRGBA_F16_SkColorType, SkColorSpace::MakeSRGBLinear());
245 } else {
246 // F16 sRGB -> Legacy
247 this->setColorMode(kN32_SkColorType, nullptr);
248 }
brianosman622c8d52016-05-10 06:50:49 -0700249 });
250 fCommands.addCommand(Window::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
251 int previousSlide = fCurrentSlide;
252 fCurrentSlide++;
253 if (fCurrentSlide >= fSlides.count()) {
254 fCurrentSlide = 0;
255 }
256 this->setupCurrentSlide(previousSlide);
257 });
258 fCommands.addCommand(Window::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
259 int previousSlide = fCurrentSlide;
260 fCurrentSlide--;
261 if (fCurrentSlide < 0) {
262 fCurrentSlide = fSlides.count() - 1;
263 }
264 this->setupCurrentSlide(previousSlide);
265 });
266 fCommands.addCommand(Window::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
267 this->changeZoomLevel(1.f / 32.f);
268 fWindow->inval();
269 });
270 fCommands.addCommand(Window::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
271 this->changeZoomLevel(-1.f / 32.f);
272 fWindow->inval();
273 });
jvanverthaf236b52016-05-20 06:01:06 -0700274 fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
Jim Van Verthd63c1022017-01-05 13:50:49 -0500275#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC)
jvanverthb8794cc2016-07-27 14:29:18 -0700276 if (sk_app::Window::kRaster_BackendType == fBackendType) {
277 fBackendType = sk_app::Window::kNativeGL_BackendType;
278#ifdef SK_VULKAN
279 } else if (sk_app::Window::kNativeGL_BackendType == fBackendType) {
280 fBackendType = sk_app::Window::kVulkan_BackendType;
281#endif
282 } else {
283 fBackendType = sk_app::Window::kRaster_BackendType;
284 }
Jim Van Verthd63c1022017-01-05 13:50:49 -0500285#elif defined(SK_BUILD_FOR_UNIX)
286 // Switching to and from Vulkan is problematic on Linux so disabled for now
287 if (sk_app::Window::kRaster_BackendType == fBackendType) {
288 fBackendType = sk_app::Window::kNativeGL_BackendType;
289 } else if (sk_app::Window::kNativeGL_BackendType == fBackendType) {
290 fBackendType = sk_app::Window::kRaster_BackendType;
291 }
292#endif
jvanverthaf236b52016-05-20 06:01:06 -0700293 fWindow->detach();
294
Jim Van Verthd63c1022017-01-05 13:50:49 -0500295#if defined(SK_BUILD_FOR_WIN) && defined(SK_VULKAN)
296 // Switching from OpenGL to Vulkan in the same window is problematic at this point on
297 // Windows, so we just delete the window and recreate it.
jvanverthaf236b52016-05-20 06:01:06 -0700298 if (sk_app::Window::kVulkan_BackendType == fBackendType) {
jvanverthb8794cc2016-07-27 14:29:18 -0700299 delete fWindow;
300 fWindow = Window::CreateNativeWindow(nullptr);
jvanverthaf236b52016-05-20 06:01:06 -0700301
jvanverthb8794cc2016-07-27 14:29:18 -0700302 // re-register callbacks
303 fCommands.attach(fWindow);
304 fWindow->registerPaintFunc(on_paint_handler, this);
305 fWindow->registerTouchFunc(on_touch_handler, this);
306 fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
Brian Osman79086b92017-02-10 13:36:16 -0500307 fWindow->registerMouseFunc(on_mouse_handler, this);
308 fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this);
309 fWindow->registerKeyFunc(on_key_handler, this);
310 fWindow->registerCharFunc(on_char_handler, this);
jvanverthb8794cc2016-07-27 14:29:18 -0700311 }
312#endif
jvanverthaf236b52016-05-20 06:01:06 -0700313 fWindow->attach(fBackendType, DisplayParams());
jvanverthb8794cc2016-07-27 14:29:18 -0700314
jvanverthaf236b52016-05-20 06:01:06 -0700315 this->updateTitle();
jvanverth85f758c2016-05-27 06:47:08 -0700316 fWindow->inval();
jvanverthb8794cc2016-07-27 14:29:18 -0700317 fWindow->show();
jvanverthaf236b52016-05-20 06:01:06 -0700318 });
brianosman622c8d52016-05-10 06:50:49 -0700319
jvanverth2bb3b6d2016-04-08 07:24:09 -0700320 // set up slides
321 this->initSlides();
322
djsollen12d62a72016-04-21 07:59:44 -0700323 fAnimTimer.run();
324
jvanverth2bb3b6d2016-04-08 07:24:09 -0700325 // set up first frame
jvanverth2bb3b6d2016-04-08 07:24:09 -0700326 fCurrentSlide = 0;
jvanverthc265a922016-04-08 12:51:45 -0700327 setupCurrentSlide(-1);
jvanverthc265a922016-04-08 12:51:45 -0700328
Brian Osman79086b92017-02-10 13:36:16 -0500329 // ImGui initialization:
330 ImGuiIO& io = ImGui::GetIO();
331 io.DisplaySize.x = static_cast<float>(fWindow->width());
332 io.DisplaySize.y = static_cast<float>(fWindow->height());
333
334 // Keymap...
335 io.KeyMap[ImGuiKey_Tab] = (int)Window::Key::kTab;
336 io.KeyMap[ImGuiKey_LeftArrow] = (int)Window::Key::kLeft;
337 io.KeyMap[ImGuiKey_RightArrow] = (int)Window::Key::kRight;
338 io.KeyMap[ImGuiKey_UpArrow] = (int)Window::Key::kUp;
339 io.KeyMap[ImGuiKey_DownArrow] = (int)Window::Key::kDown;
340 io.KeyMap[ImGuiKey_PageUp] = (int)Window::Key::kPageUp;
341 io.KeyMap[ImGuiKey_PageDown] = (int)Window::Key::kPageDown;
342 io.KeyMap[ImGuiKey_Home] = (int)Window::Key::kHome;
343 io.KeyMap[ImGuiKey_End] = (int)Window::Key::kEnd;
344 io.KeyMap[ImGuiKey_Delete] = (int)Window::Key::kDelete;
345 io.KeyMap[ImGuiKey_Backspace] = (int)Window::Key::kBack;
346 io.KeyMap[ImGuiKey_Enter] = (int)Window::Key::kOK;
347 io.KeyMap[ImGuiKey_Escape] = (int)Window::Key::kEscape;
348 io.KeyMap[ImGuiKey_A] = (int)Window::Key::kA;
349 io.KeyMap[ImGuiKey_C] = (int)Window::Key::kC;
350 io.KeyMap[ImGuiKey_V] = (int)Window::Key::kV;
351 io.KeyMap[ImGuiKey_X] = (int)Window::Key::kX;
352 io.KeyMap[ImGuiKey_Y] = (int)Window::Key::kY;
353 io.KeyMap[ImGuiKey_Z] = (int)Window::Key::kZ;
354
355 int w, h;
356 unsigned char* pixels;
357 io.Fonts->GetTexDataAsAlpha8(&pixels, &w, &h);
358 SkImageInfo info = SkImageInfo::MakeA8(w, h);
359 SkPixmap pmap(info, pixels, info.minRowBytes());
Brian Osmanf6877092017-02-13 09:39:57 -0500360 SkMatrix localMatrix = SkMatrix::MakeScale(1.0f / w, 1.0f / h);
361 auto fontImage = SkImage::MakeFromRaster(pmap, nullptr, nullptr);
362 auto fontShader = fontImage->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
363 &localMatrix);
364 fImGuiFontPaint.setShader(fontShader);
365 fImGuiFontPaint.setColor(SK_ColorWHITE);
366 fImGuiFontPaint.setFilterQuality(kLow_SkFilterQuality);
367 io.Fonts->TexID = &fImGuiFontPaint;
Brian Osman79086b92017-02-10 13:36:16 -0500368
jvanverth9f372462016-04-06 06:08:59 -0700369 fWindow->show();
370}
371
jvanverth34524262016-05-04 13:49:13 -0700372void Viewer::initSlides() {
liyuqian1f508fd2016-06-07 06:57:40 -0700373 fAllSlideNames = Json::Value(Json::arrayValue);
374
jvanverth2bb3b6d2016-04-08 07:24:09 -0700375 const skiagm::GMRegistry* gms(skiagm::GMRegistry::Head());
376 while (gms) {
Ben Wagner145dbcd2016-11-03 14:40:50 -0400377 std::unique_ptr<skiagm::GM> gm(gms->factory()(nullptr));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700378
379 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
380 sk_sp<Slide> slide(new GMSlide(gm.release()));
381 fSlides.push_back(slide);
382 }
383
384 gms = gms->next();
385 }
386
387 // reverse array
388 for (int i = 0; i < fSlides.count()/2; ++i) {
389 sk_sp<Slide> temp = fSlides[i];
390 fSlides[i] = fSlides[fSlides.count() - i - 1];
391 fSlides[fSlides.count() - i - 1] = temp;
392 }
393
jvanverthc7027ab2016-06-16 09:52:35 -0700394 // samples
395 const SkViewRegister* reg = SkViewRegister::Head();
396 while (reg) {
397 sk_sp<Slide> slide(new SampleSlide(reg->factory()));
brianosmane1d20072016-07-12 09:07:33 -0700398 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
399 fSlides.push_back(slide);
400 }
jvanverthc7027ab2016-06-16 09:52:35 -0700401 reg = reg->next();
402 }
403
jvanverth2bb3b6d2016-04-08 07:24:09 -0700404 // SKPs
405 for (int i = 0; i < FLAGS_skps.count(); i++) {
406 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
jvanverthc265a922016-04-08 12:51:45 -0700407 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, FLAGS_skps[i])) {
408 continue;
409 }
410
jvanverth2bb3b6d2016-04-08 07:24:09 -0700411 SkString path(FLAGS_skps[i]);
jvanverthc265a922016-04-08 12:51:45 -0700412 sk_sp<SKPSlide> slide(new SKPSlide(SkOSPath::Basename(path.c_str()), path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700413 if (slide) {
414 fSlides.push_back(slide);
415 }
416 } else {
417 SkOSFile::Iter it(FLAGS_skps[i], ".skp");
jvanverthc265a922016-04-08 12:51:45 -0700418 SkString skpName;
419 while (it.next(&skpName)) {
420 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, skpName.c_str())) {
421 continue;
422 }
423
424 SkString path = SkOSPath::Join(FLAGS_skps[i], skpName.c_str());
425 sk_sp<SKPSlide> slide(new SKPSlide(skpName, path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700426 if (slide) {
427 fSlides.push_back(slide);
428 }
429 }
430 }
431 }
liyuqian6f163d22016-06-13 12:26:45 -0700432
433 // JPGs
434 for (int i = 0; i < FLAGS_jpgs.count(); i++) {
435 SkOSFile::Iter it(FLAGS_jpgs[i], ".jpg");
436 SkString jpgName;
437 while (it.next(&jpgName)) {
brianosmane1d20072016-07-12 09:07:33 -0700438 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, jpgName.c_str())) {
439 continue;
440 }
441
liyuqian6f163d22016-06-13 12:26:45 -0700442 SkString path = SkOSPath::Join(FLAGS_jpgs[i], jpgName.c_str());
443 sk_sp<ImageSlide> slide(new ImageSlide(jpgName, path));
444 if (slide) {
445 fSlides.push_back(slide);
446 }
447 }
448 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700449}
450
451
jvanverth34524262016-05-04 13:49:13 -0700452Viewer::~Viewer() {
jvanverth9f372462016-04-06 06:08:59 -0700453 fWindow->detach();
454 delete fWindow;
455}
456
brianosman05de2162016-05-06 13:28:57 -0700457void Viewer::updateTitle() {
jvanverth34524262016-05-04 13:49:13 -0700458 SkString title("Viewer: ");
jvanverthc265a922016-04-08 12:51:45 -0700459 title.append(fSlides[fCurrentSlide]->getName());
brianosmanb109b8c2016-06-16 13:03:24 -0700460
Brian Osmanf750fbc2017-02-08 10:47:28 -0500461 title.appendf(" %s", sk_tool_utils::colortype_name(fColorType));
462
463 // TODO: Find a short string to describe the gamut of the color space?
464 if (fColorSpace) {
465 title.append(" ColorManaged");
brianosman05de2162016-05-06 13:28:57 -0700466 }
Brian Osmanf750fbc2017-02-08 10:47:28 -0500467
jvanverthaf236b52016-05-20 06:01:06 -0700468 title.append(kBackendTypeStrings[fBackendType]);
brianosman05de2162016-05-06 13:28:57 -0700469 fWindow->setTitle(title.c_str());
470}
471
472void Viewer::setupCurrentSlide(int previousSlide) {
liyuqiane5a6cd92016-05-27 08:52:52 -0700473 if (fCurrentSlide == previousSlide) {
474 return; // no change; do nothing
475 }
476
liyuqian6f163d22016-06-13 12:26:45 -0700477 // prepare dimensions for image slides
jvanverthc7027ab2016-06-16 09:52:35 -0700478 fSlides[fCurrentSlide]->load(SkIntToScalar(fWindow->width()), SkIntToScalar(fWindow->height()));
liyuqian6f163d22016-06-13 12:26:45 -0700479
liyuqiane46e4f02016-05-20 07:32:19 -0700480 fGesture.reset();
481 fDefaultMatrix.reset();
482 fDefaultMatrixInv.reset();
483
484 if (fWindow->supportsContentRect() && fWindow->scaleContentToFit()) {
485 const SkRect contentRect = fWindow->getContentRect();
486 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
487 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
488 if (contentRect.width() > 0 && contentRect.height() > 0) {
489 fDefaultMatrix.setRectToRect(slideBounds, contentRect, SkMatrix::kStart_ScaleToFit);
liyuqianbeb1c672016-05-20 11:41:01 -0700490 SkAssertResult(fDefaultMatrix.invert(&fDefaultMatrixInv));
liyuqiane46e4f02016-05-20 07:32:19 -0700491 }
492 }
493
494 if (fWindow->supportsContentRect()) {
495 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
496 SkRect windowRect = fWindow->getContentRect();
497 fDefaultMatrixInv.mapRect(&windowRect);
jvanverth1e305ba2016-06-01 09:39:15 -0700498 fGesture.setTransLimit(SkRect::MakeWH(SkIntToScalar(slideSize.width()),
499 SkIntToScalar(slideSize.height())),
500 windowRect);
liyuqiane46e4f02016-05-20 07:32:19 -0700501 }
502
brianosman05de2162016-05-06 13:28:57 -0700503 this->updateTitle();
liyuqiane5a6cd92016-05-27 08:52:52 -0700504 this->updateUIState();
jvanverthc265a922016-04-08 12:51:45 -0700505 if (previousSlide >= 0) {
506 fSlides[previousSlide]->unload();
507 }
jvanverthc265a922016-04-08 12:51:45 -0700508 fWindow->inval();
509}
510
511#define MAX_ZOOM_LEVEL 8
512#define MIN_ZOOM_LEVEL -8
513
jvanverth34524262016-05-04 13:49:13 -0700514void Viewer::changeZoomLevel(float delta) {
jvanverthc265a922016-04-08 12:51:45 -0700515 fZoomLevel += delta;
516 if (fZoomLevel > 0) {
517 fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
518 fZoomScale = fZoomLevel + SK_Scalar1;
519 } else if (fZoomLevel < 0) {
520 fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
521 fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
522 } else {
523 fZoomScale = SK_Scalar1;
524 }
jvanverthc265a922016-04-08 12:51:45 -0700525}
526
liyuqiand3cdbca2016-05-17 12:44:20 -0700527SkMatrix Viewer::computeMatrix() {
jvanverthc265a922016-04-08 12:51:45 -0700528 SkMatrix m;
529 m.reset();
530
531 if (fZoomLevel) {
532 SkPoint center;
533 //m = this->getLocalMatrix();//.invert(&m);
534 m.mapXY(fZoomCenterX, fZoomCenterY, &center);
535 SkScalar cx = center.fX;
536 SkScalar cy = center.fY;
537
538 m.setTranslate(-cx, -cy);
539 m.postScale(fZoomScale, fZoomScale);
540 m.postTranslate(cx, cy);
541 }
542
liyuqiand3cdbca2016-05-17 12:44:20 -0700543 m.preConcat(fGesture.localM());
544 m.preConcat(fGesture.globalM());
jvanverthc265a922016-04-08 12:51:45 -0700545
liyuqiand3cdbca2016-05-17 12:44:20 -0700546 return m;
jvanverthc265a922016-04-08 12:51:45 -0700547}
548
Brian Osmanf750fbc2017-02-08 10:47:28 -0500549void Viewer::setColorMode(SkColorType colorType, sk_sp<SkColorSpace> colorSpace) {
550 fColorType = colorType;
551 fColorSpace = std::move(colorSpace);
liyuqian6f163d22016-06-13 12:26:45 -0700552
Brian Osmanf750fbc2017-02-08 10:47:28 -0500553 // When we're in color managed mode, we tag our window surface as sRGB. If we've switched into
554 // or out of legacy mode, we need to update our window configuration.
555 DisplayParams params = fWindow->getDisplayParams();
556 if (SkToBool(fColorSpace) != SkToBool(params.fColorSpace)) {
557 params.fColorSpace = fColorSpace ? SkColorSpace::MakeSRGB() : nullptr;
558 fWindow->setDisplayParams(params);
559 }
560
561 this->updateTitle();
562 fWindow->inval();
563}
564
565void Viewer::drawSlide(SkCanvas* canvas) {
jvanverthc265a922016-04-08 12:51:45 -0700566 int count = canvas->save();
djsollen12d62a72016-04-21 07:59:44 -0700567 if (fWindow->supportsContentRect()) {
568 SkRect contentRect = fWindow->getContentRect();
569 canvas->clipRect(contentRect);
570 canvas->translate(contentRect.fLeft, contentRect.fTop);
571 }
572
Brian Osmanf750fbc2017-02-08 10:47:28 -0500573 // By default, we render directly into the window's surface/canvas
574 SkCanvas* slideCanvas = canvas;
Brian Osmanf6877092017-02-13 09:39:57 -0500575 fLastImage.reset();
jvanverth3d6ed3a2016-04-07 11:09:51 -0700576
Brian Osmanf6877092017-02-13 09:39:57 -0500577 // If we're in F16, or the gamut isn't sRGB, or we're zooming, we need to render offscreen
Brian Osmanf750fbc2017-02-08 10:47:28 -0500578 sk_sp<SkSurface> offscreenSurface = nullptr;
Brian Osmanf6877092017-02-13 09:39:57 -0500579 if (kRGBA_F16_SkColorType == fColorType || fShowZoomWindow ||
Brian Osman462334e2017-02-09 11:22:57 -0500580 (fColorSpace && fColorSpace != SkColorSpace::MakeSRGB())) {
Brian Osmanf750fbc2017-02-08 10:47:28 -0500581 SkImageInfo info = SkImageInfo::Make(fWindow->width(), fWindow->height(), fColorType,
582 kPremul_SkAlphaType, fColorSpace);
583 offscreenSurface = canvas->makeSurface(info);
584 slideCanvas = offscreenSurface->getCanvas();
585 }
586
587 slideCanvas->clear(SK_ColorWHITE);
588 slideCanvas->concat(fDefaultMatrix);
589 slideCanvas->concat(computeMatrix());
590
Brian Osman1df161a2017-02-09 12:10:20 -0500591 // Time the painting logic of the slide
592 double startTime = SkTime::GetMSecs();
Brian Osmanf750fbc2017-02-08 10:47:28 -0500593 fSlides[fCurrentSlide]->draw(slideCanvas);
Brian Osman1df161a2017-02-09 12:10:20 -0500594 fPaintTimes[fCurrentMeasurement] = SkTime::GetMSecs() - startTime;
595
596 // Force a flush so we can time that, too
597 startTime = SkTime::GetMSecs();
598 slideCanvas->flush();
599 fFlushTimes[fCurrentMeasurement] = SkTime::GetMSecs() - startTime;
Brian Osmanf750fbc2017-02-08 10:47:28 -0500600
601 // If we rendered offscreen, snap an image and push the results to the window's canvas
602 if (offscreenSurface) {
Brian Osmanf6877092017-02-13 09:39:57 -0500603 fLastImage = offscreenSurface->makeImageSnapshot();
Brian Osmanf750fbc2017-02-08 10:47:28 -0500604
605 // Tag the image with the sRGB gamut, so no further color space conversion happens
606 sk_sp<SkColorSpace> cs = (kRGBA_F16_SkColorType == fColorType)
607 ? SkColorSpace::MakeSRGBLinear() : SkColorSpace::MakeSRGB();
Brian Osmanf6877092017-02-13 09:39:57 -0500608 auto retaggedImage = SkImageMakeRasterCopyAndAssignColorSpace(fLastImage.get(), cs.get());
Brian Osmanf750fbc2017-02-08 10:47:28 -0500609 canvas->drawImage(retaggedImage, 0, 0);
liyuqian74959a12016-06-16 14:10:34 -0700610 }
611
jvanverthc265a922016-04-08 12:51:45 -0700612 canvas->restoreToCount(count);
liyuqian6f163d22016-06-13 12:26:45 -0700613}
614
615void Viewer::onPaint(SkCanvas* canvas) {
Brian Osman79086b92017-02-10 13:36:16 -0500616 // Update ImGui input
617 ImGuiIO& io = ImGui::GetIO();
618 io.DeltaTime = 1.0f / 60.0f;
619 io.DisplaySize.x = static_cast<float>(fWindow->width());
620 io.DisplaySize.y = static_cast<float>(fWindow->height());
621
622 io.KeyAlt = io.KeysDown[static_cast<int>(Window::Key::kOption)];
623 io.KeyCtrl = io.KeysDown[static_cast<int>(Window::Key::kCtrl)];
624 io.KeyShift = io.KeysDown[static_cast<int>(Window::Key::kShift)];
625
626 ImGui::NewFrame();
627
Brian Osmanf750fbc2017-02-08 10:47:28 -0500628 drawSlide(canvas);
jvanverthc265a922016-04-08 12:51:45 -0700629
Brian Osman1df161a2017-02-09 12:10:20 -0500630 // Advance our timing bookkeeping
631 fCurrentMeasurement = (fCurrentMeasurement + 1) & (kMeasurementCount - 1);
632 SkASSERT(fCurrentMeasurement < kMeasurementCount);
633
634 // Draw any overlays or UI that we don't want timed
jvanverthc265a922016-04-08 12:51:45 -0700635 if (fDisplayStats) {
636 drawStats(canvas);
637 }
brianosman622c8d52016-05-10 06:50:49 -0700638 fCommands.drawHelp(canvas);
liyuqian2edb0f42016-07-06 14:11:32 -0700639
Brian Osman79086b92017-02-10 13:36:16 -0500640 drawImGui(canvas);
641
Brian Osman1df161a2017-02-09 12:10:20 -0500642 // Update the FPS
643 updateUIState();
jvanverth3d6ed3a2016-04-07 11:09:51 -0700644}
645
jvanverth814e38d2016-06-06 08:48:47 -0700646bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) {
liyuqiand3cdbca2016-05-17 12:44:20 -0700647 void* castedOwner = reinterpret_cast<void*>(owner);
liyuqiane46e4f02016-05-20 07:32:19 -0700648 SkPoint touchPoint = fDefaultMatrixInv.mapXY(x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -0700649 switch (state) {
650 case Window::kUp_InputState: {
651 fGesture.touchEnd(castedOwner);
652 break;
653 }
654 case Window::kDown_InputState: {
liyuqiane46e4f02016-05-20 07:32:19 -0700655 fGesture.touchBegin(castedOwner, touchPoint.fX, touchPoint.fY);
liyuqiand3cdbca2016-05-17 12:44:20 -0700656 break;
657 }
658 case Window::kMove_InputState: {
liyuqiane46e4f02016-05-20 07:32:19 -0700659 fGesture.touchMoved(castedOwner, touchPoint.fX, touchPoint.fY);
liyuqiand3cdbca2016-05-17 12:44:20 -0700660 break;
661 }
662 }
663 fWindow->inval();
664 return true;
665}
666
jvanverth34524262016-05-04 13:49:13 -0700667void Viewer::drawStats(SkCanvas* canvas) {
jvanverth3d6ed3a2016-04-07 11:09:51 -0700668 static const float kPixelPerMS = 2.0f;
669 static const int kDisplayWidth = 130;
670 static const int kDisplayHeight = 100;
671 static const int kDisplayPadding = 10;
672 static const int kGraphPadding = 3;
673 static const SkScalar kBaseMS = 1000.f / 60.f; // ms/frame to hit 60 fps
674
675 SkISize canvasSize = canvas->getDeviceSize();
676 SkRect rect = SkRect::MakeXYWH(SkIntToScalar(canvasSize.fWidth-kDisplayWidth-kDisplayPadding),
677 SkIntToScalar(kDisplayPadding),
678 SkIntToScalar(kDisplayWidth), SkIntToScalar(kDisplayHeight));
679 SkPaint paint;
680 canvas->save();
681
djsollen12d62a72016-04-21 07:59:44 -0700682 if (fWindow->supportsContentRect()) {
683 SkRect contentRect = fWindow->getContentRect();
684 canvas->clipRect(contentRect);
685 canvas->translate(contentRect.fLeft, contentRect.fTop);
686 }
687
jvanverth3d6ed3a2016-04-07 11:09:51 -0700688 canvas->clipRect(rect);
689 paint.setColor(SK_ColorBLACK);
690 canvas->drawRect(rect, paint);
691 // draw the 16ms line
692 paint.setColor(SK_ColorLTGRAY);
693 canvas->drawLine(rect.fLeft, rect.fBottom - kBaseMS*kPixelPerMS,
694 rect.fRight, rect.fBottom - kBaseMS*kPixelPerMS, paint);
695 paint.setColor(SK_ColorRED);
696 paint.setStyle(SkPaint::kStroke_Style);
697 canvas->drawRect(rect, paint);
698
699 int x = SkScalarTruncToInt(rect.fLeft) + kGraphPadding;
700 const int xStep = 2;
jvanverth3d6ed3a2016-04-07 11:09:51 -0700701 int i = fCurrentMeasurement;
702 do {
Brian Osman1df161a2017-02-09 12:10:20 -0500703 // Round to nearest values
704 int animateHeight = (int)(fAnimateTimes[i] * kPixelPerMS + 0.5);
705 int paintHeight = (int)(fPaintTimes[i] * kPixelPerMS + 0.5);
706 int flushHeight = (int)(fFlushTimes[i] * kPixelPerMS + 0.5);
707 int startY = SkScalarTruncToInt(rect.fBottom);
708 int endY = startY - flushHeight;
709 paint.setColor(SK_ColorRED);
710 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
711 SkIntToScalar(x), SkIntToScalar(endY), paint);
712 startY = endY;
713 endY = startY - paintHeight;
714 paint.setColor(SK_ColorGREEN);
715 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
716 SkIntToScalar(x), SkIntToScalar(endY), paint);
717 startY = endY;
718 endY = startY - animateHeight;
719 paint.setColor(SK_ColorMAGENTA);
jvanverth3d6ed3a2016-04-07 11:09:51 -0700720 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
721 SkIntToScalar(x), SkIntToScalar(endY), paint);
722 i++;
723 i &= (kMeasurementCount - 1); // fast mod
724 x += xStep;
725 } while (i != fCurrentMeasurement);
jvanverth9f372462016-04-06 06:08:59 -0700726
727 canvas->restore();
728}
729
Brian Osman79086b92017-02-10 13:36:16 -0500730void Viewer::drawImGui(SkCanvas* canvas) {
731 // Support drawing the ImGui demo window. Superfluous, but gives a good idea of what's possible
732 if (fShowImGuiTestWindow) {
733 ImGui::ShowTestWindow(&fShowImGuiTestWindow);
734 }
735
736 if (fShowImGuiDebugWindow) {
737 if (ImGui::Begin("Debug", &fShowImGuiDebugWindow)) {
738 if (ImGui::CollapsingHeader("Slide")) {
739 static ImGuiTextFilter filter;
740 filter.Draw();
741 int previousSlide = fCurrentSlide;
742 fCurrentSlide = 0;
743 for (auto slide : fSlides) {
744 if (filter.PassFilter(slide->getName().c_str())) {
745 ImGui::BulletText("%s", slide->getName().c_str());
746 if (ImGui::IsItemClicked()) {
747 setupCurrentSlide(previousSlide);
748 break;
749 }
750 }
751 ++fCurrentSlide;
752 }
753 if (fCurrentSlide >= fSlides.count()) {
754 fCurrentSlide = previousSlide;
755 }
756 }
757 }
758
759 ImGui::End();
760 }
761
Brian Osmanf6877092017-02-13 09:39:57 -0500762 SkPaint zoomImagePaint;
763 if (fShowZoomWindow && fLastImage) {
764 if (ImGui::Begin("Zoom", &fShowZoomWindow, ImVec2(200, 200))) {
765 static int zoomFactor = 4;
766 ImGui::SliderInt("Scale", &zoomFactor, 1, 16);
767
768 zoomImagePaint.setShader(fLastImage->makeShader(SkShader::kClamp_TileMode,
769 SkShader::kClamp_TileMode));
770 zoomImagePaint.setColor(SK_ColorWHITE);
771
772 // Zoom by shrinking the corner UVs towards the mouse cursor
773 ImVec2 mousePos = ImGui::GetMousePos();
774 ImVec2 avail = ImGui::GetContentRegionAvail();
775
776 ImVec2 zoomHalfExtents = ImVec2((avail.x * 0.5f) / zoomFactor,
777 (avail.y * 0.5f) / zoomFactor);
778 ImGui::Image(&zoomImagePaint, avail,
779 ImVec2(mousePos.x - zoomHalfExtents.x, mousePos.y - zoomHalfExtents.y),
780 ImVec2(mousePos.x + zoomHalfExtents.x, mousePos.y + zoomHalfExtents.y));
781 }
782
783 ImGui::End();
784 }
785
Brian Osman79086b92017-02-10 13:36:16 -0500786 // This causes ImGui to rebuild vertex/index data based on all immediate-mode commands
787 // (widgets, etc...) that have been issued
788 ImGui::Render();
789
790 // Then we fetch the most recent data, and convert it so we can render with Skia
791 const ImDrawData* drawData = ImGui::GetDrawData();
792 SkTDArray<SkPoint> pos;
793 SkTDArray<SkPoint> uv;
794 SkTDArray<SkColor> color;
Brian Osman79086b92017-02-10 13:36:16 -0500795
796 for (int i = 0; i < drawData->CmdListsCount; ++i) {
797 const ImDrawList* drawList = drawData->CmdLists[i];
798
799 // De-interleave all vertex data (sigh), convert to Skia types
800 pos.rewind(); uv.rewind(); color.rewind();
801 for (int i = 0; i < drawList->VtxBuffer.size(); ++i) {
802 const ImDrawVert& vert = drawList->VtxBuffer[i];
803 pos.push(SkPoint::Make(vert.pos.x, vert.pos.y));
804 uv.push(SkPoint::Make(vert.uv.x, vert.uv.y));
805 color.push(vert.col);
806 }
807 // ImGui colors are RGBA
808 SkSwapRB(color.begin(), color.begin(), color.count());
809
810 int indexOffset = 0;
811
812 // Draw everything with canvas.drawVertices...
813 for (int j = 0; j < drawList->CmdBuffer.size(); ++j) {
814 const ImDrawCmd* drawCmd = &drawList->CmdBuffer[j];
815
816 // TODO: Find min/max index for each draw, so we know how many vertices (sigh)
817 if (drawCmd->UserCallback) {
818 drawCmd->UserCallback(drawList, drawCmd);
819 } else {
Brian Osmanf6877092017-02-13 09:39:57 -0500820 SkPaint* paint = static_cast<SkPaint*>(drawCmd->TextureId);
821 SkASSERT(paint);
822
Brian Osman79086b92017-02-10 13:36:16 -0500823 canvas->save();
824 canvas->clipRect(SkRect::MakeLTRB(drawCmd->ClipRect.x, drawCmd->ClipRect.y,
825 drawCmd->ClipRect.z, drawCmd->ClipRect.w));
826 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, drawList->VtxBuffer.size(),
827 pos.begin(), uv.begin(), color.begin(),
828 drawList->IdxBuffer.begin() + indexOffset, drawCmd->ElemCount,
Brian Osmanf6877092017-02-13 09:39:57 -0500829 *paint);
Brian Osman79086b92017-02-10 13:36:16 -0500830 indexOffset += drawCmd->ElemCount;
831 canvas->restore();
832 }
833 }
834 }
835}
836
liyuqian2edb0f42016-07-06 14:11:32 -0700837void Viewer::onIdle() {
Brian Osman1df161a2017-02-09 12:10:20 -0500838 double startTime = SkTime::GetMSecs();
jvanverthc265a922016-04-08 12:51:45 -0700839 fAnimTimer.updateTime();
Brian Osman1df161a2017-02-09 12:10:20 -0500840 bool animateWantsInval = fSlides[fCurrentSlide]->animate(fAnimTimer);
841 fAnimateTimes[fCurrentMeasurement] = SkTime::GetMSecs() - startTime;
842
Brian Osman79086b92017-02-10 13:36:16 -0500843 ImGuiIO& io = ImGui::GetIO();
844 if (animateWantsInval || fDisplayStats || fRefresh || io.MetricsActiveWindows) {
jvanverthc265a922016-04-08 12:51:45 -0700845 fWindow->inval();
846 }
jvanverth9f372462016-04-06 06:08:59 -0700847}
liyuqiane5a6cd92016-05-27 08:52:52 -0700848
849void Viewer::updateUIState() {
liyuqianb73c24b2016-06-03 08:47:23 -0700850 // Slide state
liyuqiane5a6cd92016-05-27 08:52:52 -0700851 Json::Value slideState(Json::objectValue);
852 slideState[kName] = kSlideStateName;
853 slideState[kValue] = fSlides[fCurrentSlide]->getName().c_str();
liyuqian1f508fd2016-06-07 06:57:40 -0700854 if (fAllSlideNames.size() == 0) {
855 for(auto slide : fSlides) {
856 fAllSlideNames.append(Json::Value(slide->getName().c_str()));
857 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700858 }
liyuqian1f508fd2016-06-07 06:57:40 -0700859 slideState[kOptions] = fAllSlideNames;
liyuqiane5a6cd92016-05-27 08:52:52 -0700860
liyuqianb73c24b2016-06-03 08:47:23 -0700861 // Backend state
liyuqiane5a6cd92016-05-27 08:52:52 -0700862 Json::Value backendState(Json::objectValue);
863 backendState[kName] = kBackendStateName;
liyuqian6cb70252016-06-02 12:16:25 -0700864 backendState[kValue] = kBackendTypeStrings[fBackendType];
liyuqiane5a6cd92016-05-27 08:52:52 -0700865 backendState[kOptions] = Json::Value(Json::arrayValue);
liyuqianb73c24b2016-06-03 08:47:23 -0700866 for (auto str : kBackendTypeStrings) {
liyuqian6cb70252016-06-02 12:16:25 -0700867 backendState[kOptions].append(Json::Value(str));
868 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700869
liyuqianb73c24b2016-06-03 08:47:23 -0700870 // Softkey state
871 Json::Value softkeyState(Json::objectValue);
872 softkeyState[kName] = kSoftkeyStateName;
873 softkeyState[kValue] = kSoftkeyHint;
874 softkeyState[kOptions] = Json::Value(Json::arrayValue);
875 softkeyState[kOptions].append(kSoftkeyHint);
876 for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
877 softkeyState[kOptions].append(Json::Value(softkey.c_str()));
878 }
879
liyuqian1f508fd2016-06-07 06:57:40 -0700880 // FPS state
881 Json::Value fpsState(Json::objectValue);
882 fpsState[kName] = kFpsStateName;
Brian Osman1df161a2017-02-09 12:10:20 -0500883 int idx = (fCurrentMeasurement + (kMeasurementCount - 1)) & (kMeasurementCount - 1);
884 fpsState[kValue] = SkStringPrintf("%8.3lf ms\n\nA %8.3lf\nP %8.3lf\nF%8.3lf",
885 fAnimateTimes[idx] + fPaintTimes[idx] + fFlushTimes[idx],
886 fAnimateTimes[idx],
887 fPaintTimes[idx],
888 fFlushTimes[idx]).c_str();
liyuqian1f508fd2016-06-07 06:57:40 -0700889 fpsState[kOptions] = Json::Value(Json::arrayValue);
890
liyuqiane5a6cd92016-05-27 08:52:52 -0700891 Json::Value state(Json::arrayValue);
892 state.append(slideState);
893 state.append(backendState);
liyuqianb73c24b2016-06-03 08:47:23 -0700894 state.append(softkeyState);
liyuqian1f508fd2016-06-07 06:57:40 -0700895 state.append(fpsState);
liyuqiane5a6cd92016-05-27 08:52:52 -0700896
897 fWindow->setUIState(state);
898}
899
900void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
liyuqian6cb70252016-06-02 12:16:25 -0700901 // For those who will add more features to handle the state change in this function:
902 // After the change, please call updateUIState no notify the frontend (e.g., Android app).
903 // For example, after slide change, updateUIState is called inside setupCurrentSlide;
904 // after backend change, updateUIState is called in this function.
liyuqiane5a6cd92016-05-27 08:52:52 -0700905 if (stateName.equals(kSlideStateName)) {
906 int previousSlide = fCurrentSlide;
907 fCurrentSlide = 0;
908 for(auto slide : fSlides) {
909 if (slide->getName().equals(stateValue)) {
910 setupCurrentSlide(previousSlide);
911 break;
912 }
913 fCurrentSlide++;
914 }
915 if (fCurrentSlide >= fSlides.count()) {
916 fCurrentSlide = previousSlide;
917 SkDebugf("Slide not found: %s", stateValue.c_str());
918 }
liyuqian6cb70252016-06-02 12:16:25 -0700919 } else if (stateName.equals(kBackendStateName)) {
920 for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
921 if (stateValue.equals(kBackendTypeStrings[i])) {
922 if (fBackendType != i) {
923 fBackendType = (sk_app::Window::BackendType)i;
924 fWindow->detach();
925 fWindow->attach(fBackendType, DisplayParams());
926 fWindow->inval();
927 updateTitle();
928 updateUIState();
929 }
930 break;
931 }
932 }
liyuqianb73c24b2016-06-03 08:47:23 -0700933 } else if (stateName.equals(kSoftkeyStateName)) {
934 if (!stateValue.equals(kSoftkeyHint)) {
935 fCommands.onSoftkey(stateValue);
936 updateUIState(); // This is still needed to reset the value to kSoftkeyHint
937 }
liyuqian2edb0f42016-07-06 14:11:32 -0700938 } else if (stateName.equals(kRefreshStateName)) {
939 // This state is actually NOT in the UI state.
940 // We use this to allow Android to quickly set bool fRefresh.
941 fRefresh = stateValue.equals(kON);
liyuqiane5a6cd92016-05-27 08:52:52 -0700942 } else {
943 SkDebugf("Unknown stateName: %s", stateName.c_str());
944 }
945}
Brian Osman79086b92017-02-10 13:36:16 -0500946
947bool Viewer::onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers) {
948 return fCommands.onKey(key, state, modifiers);
949}
950
951bool Viewer::onChar(SkUnichar c, uint32_t modifiers) {
952 return fCommands.onChar(c, modifiers);
953}