blob: 083916178467401cba17aa087cd48786f24800e5 [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
csmartdalton61cd31a2017-02-27 17:00:53 -070016#include "GrContext.h"
Greg Daniel285db442016-10-14 09:12:53 -040017#include "SkATrace.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070018#include "SkCanvas.h"
Brian Osmana109e392017-02-24 09:49:14 -050019#include "SkColorSpace_Base.h"
Brian Osmanfdab5762017-11-09 10:27:55 -050020#include "SkColorSpacePriv.h"
Brian Osmane0d4fba2017-03-15 10:24:55 -040021#include "SkColorSpaceXformCanvas.h"
Brian Osman2dd96932016-10-18 15:33:53 -040022#include "SkCommandLineFlags.h"
Brian Osmanf9810662017-08-30 10:02:10 -040023#include "SkCommonFlagsGpuThreads.h"
csmartdalton008b9d82017-02-22 12:00:42 -070024#include "SkCommonFlagsPathRenderer.h"
liyuqian74959a12016-06-16 14:10:34 -070025#include "SkDashPathEffect.h"
Brian Osman53136aa2017-07-20 15:43:35 -040026#include "SkEventTracingPriv.h"
Greg Daniel285db442016-10-14 09:12:53 -040027#include "SkGraphics.h"
Brian Osmanf750fbc2017-02-08 10:47:28 -050028#include "SkImagePriv.h"
liyuqian6f163d22016-06-13 12:26:45 -070029#include "SkMetaData.h"
csmartdalton61cd31a2017-02-27 17:00:53 -070030#include "SkOnce.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070031#include "SkOSFile.h"
Ben Wagnerbf111d72016-11-07 18:05:29 -050032#include "SkOSPath.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070033#include "SkRandom.h"
Yuqian Li399b3c22017-08-03 11:08:15 -040034#include "SkScan.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070035#include "SkStream.h"
liyuqian74959a12016-06-16 14:10:34 -070036#include "SkSurface.h"
Brian Osman79086b92017-02-10 13:36:16 -050037#include "SkSwizzle.h"
csmartdalton29d87152017-02-10 17:05:14 -050038#include "SkTaskGroup.h"
liyuqian2edb0f42016-07-06 14:11:32 -070039#include "SkTime.h"
Mike Reed887cdf12017-04-03 11:11:09 -040040#include "SkVertices.h"
jvanverth9f372462016-04-06 06:08:59 -070041
Brian Osman79086b92017-02-10 13:36:16 -050042#include "imgui.h"
43
Chris Dalton1a325d22017-07-14 15:17:41 -060044#include "ccpr/GrCoverageCountingPathRenderer.h"
45
csmartdalton578f0642017-02-24 16:04:47 -070046#include <stdlib.h>
csmartdalton61cd31a2017-02-27 17:00:53 -070047#include <map>
csmartdalton578f0642017-02-24 16:04:47 -070048
jvanverth34524262016-05-04 13:49:13 -070049using namespace sk_app;
50
csmartdalton61cd31a2017-02-27 17:00:53 -070051static std::map<GpuPathRenderers, std::string> gPathRendererNames;
52
jvanverth9f372462016-04-06 06:08:59 -070053Application* Application::Create(int argc, char** argv, void* platformData) {
jvanverth34524262016-05-04 13:49:13 -070054 return new Viewer(argc, argv, platformData);
jvanverth9f372462016-04-06 06:08:59 -070055}
56
Christopher Dalton443ec1b2017-02-24 13:22:53 -070057static void on_backend_created_func(void* userData) {
58 Viewer* vv = reinterpret_cast<Viewer*>(userData);
59
60 return vv->onBackendCreated();
61}
62
jvanverth9f372462016-04-06 06:08:59 -070063static void on_paint_handler(SkCanvas* canvas, void* userData) {
jvanverth34524262016-05-04 13:49:13 -070064 Viewer* vv = reinterpret_cast<Viewer*>(userData);
jvanverth9f372462016-04-06 06:08:59 -070065
66 return vv->onPaint(canvas);
67}
68
jvanverth814e38d2016-06-06 08:48:47 -070069static bool on_touch_handler(intptr_t owner, Window::InputState state, float x, float y, void* userData)
liyuqiand3cdbca2016-05-17 12:44:20 -070070{
71 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
72
73 return viewer->onTouch(owner, state, x, y);
74}
75
liyuqiane5a6cd92016-05-27 08:52:52 -070076static void on_ui_state_changed_handler(const SkString& stateName, const SkString& stateValue, void* userData) {
77 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
78
79 return viewer->onUIStateChanged(stateName, stateValue);
80}
81
Brian Osman79086b92017-02-10 13:36:16 -050082static bool on_mouse_handler(int x, int y, Window::InputState state, uint32_t modifiers,
83 void* userData) {
84 ImGuiIO& io = ImGui::GetIO();
85 io.MousePos.x = static_cast<float>(x);
86 io.MousePos.y = static_cast<float>(y);
87 if (Window::kDown_InputState == state) {
88 io.MouseDown[0] = true;
89 } else if (Window::kUp_InputState == state) {
90 io.MouseDown[0] = false;
91 }
Jim Van Verthe7705782017-05-04 14:00:59 -040092 if (io.WantCaptureMouse) {
93 return true;
94 } else {
95 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
96 return viewer->onMouse(x, y, state, modifiers);
97 }
Brian Osman79086b92017-02-10 13:36:16 -050098}
99
100static bool on_mouse_wheel_handler(float delta, uint32_t modifiers, void* userData) {
101 ImGuiIO& io = ImGui::GetIO();
102 io.MouseWheel += delta;
103 return true;
104}
105
106static bool on_key_handler(Window::Key key, Window::InputState state, uint32_t modifiers,
107 void* userData) {
108 ImGuiIO& io = ImGui::GetIO();
109 io.KeysDown[static_cast<int>(key)] = (Window::kDown_InputState == state);
110
111 if (io.WantCaptureKeyboard) {
112 return true;
113 } else {
114 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
115 return viewer->onKey(key, state, modifiers);
116 }
117}
118
119static bool on_char_handler(SkUnichar c, uint32_t modifiers, void* userData) {
120 ImGuiIO& io = ImGui::GetIO();
121 if (io.WantTextInput) {
122 if (c > 0 && c < 0x10000) {
123 io.AddInputCharacter(c);
124 }
125 return true;
126 } else {
127 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
128 return viewer->onChar(c, modifiers);
129 }
130}
131
Brian Osman2dd96932016-10-18 15:33:53 -0400132static DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
Brian Osman16adfa32016-10-18 14:42:44 -0400133
Brian Osman2dd96932016-10-18 15:33:53 -0400134static DEFINE_string2(match, m, nullptr,
jvanverth2bb3b6d2016-04-08 07:24:09 -0700135 "[~][^]substring[$] [...] of bench name to run.\n"
136 "Multiple matches may be separated by spaces.\n"
137 "~ causes a matching bench to always be skipped\n"
138 "^ requires the start of the bench to match\n"
139 "$ requires the end of the bench to match\n"
140 "^ and $ requires an exact match\n"
141 "If a bench does not match any list entry,\n"
142 "it is skipped unless some list entry starts with ~");
bsalomon6c471f72016-07-26 12:56:32 -0700143
Chris Dalton7a0ebfc2017-10-13 12:35:50 -0600144static DEFINE_string(slide, "", "Start on this sample.");
145static DEFINE_bool(list, false, "List samples?");
Jim Van Verth6f449692017-02-14 15:16:46 -0500146
bsalomon6c471f72016-07-26 12:56:32 -0700147#ifdef SK_VULKAN
jvanverthb8794cc2016-07-27 14:29:18 -0700148# define BACKENDS_STR "\"sw\", \"gl\", and \"vk\""
bsalomon6c471f72016-07-26 12:56:32 -0700149#else
150# define BACKENDS_STR "\"sw\" and \"gl\""
151#endif
152
liyuqian71491dc2016-06-09 12:02:34 -0700153#ifdef SK_BUILD_FOR_ANDROID
Brian Salomon96789b32017-05-26 12:06:21 -0400154static DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from.");
155static DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from.");
liyuqian71491dc2016-06-09 12:02:34 -0700156#else
Brian Osman2dd96932016-10-18 15:33:53 -0400157static DEFINE_string(skps, "skps", "Directory to read skps from.");
158static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
liyuqian71491dc2016-06-09 12:02:34 -0700159#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -0700160
Brian Osman2dd96932016-10-18 15:33:53 -0400161static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR ".");
bsalomon6c471f72016-07-26 12:56:32 -0700162
Chris Dalton7a0ebfc2017-10-13 12:35:50 -0600163static DEFINE_int32(msaa, 0, "Number of subpixel samples. 0 for no HW antialiasing.");
164static DEFINE_bool(cachePathMasks, true, "Allows path mask textures to be cached in GPU configs.");
csmartdalton008b9d82017-02-22 12:00:42 -0700165DEFINE_pathrenderer_flag;
166
Chris Dalton7a0ebfc2017-10-13 12:35:50 -0600167static DEFINE_bool(instancedRendering, false, "Enable instanced rendering on GPU backends.");
Brian Osman53136aa2017-07-20 15:43:35 -0400168DECLARE_int32(threads)
Brian Salomon41eac792017-03-08 14:03:56 -0500169
Brian Salomon194db172017-08-17 14:37:06 -0400170const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
csmartdalton578f0642017-02-24 16:04:47 -0700171 "OpenGL",
Brian Salomon194db172017-08-17 14:37:06 -0400172#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
173 "ANGLE",
174#endif
jvanverth063ece72016-06-17 09:29:14 -0700175#ifdef SK_VULKAN
csmartdalton578f0642017-02-24 16:04:47 -0700176 "Vulkan",
jvanverth063ece72016-06-17 09:29:14 -0700177#endif
csmartdalton578f0642017-02-24 16:04:47 -0700178 "Raster"
jvanverthaf236b52016-05-20 06:01:06 -0700179};
180
bsalomon6c471f72016-07-26 12:56:32 -0700181static sk_app::Window::BackendType get_backend_type(const char* str) {
182#ifdef SK_VULKAN
183 if (0 == strcmp(str, "vk")) {
184 return sk_app::Window::kVulkan_BackendType;
185 } else
186#endif
Brian Salomon194db172017-08-17 14:37:06 -0400187#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
188 if (0 == strcmp(str, "angle")) {
189 return sk_app::Window::kANGLE_BackendType;
190 } else
191#endif
bsalomon6c471f72016-07-26 12:56:32 -0700192 if (0 == strcmp(str, "gl")) {
193 return sk_app::Window::kNativeGL_BackendType;
194 } else if (0 == strcmp(str, "sw")) {
195 return sk_app::Window::kRaster_BackendType;
196 } else {
197 SkDebugf("Unknown backend type, %s, defaulting to sw.", str);
198 return sk_app::Window::kRaster_BackendType;
199 }
200}
201
Brian Osmana109e392017-02-24 09:49:14 -0500202static SkColorSpacePrimaries gSrgbPrimaries = {
203 0.64f, 0.33f,
204 0.30f, 0.60f,
205 0.15f, 0.06f,
206 0.3127f, 0.3290f };
207
208static SkColorSpacePrimaries gAdobePrimaries = {
209 0.64f, 0.33f,
210 0.21f, 0.71f,
211 0.15f, 0.06f,
212 0.3127f, 0.3290f };
213
214static SkColorSpacePrimaries gP3Primaries = {
215 0.680f, 0.320f,
216 0.265f, 0.690f,
217 0.150f, 0.060f,
218 0.3127f, 0.3290f };
219
220static SkColorSpacePrimaries gRec2020Primaries = {
221 0.708f, 0.292f,
222 0.170f, 0.797f,
223 0.131f, 0.046f,
224 0.3127f, 0.3290f };
225
226struct NamedPrimaries {
227 const char* fName;
228 SkColorSpacePrimaries* fPrimaries;
229} gNamedPrimaries[] = {
230 { "sRGB", &gSrgbPrimaries },
231 { "AdobeRGB", &gAdobePrimaries },
232 { "P3", &gP3Primaries },
233 { "Rec. 2020", &gRec2020Primaries },
234};
235
236static bool primaries_equal(const SkColorSpacePrimaries& a, const SkColorSpacePrimaries& b) {
237 return memcmp(&a, &b, sizeof(SkColorSpacePrimaries)) == 0;
238}
239
Brian Osman70d2f432017-11-08 09:54:10 -0500240static Window::BackendType backend_type_for_window(Window::BackendType backendType) {
241 // In raster mode, we still use GL for the window.
242 // This lets us render the GUI faster (and correct).
243 return Window::kRaster_BackendType == backendType ? Window::kNativeGL_BackendType : backendType;
244}
245
liyuqiane5a6cd92016-05-27 08:52:52 -0700246const char* kName = "name";
247const char* kValue = "value";
248const char* kOptions = "options";
249const char* kSlideStateName = "Slide";
250const char* kBackendStateName = "Backend";
csmartdalton578f0642017-02-24 16:04:47 -0700251const char* kMSAAStateName = "MSAA";
csmartdalton61cd31a2017-02-27 17:00:53 -0700252const char* kPathRendererStateName = "Path renderer";
Brian Salomon99a33902017-03-07 15:16:34 -0500253const char* kInstancedRenderingStateName = "Instanced rendering";
liyuqianb73c24b2016-06-03 08:47:23 -0700254const char* kSoftkeyStateName = "Softkey";
255const char* kSoftkeyHint = "Please select a softkey";
liyuqian1f508fd2016-06-07 06:57:40 -0700256const char* kFpsStateName = "FPS";
liyuqian6f163d22016-06-13 12:26:45 -0700257const char* kON = "ON";
258const char* kOFF = "OFF";
liyuqian2edb0f42016-07-06 14:11:32 -0700259const char* kRefreshStateName = "Refresh";
liyuqiane5a6cd92016-05-27 08:52:52 -0700260
jvanverth34524262016-05-04 13:49:13 -0700261Viewer::Viewer(int argc, char** argv, void* platformData)
jvanverthc265a922016-04-08 12:51:45 -0700262 : fCurrentMeasurement(0)
Jim Van Verth90dcce52017-11-03 13:36:07 -0400263 , fCumulativeMeasurementTime(0)
264 , fCumulativeMeasurementCount(0)
jvanverthc265a922016-04-08 12:51:45 -0700265 , fDisplayStats(false)
liyuqian2edb0f42016-07-06 14:11:32 -0700266 , fRefresh(false)
Brian Osman79086b92017-02-10 13:36:16 -0500267 , fShowImGuiDebugWindow(false)
Brian Osmanfce09c52017-11-14 15:32:20 -0500268 , fShowSlidePicker(false)
Brian Osman79086b92017-02-10 13:36:16 -0500269 , fShowImGuiTestWindow(false)
Brian Osmanf6877092017-02-13 09:39:57 -0500270 , fShowZoomWindow(false)
271 , fLastImage(nullptr)
jvanverth063ece72016-06-17 09:29:14 -0700272 , fBackendType(sk_app::Window::kNativeGL_BackendType)
Brian Osman92004802017-03-06 11:47:26 -0500273 , fColorMode(ColorMode::kLegacy)
Brian Osmana109e392017-02-24 09:49:14 -0500274 , fColorSpacePrimaries(gSrgbPrimaries)
Brian Osmanfdab5762017-11-09 10:27:55 -0500275 // Our UI can only tweak gamma (currently), so start out gamma-only
276 , fColorSpaceTransferFn(g2Dot2_TransferFn)
egdaniel2a0bb0a2016-04-11 08:30:40 -0700277 , fZoomLevel(0.0f)
Brian Osmanb53f48c2017-06-07 10:00:30 -0400278 , fGestureDevice(GestureDevice::kNone)
jvanverthc265a922016-04-08 12:51:45 -0700279{
Greg Daniel285db442016-10-14 09:12:53 -0400280 SkGraphics::Init();
csmartdalton61cd31a2017-02-27 17:00:53 -0700281
282 static SkOnce initPathRendererNames;
283 initPathRendererNames([]() {
Brian Osman8b0f2652017-08-29 15:18:34 -0400284 gPathRendererNames[GpuPathRenderers::kAll] = "All Path Renderers";
285 gPathRendererNames[GpuPathRenderers::kDefault] =
286 "Default Ganesh Behavior (best path renderer, not including CCPR)";
csmartdalton61cd31a2017-02-27 17:00:53 -0700287 gPathRendererNames[GpuPathRenderers::kStencilAndCover] = "NV_path_rendering";
288 gPathRendererNames[GpuPathRenderers::kMSAA] = "Sample shading";
Jim Van Verth83010462017-03-16 08:45:39 -0400289 gPathRendererNames[GpuPathRenderers::kSmall] = "Small paths (cached sdf or alpha masks)";
Chris Dalton1a325d22017-07-14 15:17:41 -0600290 gPathRendererNames[GpuPathRenderers::kCoverageCounting] = "Coverage counting";
Brian Osman8a9de3d2017-03-01 14:59:05 -0500291 gPathRendererNames[GpuPathRenderers::kTessellating] = "Tessellating";
csmartdalton61cd31a2017-02-27 17:00:53 -0700292 gPathRendererNames[GpuPathRenderers::kNone] = "Software masks";
293 });
294
Brian Osman1df161a2017-02-09 12:10:20 -0500295 memset(fPaintTimes, 0, sizeof(fPaintTimes));
296 memset(fFlushTimes, 0, sizeof(fFlushTimes));
297 memset(fAnimateTimes, 0, sizeof(fAnimateTimes));
jvanverth9f372462016-04-06 06:08:59 -0700298
jvanverth2bb3b6d2016-04-08 07:24:09 -0700299 SkDebugf("Command line arguments: ");
300 for (int i = 1; i < argc; ++i) {
301 SkDebugf("%s ", argv[i]);
302 }
303 SkDebugf("\n");
304
305 SkCommandLineFlags::Parse(argc, argv);
Greg Daniel9fcc7432016-11-29 16:35:19 -0500306#ifdef SK_BUILD_FOR_ANDROID
Brian Salomon96789b32017-05-26 12:06:21 -0400307 SetResourcePath("/data/local/tmp/resources");
Greg Daniel9fcc7432016-11-29 16:35:19 -0500308#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -0700309
Brian Osmanbc8150f2017-07-24 11:38:01 -0400310 initializeEventTracingForTools();
Brian Osman53136aa2017-07-20 15:43:35 -0400311 static SkTaskGroup::Enabler kTaskGroupEnabler(FLAGS_threads);
Greg Daniel285db442016-10-14 09:12:53 -0400312
bsalomon6c471f72016-07-26 12:56:32 -0700313 fBackendType = get_backend_type(FLAGS_backend[0]);
jvanverth9f372462016-04-06 06:08:59 -0700314 fWindow = Window::CreateNativeWindow(platformData);
jvanverth9f372462016-04-06 06:08:59 -0700315
csmartdalton578f0642017-02-24 16:04:47 -0700316 DisplayParams displayParams;
317 displayParams.fMSAASampleCount = FLAGS_msaa;
Brian Salomon41eac792017-03-08 14:03:56 -0500318 displayParams.fGrContextOptions.fEnableInstancedRendering = FLAGS_instancedRendering;
csmartdalton61cd31a2017-02-27 17:00:53 -0700319 displayParams.fGrContextOptions.fGpuPathRenderers = CollectGpuPathRenderersFromFlags();
Chris Dalton7a0ebfc2017-10-13 12:35:50 -0600320 displayParams.fGrContextOptions.fAllowPathMaskCaching = FLAGS_cachePathMasks;
Brian Osmanf9810662017-08-30 10:02:10 -0400321 displayParams.fGrContextOptions.fExecutor = GpuExecutorForTools();
csmartdalton578f0642017-02-24 16:04:47 -0700322 fWindow->setRequestedDisplayParams(displayParams);
323
jvanverth9f372462016-04-06 06:08:59 -0700324 // register callbacks
brianosman622c8d52016-05-10 06:50:49 -0700325 fCommands.attach(fWindow);
Christopher Dalton443ec1b2017-02-24 13:22:53 -0700326 fWindow->registerBackendCreatedFunc(on_backend_created_func, this);
jvanverth9f372462016-04-06 06:08:59 -0700327 fWindow->registerPaintFunc(on_paint_handler, this);
liyuqiand3cdbca2016-05-17 12:44:20 -0700328 fWindow->registerTouchFunc(on_touch_handler, this);
liyuqiane5a6cd92016-05-27 08:52:52 -0700329 fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
Brian Osman79086b92017-02-10 13:36:16 -0500330 fWindow->registerMouseFunc(on_mouse_handler, this);
331 fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this);
332 fWindow->registerKeyFunc(on_key_handler, this);
333 fWindow->registerCharFunc(on_char_handler, this);
jvanverth9f372462016-04-06 06:08:59 -0700334
brianosman622c8d52016-05-10 06:50:49 -0700335 // add key-bindings
Brian Osman79086b92017-02-10 13:36:16 -0500336 fCommands.addCommand(' ', "GUI", "Toggle Debug GUI", [this]() {
337 this->fShowImGuiDebugWindow = !this->fShowImGuiDebugWindow;
338 fWindow->inval();
339 });
Brian Osmanfce09c52017-11-14 15:32:20 -0500340 // Command to jump directly to the slide picker and give it focus
341 fCommands.addCommand('/', "GUI", "Jump to slide picker", [this]() {
342 this->fShowImGuiDebugWindow = true;
343 this->fShowSlidePicker = true;
344 fWindow->inval();
345 });
346 // Alias that to Backspace, to match SampleApp
347 fCommands.addCommand(Window::Key::kBack, "Backspace", "GUI", "Jump to slide picker", [this]() {
348 this->fShowImGuiDebugWindow = true;
349 this->fShowSlidePicker = true;
350 fWindow->inval();
351 });
Brian Osman79086b92017-02-10 13:36:16 -0500352 fCommands.addCommand('g', "GUI", "Toggle GUI Demo", [this]() {
353 this->fShowImGuiTestWindow = !this->fShowImGuiTestWindow;
354 fWindow->inval();
355 });
Brian Osmanf6877092017-02-13 09:39:57 -0500356 fCommands.addCommand('z', "GUI", "Toggle zoom window", [this]() {
357 this->fShowZoomWindow = !this->fShowZoomWindow;
358 fWindow->inval();
359 });
brianosman622c8d52016-05-10 06:50:49 -0700360 fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
361 this->fDisplayStats = !this->fDisplayStats;
362 fWindow->inval();
363 });
Jim Van Verth90dcce52017-11-03 13:36:07 -0400364 fCommands.addCommand('0', "Overlays", "Reset stats", [this]() {
365 this->resetMeasurements();
366 this->updateTitle();
367 fWindow->inval();
368 });
Brian Osmanf750fbc2017-02-08 10:47:28 -0500369 fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() {
Brian Osman92004802017-03-06 11:47:26 -0500370 switch (fColorMode) {
371 case ColorMode::kLegacy:
372 this->setColorMode(ColorMode::kColorManagedSRGB8888_NonLinearBlending);
373 break;
374 case ColorMode::kColorManagedSRGB8888_NonLinearBlending:
375 this->setColorMode(ColorMode::kColorManagedSRGB8888);
376 break;
377 case ColorMode::kColorManagedSRGB8888:
378 this->setColorMode(ColorMode::kColorManagedLinearF16);
379 break;
380 case ColorMode::kColorManagedLinearF16:
381 this->setColorMode(ColorMode::kLegacy);
382 break;
Brian Osmanf750fbc2017-02-08 10:47:28 -0500383 }
brianosman622c8d52016-05-10 06:50:49 -0700384 });
385 fCommands.addCommand(Window::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
386 int previousSlide = fCurrentSlide;
387 fCurrentSlide++;
388 if (fCurrentSlide >= fSlides.count()) {
389 fCurrentSlide = 0;
390 }
391 this->setupCurrentSlide(previousSlide);
392 });
393 fCommands.addCommand(Window::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
394 int previousSlide = fCurrentSlide;
395 fCurrentSlide--;
396 if (fCurrentSlide < 0) {
397 fCurrentSlide = fSlides.count() - 1;
398 }
399 this->setupCurrentSlide(previousSlide);
400 });
401 fCommands.addCommand(Window::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
402 this->changeZoomLevel(1.f / 32.f);
403 fWindow->inval();
404 });
405 fCommands.addCommand(Window::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
406 this->changeZoomLevel(-1.f / 32.f);
407 fWindow->inval();
408 });
jvanverthaf236b52016-05-20 06:01:06 -0700409 fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
Brian Salomon194db172017-08-17 14:37:06 -0400410 sk_app::Window::BackendType newBackend = (sk_app::Window::BackendType)(
411 (fBackendType + 1) % sk_app::Window::kBackendTypeCount);
Jim Van Verthd63c1022017-01-05 13:50:49 -0500412 // Switching to and from Vulkan is problematic on Linux so disabled for now
Brian Salomon194db172017-08-17 14:37:06 -0400413#if defined(SK_BUILD_FOR_UNIX) && defined(SK_VULKAN)
414 if (newBackend == sk_app::Window::kVulkan_BackendType) {
415 newBackend = (sk_app::Window::BackendType)((newBackend + 1) %
416 sk_app::Window::kBackendTypeCount);
417 } else if (fBackendType == sk_app::Window::kVulkan_BackendType) {
418 newBackend = sk_app::Window::kVulkan_BackendType;
Jim Van Verthd63c1022017-01-05 13:50:49 -0500419 }
420#endif
Brian Osman621491e2017-02-28 15:45:01 -0500421 this->setBackend(newBackend);
jvanverthaf236b52016-05-20 06:01:06 -0700422 });
brianosman622c8d52016-05-10 06:50:49 -0700423
Yuqian Li399b3c22017-08-03 11:08:15 -0400424 fCommands.addCommand('A', "AAA", "Toggle analytic AA", [this]() {
425 if (!gSkUseAnalyticAA) {
426 gSkUseAnalyticAA = true;
427 } else if (!gSkForceAnalyticAA) {
428 gSkForceAnalyticAA = true;
429 } else {
430 gSkUseAnalyticAA = gSkForceAnalyticAA = false;
431 }
432 this->updateTitle();
433 fWindow->inval();
434 });
435 fCommands.addCommand('D', "DAA", "Toggle delta AA", [this]() {
436 if (!gSkUseDeltaAA) {
437 gSkUseDeltaAA = true;
438 } else if (!gSkForceDeltaAA) {
439 gSkForceDeltaAA = true;
440 } else {
441 gSkUseDeltaAA = gSkForceDeltaAA = false;
442 }
443 this->updateTitle();
444 fWindow->inval();
445 });
446
jvanverth2bb3b6d2016-04-08 07:24:09 -0700447 // set up slides
448 this->initSlides();
Jim Van Verth6f449692017-02-14 15:16:46 -0500449 this->setStartupSlide();
450 if (FLAGS_list) {
451 this->listNames();
452 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700453
djsollen12d62a72016-04-21 07:59:44 -0700454 fAnimTimer.run();
455
Brian Osman79086b92017-02-10 13:36:16 -0500456 // ImGui initialization:
457 ImGuiIO& io = ImGui::GetIO();
458 io.DisplaySize.x = static_cast<float>(fWindow->width());
459 io.DisplaySize.y = static_cast<float>(fWindow->height());
460
461 // Keymap...
462 io.KeyMap[ImGuiKey_Tab] = (int)Window::Key::kTab;
463 io.KeyMap[ImGuiKey_LeftArrow] = (int)Window::Key::kLeft;
464 io.KeyMap[ImGuiKey_RightArrow] = (int)Window::Key::kRight;
465 io.KeyMap[ImGuiKey_UpArrow] = (int)Window::Key::kUp;
466 io.KeyMap[ImGuiKey_DownArrow] = (int)Window::Key::kDown;
467 io.KeyMap[ImGuiKey_PageUp] = (int)Window::Key::kPageUp;
468 io.KeyMap[ImGuiKey_PageDown] = (int)Window::Key::kPageDown;
469 io.KeyMap[ImGuiKey_Home] = (int)Window::Key::kHome;
470 io.KeyMap[ImGuiKey_End] = (int)Window::Key::kEnd;
471 io.KeyMap[ImGuiKey_Delete] = (int)Window::Key::kDelete;
472 io.KeyMap[ImGuiKey_Backspace] = (int)Window::Key::kBack;
473 io.KeyMap[ImGuiKey_Enter] = (int)Window::Key::kOK;
474 io.KeyMap[ImGuiKey_Escape] = (int)Window::Key::kEscape;
475 io.KeyMap[ImGuiKey_A] = (int)Window::Key::kA;
476 io.KeyMap[ImGuiKey_C] = (int)Window::Key::kC;
477 io.KeyMap[ImGuiKey_V] = (int)Window::Key::kV;
478 io.KeyMap[ImGuiKey_X] = (int)Window::Key::kX;
479 io.KeyMap[ImGuiKey_Y] = (int)Window::Key::kY;
480 io.KeyMap[ImGuiKey_Z] = (int)Window::Key::kZ;
481
482 int w, h;
483 unsigned char* pixels;
484 io.Fonts->GetTexDataAsAlpha8(&pixels, &w, &h);
485 SkImageInfo info = SkImageInfo::MakeA8(w, h);
486 SkPixmap pmap(info, pixels, info.minRowBytes());
Brian Osmanf6877092017-02-13 09:39:57 -0500487 SkMatrix localMatrix = SkMatrix::MakeScale(1.0f / w, 1.0f / h);
488 auto fontImage = SkImage::MakeFromRaster(pmap, nullptr, nullptr);
Mike Reed0acd7952017-04-28 11:12:19 -0400489 auto fontShader = fontImage->makeShader(&localMatrix);
Brian Osmanf6877092017-02-13 09:39:57 -0500490 fImGuiFontPaint.setShader(fontShader);
491 fImGuiFontPaint.setColor(SK_ColorWHITE);
492 fImGuiFontPaint.setFilterQuality(kLow_SkFilterQuality);
493 io.Fonts->TexID = &fImGuiFontPaint;
Brian Osman79086b92017-02-10 13:36:16 -0500494
Brian Osmana109e392017-02-24 09:49:14 -0500495 auto gamutImage = GetResourceAsImage("gamut.png");
496 if (gamutImage) {
Mike Reed0acd7952017-04-28 11:12:19 -0400497 fImGuiGamutPaint.setShader(gamutImage->makeShader());
Brian Osmana109e392017-02-24 09:49:14 -0500498 }
499 fImGuiGamutPaint.setColor(SK_ColorWHITE);
500 fImGuiGamutPaint.setFilterQuality(kLow_SkFilterQuality);
501
Brian Osman70d2f432017-11-08 09:54:10 -0500502 fWindow->attach(backend_type_for_window(fBackendType));
jvanverth9f372462016-04-06 06:08:59 -0700503}
504
jvanverth34524262016-05-04 13:49:13 -0700505void Viewer::initSlides() {
liyuqian1f508fd2016-06-07 06:57:40 -0700506 fAllSlideNames = Json::Value(Json::arrayValue);
507
jvanverth2bb3b6d2016-04-08 07:24:09 -0700508 const skiagm::GMRegistry* gms(skiagm::GMRegistry::Head());
509 while (gms) {
Ben Wagner145dbcd2016-11-03 14:40:50 -0400510 std::unique_ptr<skiagm::GM> gm(gms->factory()(nullptr));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700511
512 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
513 sk_sp<Slide> slide(new GMSlide(gm.release()));
514 fSlides.push_back(slide);
515 }
516
517 gms = gms->next();
518 }
519
520 // reverse array
521 for (int i = 0; i < fSlides.count()/2; ++i) {
522 sk_sp<Slide> temp = fSlides[i];
523 fSlides[i] = fSlides[fSlides.count() - i - 1];
524 fSlides[fSlides.count() - i - 1] = temp;
525 }
526
jvanverthc7027ab2016-06-16 09:52:35 -0700527 // samples
528 const SkViewRegister* reg = SkViewRegister::Head();
529 while (reg) {
530 sk_sp<Slide> slide(new SampleSlide(reg->factory()));
brianosmane1d20072016-07-12 09:07:33 -0700531 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
532 fSlides.push_back(slide);
533 }
jvanverthc7027ab2016-06-16 09:52:35 -0700534 reg = reg->next();
535 }
536
jvanverth2bb3b6d2016-04-08 07:24:09 -0700537 // SKPs
538 for (int i = 0; i < FLAGS_skps.count(); i++) {
539 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
jvanverthc265a922016-04-08 12:51:45 -0700540 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, FLAGS_skps[i])) {
541 continue;
542 }
543
jvanverth2bb3b6d2016-04-08 07:24:09 -0700544 SkString path(FLAGS_skps[i]);
jvanverthc265a922016-04-08 12:51:45 -0700545 sk_sp<SKPSlide> slide(new SKPSlide(SkOSPath::Basename(path.c_str()), path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700546 if (slide) {
547 fSlides.push_back(slide);
548 }
549 } else {
550 SkOSFile::Iter it(FLAGS_skps[i], ".skp");
jvanverthc265a922016-04-08 12:51:45 -0700551 SkString skpName;
552 while (it.next(&skpName)) {
553 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, skpName.c_str())) {
554 continue;
555 }
556
557 SkString path = SkOSPath::Join(FLAGS_skps[i], skpName.c_str());
558 sk_sp<SKPSlide> slide(new SKPSlide(skpName, path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700559 if (slide) {
560 fSlides.push_back(slide);
561 }
562 }
563 }
564 }
liyuqian6f163d22016-06-13 12:26:45 -0700565
566 // JPGs
567 for (int i = 0; i < FLAGS_jpgs.count(); i++) {
568 SkOSFile::Iter it(FLAGS_jpgs[i], ".jpg");
569 SkString jpgName;
570 while (it.next(&jpgName)) {
brianosmane1d20072016-07-12 09:07:33 -0700571 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, jpgName.c_str())) {
572 continue;
573 }
574
liyuqian6f163d22016-06-13 12:26:45 -0700575 SkString path = SkOSPath::Join(FLAGS_jpgs[i], jpgName.c_str());
576 sk_sp<ImageSlide> slide(new ImageSlide(jpgName, path));
577 if (slide) {
578 fSlides.push_back(slide);
579 }
580 }
581 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700582}
583
584
jvanverth34524262016-05-04 13:49:13 -0700585Viewer::~Viewer() {
jvanverth9f372462016-04-06 06:08:59 -0700586 fWindow->detach();
587 delete fWindow;
588}
589
brianosman05de2162016-05-06 13:28:57 -0700590void Viewer::updateTitle() {
csmartdalton578f0642017-02-24 16:04:47 -0700591 if (!fWindow) {
592 return;
593 }
594 if (fWindow->sampleCount() < 0) {
595 return; // Surface hasn't been created yet.
596 }
597
jvanverth34524262016-05-04 13:49:13 -0700598 SkString title("Viewer: ");
jvanverthc265a922016-04-08 12:51:45 -0700599 title.append(fSlides[fCurrentSlide]->getName());
brianosmanb109b8c2016-06-16 13:03:24 -0700600
Yuqian Li399b3c22017-08-03 11:08:15 -0400601 if (gSkUseDeltaAA) {
602 if (gSkForceDeltaAA) {
603 title.append(" <FDAA>");
604 } else {
605 title.append(" <DAA>");
606 }
607 } else if (gSkUseAnalyticAA) {
608 if (gSkForceAnalyticAA) {
609 title.append(" <FAAA>");
610 } else {
611 title.append(" <AAA>");
612 }
613 }
614
Brian Osman92004802017-03-06 11:47:26 -0500615 switch (fColorMode) {
616 case ColorMode::kLegacy:
617 title.append(" Legacy 8888");
618 break;
619 case ColorMode::kColorManagedSRGB8888_NonLinearBlending:
620 title.append(" ColorManaged 8888 (Nonlinear blending)");
621 break;
622 case ColorMode::kColorManagedSRGB8888:
623 title.append(" ColorManaged 8888");
624 break;
625 case ColorMode::kColorManagedLinearF16:
626 title.append(" ColorManaged F16");
627 break;
628 }
Brian Osmanf750fbc2017-02-08 10:47:28 -0500629
Brian Osman92004802017-03-06 11:47:26 -0500630 if (ColorMode::kLegacy != fColorMode) {
Brian Osmana109e392017-02-24 09:49:14 -0500631 int curPrimaries = -1;
632 for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
633 if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
634 curPrimaries = i;
635 break;
636 }
637 }
638 title.appendf(" %s", curPrimaries >= 0 ? gNamedPrimaries[curPrimaries].fName : "Custom");
Brian Osmanfdab5762017-11-09 10:27:55 -0500639
640 if (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) {
641 title.appendf(" Gamma %f", fColorSpaceTransferFn.fG);
642 }
brianosman05de2162016-05-06 13:28:57 -0700643 }
Brian Osmanf750fbc2017-02-08 10:47:28 -0500644
csmartdalton578f0642017-02-24 16:04:47 -0700645 title.append(" [");
jvanverthaf236b52016-05-20 06:01:06 -0700646 title.append(kBackendTypeStrings[fBackendType]);
csmartdalton578f0642017-02-24 16:04:47 -0700647 if (int msaa = fWindow->sampleCount()) {
648 title.appendf(" MSAA: %i", msaa);
649 }
650 title.append("]");
csmartdalton61cd31a2017-02-27 17:00:53 -0700651
652 GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
Brian Osman8b0f2652017-08-29 15:18:34 -0400653 if (GpuPathRenderers::kDefault != pr) {
csmartdalton61cd31a2017-02-27 17:00:53 -0700654 title.appendf(" [Path renderer: %s]", gPathRendererNames[pr].c_str());
655 }
656
brianosman05de2162016-05-06 13:28:57 -0700657 fWindow->setTitle(title.c_str());
658}
659
Jim Van Verth6f449692017-02-14 15:16:46 -0500660void Viewer::setStartupSlide() {
661
662 if (!FLAGS_slide.isEmpty()) {
663 int count = fSlides.count();
664 for (int i = 0; i < count; i++) {
665 if (fSlides[i]->getName().equals(FLAGS_slide[0])) {
666 fCurrentSlide = i;
667 return;
668 }
669 }
670
671 fprintf(stderr, "Unknown slide \"%s\"\n", FLAGS_slide[0]);
672 this->listNames();
673 }
674
675 fCurrentSlide = 0;
676}
677
678void Viewer::listNames() {
679 int count = fSlides.count();
680 SkDebugf("All Slides:\n");
681 for (int i = 0; i < count; i++) {
682 SkDebugf(" %s\n", fSlides[i]->getName().c_str());
683 }
684}
685
Jim Van Verth90dcce52017-11-03 13:36:07 -0400686void Viewer::resetMeasurements() {
687 memset(fPaintTimes, 0, sizeof(fPaintTimes));
688 memset(fFlushTimes, 0, sizeof(fFlushTimes));
689 memset(fAnimateTimes, 0, sizeof(fAnimateTimes));
690 fCurrentMeasurement = 0;
691 fCumulativeMeasurementTime = 0;
692 fCumulativeMeasurementCount = 0;
693}
694
brianosman05de2162016-05-06 13:28:57 -0700695void Viewer::setupCurrentSlide(int previousSlide) {
liyuqiane5a6cd92016-05-27 08:52:52 -0700696 if (fCurrentSlide == previousSlide) {
697 return; // no change; do nothing
698 }
liyuqian6f163d22016-06-13 12:26:45 -0700699 // prepare dimensions for image slides
jvanverthc7027ab2016-06-16 09:52:35 -0700700 fSlides[fCurrentSlide]->load(SkIntToScalar(fWindow->width()), SkIntToScalar(fWindow->height()));
liyuqian6f163d22016-06-13 12:26:45 -0700701
liyuqiane46e4f02016-05-20 07:32:19 -0700702 fGesture.reset();
703 fDefaultMatrix.reset();
liyuqiane46e4f02016-05-20 07:32:19 -0700704
Brian Osman42bb6ac2017-06-05 08:46:04 -0400705 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
706 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
707 const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
708
709 // Start with a matrix that scales the slide to the available screen space
710 if (fWindow->scaleContentToFit()) {
711 if (windowRect.width() > 0 && windowRect.height() > 0) {
712 fDefaultMatrix.setRectToRect(slideBounds, windowRect, SkMatrix::kStart_ScaleToFit);
liyuqiane46e4f02016-05-20 07:32:19 -0700713 }
714 }
715
Brian Osman42bb6ac2017-06-05 08:46:04 -0400716 // Prevent the user from dragging content so far outside the window they can't find it again
717 fGesture.setTransLimit(slideBounds, windowRect, fDefaultMatrix);
liyuqiane46e4f02016-05-20 07:32:19 -0700718
brianosman05de2162016-05-06 13:28:57 -0700719 this->updateTitle();
liyuqiane5a6cd92016-05-27 08:52:52 -0700720 this->updateUIState();
jvanverthc265a922016-04-08 12:51:45 -0700721 if (previousSlide >= 0) {
722 fSlides[previousSlide]->unload();
723 }
Jim Van Verth90dcce52017-11-03 13:36:07 -0400724
725 this->resetMeasurements();
726
jvanverthc265a922016-04-08 12:51:45 -0700727 fWindow->inval();
728}
729
730#define MAX_ZOOM_LEVEL 8
731#define MIN_ZOOM_LEVEL -8
732
jvanverth34524262016-05-04 13:49:13 -0700733void Viewer::changeZoomLevel(float delta) {
jvanverthc265a922016-04-08 12:51:45 -0700734 fZoomLevel += delta;
Brian Osman42bb6ac2017-06-05 08:46:04 -0400735 fZoomLevel = SkScalarPin(fZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
jvanverthc265a922016-04-08 12:51:45 -0700736}
737
liyuqiand3cdbca2016-05-17 12:44:20 -0700738SkMatrix Viewer::computeMatrix() {
jvanverthc265a922016-04-08 12:51:45 -0700739 SkMatrix m;
jvanverthc265a922016-04-08 12:51:45 -0700740
Brian Osman42bb6ac2017-06-05 08:46:04 -0400741 SkScalar zoomScale = (fZoomLevel < 0) ? SK_Scalar1 / (SK_Scalar1 - fZoomLevel)
742 : SK_Scalar1 + fZoomLevel;
743 m = fGesture.localM();
liyuqiand3cdbca2016-05-17 12:44:20 -0700744 m.preConcat(fGesture.globalM());
Brian Osman42bb6ac2017-06-05 08:46:04 -0400745 m.preConcat(fDefaultMatrix);
746 m.preScale(zoomScale, zoomScale);
jvanverthc265a922016-04-08 12:51:45 -0700747
liyuqiand3cdbca2016-05-17 12:44:20 -0700748 return m;
jvanverthc265a922016-04-08 12:51:45 -0700749}
750
Brian Osman621491e2017-02-28 15:45:01 -0500751void Viewer::setBackend(sk_app::Window::BackendType backendType) {
752 fBackendType = backendType;
753
754 fWindow->detach();
755
Brian Osman70d2f432017-11-08 09:54:10 -0500756#if defined(SK_BUILD_FOR_WIN)
Brian Salomon194db172017-08-17 14:37:06 -0400757 // Switching between OpenGL, Vulkan, and ANGLE in the same window is problematic at this point
758 // on Windows, so we just delete the window and recreate it.
Brian Osman70d2f432017-11-08 09:54:10 -0500759 DisplayParams params = fWindow->getRequestedDisplayParams();
760 delete fWindow;
761 fWindow = Window::CreateNativeWindow(nullptr);
Brian Osman621491e2017-02-28 15:45:01 -0500762
Brian Osman70d2f432017-11-08 09:54:10 -0500763 // re-register callbacks
764 fCommands.attach(fWindow);
765 fWindow->registerBackendCreatedFunc(on_backend_created_func, this);
766 fWindow->registerPaintFunc(on_paint_handler, this);
767 fWindow->registerTouchFunc(on_touch_handler, this);
768 fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
769 fWindow->registerMouseFunc(on_mouse_handler, this);
770 fWindow->registerMouseWheelFunc(on_mouse_wheel_handler, this);
771 fWindow->registerKeyFunc(on_key_handler, this);
772 fWindow->registerCharFunc(on_char_handler, this);
773 // Don't allow the window to re-attach. If we're in MSAA mode, the params we grabbed above
774 // will still include our correct sample count. But the re-created fWindow will lose that
775 // information. On Windows, we need to re-create the window when changing sample count,
776 // so we'll incorrectly detect that situation, then re-initialize the window in GL mode,
777 // rendering this tear-down step pointless (and causing the Vulkan window context to fail
778 // as if we had never changed windows at all).
779 fWindow->setRequestedDisplayParams(params, false);
Brian Osman621491e2017-02-28 15:45:01 -0500780#endif
781
Brian Osman70d2f432017-11-08 09:54:10 -0500782 fWindow->attach(backend_type_for_window(fBackendType));
Brian Osman621491e2017-02-28 15:45:01 -0500783}
784
Brian Osman92004802017-03-06 11:47:26 -0500785void Viewer::setColorMode(ColorMode colorMode) {
786 fColorMode = colorMode;
liyuqian6f163d22016-06-13 12:26:45 -0700787
Brian Osmanf750fbc2017-02-08 10:47:28 -0500788 // When we're in color managed mode, we tag our window surface as sRGB. If we've switched into
Brian Osmane0d4fba2017-03-15 10:24:55 -0400789 // or out of legacy/nonlinear mode, we need to update our window configuration.
csmartdalton578f0642017-02-24 16:04:47 -0700790 DisplayParams params = fWindow->getRequestedDisplayParams();
Brian Osman92004802017-03-06 11:47:26 -0500791 bool wasInLegacy = !SkToBool(params.fColorSpace);
Brian Osmane0d4fba2017-03-15 10:24:55 -0400792 bool wantLegacy = (ColorMode::kLegacy == fColorMode) ||
793 (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode);
Brian Osman92004802017-03-06 11:47:26 -0500794 if (wasInLegacy != wantLegacy) {
795 params.fColorSpace = wantLegacy ? nullptr : SkColorSpace::MakeSRGB();
csmartdalton578f0642017-02-24 16:04:47 -0700796 fWindow->setRequestedDisplayParams(params);
Brian Osmanf750fbc2017-02-08 10:47:28 -0500797 }
798
799 this->updateTitle();
800 fWindow->inval();
801}
802
803void Viewer::drawSlide(SkCanvas* canvas) {
Brian Salomonbf52e3d2017-02-22 15:21:11 -0500804 SkAutoCanvasRestore autorestore(canvas, false);
805
Brian Osmanf750fbc2017-02-08 10:47:28 -0500806 // By default, we render directly into the window's surface/canvas
807 SkCanvas* slideCanvas = canvas;
Brian Osmanf6877092017-02-13 09:39:57 -0500808 fLastImage.reset();
jvanverth3d6ed3a2016-04-07 11:09:51 -0700809
Brian Osmane0d4fba2017-03-15 10:24:55 -0400810 // If we're in any of the color managed modes, construct the color space we're going to use
811 sk_sp<SkColorSpace> cs = nullptr;
812 if (ColorMode::kLegacy != fColorMode) {
813 auto transferFn = (ColorMode::kColorManagedLinearF16 == fColorMode)
814 ? SkColorSpace::kLinear_RenderTargetGamma : SkColorSpace::kSRGB_RenderTargetGamma;
Mike Kleinc722f792017-07-31 11:57:21 -0400815 SkMatrix44 toXYZ(SkMatrix44::kIdentity_Constructor);
Brian Osmane0d4fba2017-03-15 10:24:55 -0400816 SkAssertResult(fColorSpacePrimaries.toXYZD50(&toXYZ));
Brian Osmanfdab5762017-11-09 10:27:55 -0500817 if (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) {
818 cs = SkColorSpace::MakeRGB(fColorSpaceTransferFn, toXYZ);
819 } else {
820 cs = SkColorSpace::MakeRGB(transferFn, toXYZ);
821 }
Brian Osmane0d4fba2017-03-15 10:24:55 -0400822 }
823
824 // If we're in F16, or we're zooming, or we're in color correct 8888 and the gamut isn't sRGB,
Brian Osman70d2f432017-11-08 09:54:10 -0500825 // we need to render offscreen. We also need to render offscreen if we're in any raster mode,
826 // because the window surface is actually GL.
Brian Osmanf750fbc2017-02-08 10:47:28 -0500827 sk_sp<SkSurface> offscreenSurface = nullptr;
Brian Osman70d2f432017-11-08 09:54:10 -0500828 if (Window::kRaster_BackendType == fBackendType ||
829 ColorMode::kColorManagedLinearF16 == fColorMode ||
Brian Osman92004802017-03-06 11:47:26 -0500830 fShowZoomWindow ||
Brian Osmane0d4fba2017-03-15 10:24:55 -0400831 (ColorMode::kColorManagedSRGB8888 == fColorMode &&
832 !primaries_equal(fColorSpacePrimaries, gSrgbPrimaries))) {
833
Brian Osman92004802017-03-06 11:47:26 -0500834 SkColorType colorType = (ColorMode::kColorManagedLinearF16 == fColorMode)
835 ? kRGBA_F16_SkColorType : kN32_SkColorType;
Brian Osmane0d4fba2017-03-15 10:24:55 -0400836 // In nonlinear blending mode, we actually use a legacy off-screen canvas, and wrap it
837 // with a special canvas (below) that has the color space attached
838 sk_sp<SkColorSpace> offscreenColorSpace =
839 (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) ? nullptr : cs;
Brian Osman92004802017-03-06 11:47:26 -0500840 SkImageInfo info = SkImageInfo::Make(fWindow->width(), fWindow->height(), colorType,
Brian Osmane0d4fba2017-03-15 10:24:55 -0400841 kPremul_SkAlphaType, std::move(offscreenColorSpace));
Brian Osman70d2f432017-11-08 09:54:10 -0500842 offscreenSurface = Window::kRaster_BackendType == fBackendType ? SkSurface::MakeRaster(info)
843 : canvas->makeSurface(info);
Brian Osmanf750fbc2017-02-08 10:47:28 -0500844 slideCanvas = offscreenSurface->getCanvas();
845 }
846
Brian Osmane0d4fba2017-03-15 10:24:55 -0400847 std::unique_ptr<SkCanvas> xformCanvas = nullptr;
848 if (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) {
849 xformCanvas = SkCreateColorSpaceXformCanvas(slideCanvas, cs);
850 slideCanvas = xformCanvas.get();
851 }
852
Brian Salomonbf52e3d2017-02-22 15:21:11 -0500853 int count = slideCanvas->save();
Brian Osmanf750fbc2017-02-08 10:47:28 -0500854 slideCanvas->clear(SK_ColorWHITE);
Brian Osmanf750fbc2017-02-08 10:47:28 -0500855 slideCanvas->concat(computeMatrix());
Brian Osman1df161a2017-02-09 12:10:20 -0500856 // Time the painting logic of the slide
857 double startTime = SkTime::GetMSecs();
Brian Osmanf750fbc2017-02-08 10:47:28 -0500858 fSlides[fCurrentSlide]->draw(slideCanvas);
Brian Osman1df161a2017-02-09 12:10:20 -0500859 fPaintTimes[fCurrentMeasurement] = SkTime::GetMSecs() - startTime;
Brian Salomonbf52e3d2017-02-22 15:21:11 -0500860 slideCanvas->restoreToCount(count);
Brian Osman1df161a2017-02-09 12:10:20 -0500861
862 // Force a flush so we can time that, too
863 startTime = SkTime::GetMSecs();
864 slideCanvas->flush();
865 fFlushTimes[fCurrentMeasurement] = SkTime::GetMSecs() - startTime;
Brian Osmanf750fbc2017-02-08 10:47:28 -0500866
867 // If we rendered offscreen, snap an image and push the results to the window's canvas
868 if (offscreenSurface) {
Brian Osmanf6877092017-02-13 09:39:57 -0500869 fLastImage = offscreenSurface->makeImageSnapshot();
Brian Osmanf750fbc2017-02-08 10:47:28 -0500870
871 // Tag the image with the sRGB gamut, so no further color space conversion happens
Brian Osmane0d4fba2017-03-15 10:24:55 -0400872 sk_sp<SkColorSpace> srgb = (ColorMode::kColorManagedLinearF16 == fColorMode)
Brian Osmanf750fbc2017-02-08 10:47:28 -0500873 ? SkColorSpace::MakeSRGBLinear() : SkColorSpace::MakeSRGB();
Brian Osmane0d4fba2017-03-15 10:24:55 -0400874 auto retaggedImage = SkImageMakeRasterCopyAndAssignColorSpace(fLastImage.get(), srgb.get());
Brian Salomonbf52e3d2017-02-22 15:21:11 -0500875 SkPaint paint;
876 paint.setBlendMode(SkBlendMode::kSrc);
877 canvas->drawImage(retaggedImage, 0, 0, &paint);
liyuqian74959a12016-06-16 14:10:34 -0700878 }
liyuqian6f163d22016-06-13 12:26:45 -0700879}
880
Christopher Dalton443ec1b2017-02-24 13:22:53 -0700881void Viewer::onBackendCreated() {
882 this->updateTitle();
883 this->updateUIState();
884 this->setupCurrentSlide(-1);
Jim Van Verth90dcce52017-11-03 13:36:07 -0400885 this->resetMeasurements();
Christopher Dalton443ec1b2017-02-24 13:22:53 -0700886 fWindow->show();
887 fWindow->inval();
888}
Jim Van Verth6f449692017-02-14 15:16:46 -0500889
Christopher Dalton443ec1b2017-02-24 13:22:53 -0700890void Viewer::onPaint(SkCanvas* canvas) {
Brian Osman79086b92017-02-10 13:36:16 -0500891 // Update ImGui input
892 ImGuiIO& io = ImGui::GetIO();
893 io.DeltaTime = 1.0f / 60.0f;
894 io.DisplaySize.x = static_cast<float>(fWindow->width());
895 io.DisplaySize.y = static_cast<float>(fWindow->height());
896
897 io.KeyAlt = io.KeysDown[static_cast<int>(Window::Key::kOption)];
898 io.KeyCtrl = io.KeysDown[static_cast<int>(Window::Key::kCtrl)];
899 io.KeyShift = io.KeysDown[static_cast<int>(Window::Key::kShift)];
900
901 ImGui::NewFrame();
902
Jim Van Verth90dcce52017-11-03 13:36:07 -0400903 this->drawSlide(canvas);
jvanverthc265a922016-04-08 12:51:45 -0700904
Brian Osman1df161a2017-02-09 12:10:20 -0500905 // Advance our timing bookkeeping
Jim Van Verth90dcce52017-11-03 13:36:07 -0400906 fCumulativeMeasurementTime += fAnimateTimes[fCurrentMeasurement] +
907 fPaintTimes[fCurrentMeasurement] +
908 fFlushTimes[fCurrentMeasurement];
909 fCumulativeMeasurementCount++;
Brian Osman1df161a2017-02-09 12:10:20 -0500910 fCurrentMeasurement = (fCurrentMeasurement + 1) & (kMeasurementCount - 1);
911 SkASSERT(fCurrentMeasurement < kMeasurementCount);
912
913 // Draw any overlays or UI that we don't want timed
jvanverthc265a922016-04-08 12:51:45 -0700914 if (fDisplayStats) {
915 drawStats(canvas);
916 }
brianosman622c8d52016-05-10 06:50:49 -0700917 fCommands.drawHelp(canvas);
liyuqian2edb0f42016-07-06 14:11:32 -0700918
Jim Van Verth90dcce52017-11-03 13:36:07 -0400919 this->drawImGui(canvas);
Brian Osman79086b92017-02-10 13:36:16 -0500920
Brian Osman1df161a2017-02-09 12:10:20 -0500921 // Update the FPS
Jim Van Verth90dcce52017-11-03 13:36:07 -0400922 this->updateUIState();
jvanverth3d6ed3a2016-04-07 11:09:51 -0700923}
924
jvanverth814e38d2016-06-06 08:48:47 -0700925bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) {
Brian Osmanb53f48c2017-06-07 10:00:30 -0400926 if (GestureDevice::kMouse == fGestureDevice) {
927 return false;
928 }
liyuqiand3cdbca2016-05-17 12:44:20 -0700929 void* castedOwner = reinterpret_cast<void*>(owner);
930 switch (state) {
931 case Window::kUp_InputState: {
932 fGesture.touchEnd(castedOwner);
933 break;
934 }
935 case Window::kDown_InputState: {
Brian Osman42bb6ac2017-06-05 08:46:04 -0400936 fGesture.touchBegin(castedOwner, x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -0700937 break;
938 }
939 case Window::kMove_InputState: {
Brian Osman42bb6ac2017-06-05 08:46:04 -0400940 fGesture.touchMoved(castedOwner, x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -0700941 break;
942 }
943 }
Brian Osmanb53f48c2017-06-07 10:00:30 -0400944 fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kTouch : GestureDevice::kNone;
liyuqiand3cdbca2016-05-17 12:44:20 -0700945 fWindow->inval();
946 return true;
947}
948
Jim Van Verthe7705782017-05-04 14:00:59 -0400949bool Viewer::onMouse(float x, float y, Window::InputState state, uint32_t modifiers) {
Jim Van Verthc9e7f9c2017-11-02 09:46:49 -0400950 if (!fSlides[fCurrentSlide]->onMouse(x, y, state, modifiers)) {
951 if (GestureDevice::kTouch == fGestureDevice) {
952 return false;
953 }
954 switch (state) {
955 case Window::kUp_InputState: {
956 fGesture.touchEnd(nullptr);
957 break;
958 }
959 case Window::kDown_InputState: {
960 fGesture.touchBegin(nullptr, x, y);
961 break;
962 }
963 case Window::kMove_InputState: {
964 fGesture.touchMoved(nullptr, x, y);
965 break;
966 }
967 }
968 fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone;
Brian Osmanb53f48c2017-06-07 10:00:30 -0400969 }
Jim Van Verthe7705782017-05-04 14:00:59 -0400970 fWindow->inval();
971 return true;
972}
973
jvanverth34524262016-05-04 13:49:13 -0700974void Viewer::drawStats(SkCanvas* canvas) {
jvanverth3d6ed3a2016-04-07 11:09:51 -0700975 static const float kPixelPerMS = 2.0f;
Jim Van Verth98c5b8b2017-11-09 15:43:46 -0500976 static const int kDisplayWidth = 192;
977 static const int kGraphHeight = 100;
978 static const int kTextHeight = 60;
979 static const int kDisplayHeight = kGraphHeight + kTextHeight;
jvanverth3d6ed3a2016-04-07 11:09:51 -0700980 static const int kDisplayPadding = 10;
981 static const int kGraphPadding = 3;
982 static const SkScalar kBaseMS = 1000.f / 60.f; // ms/frame to hit 60 fps
983
Mike Reed3661bc92017-02-22 13:21:42 -0500984 SkISize canvasSize = canvas->getBaseLayerSize();
jvanverth3d6ed3a2016-04-07 11:09:51 -0700985 SkRect rect = SkRect::MakeXYWH(SkIntToScalar(canvasSize.fWidth-kDisplayWidth-kDisplayPadding),
986 SkIntToScalar(kDisplayPadding),
987 SkIntToScalar(kDisplayWidth), SkIntToScalar(kDisplayHeight));
988 SkPaint paint;
989 canvas->save();
990
jvanverth3d6ed3a2016-04-07 11:09:51 -0700991 paint.setColor(SK_ColorBLACK);
992 canvas->drawRect(rect, paint);
993 // draw the 16ms line
994 paint.setColor(SK_ColorLTGRAY);
995 canvas->drawLine(rect.fLeft, rect.fBottom - kBaseMS*kPixelPerMS,
996 rect.fRight, rect.fBottom - kBaseMS*kPixelPerMS, paint);
997 paint.setColor(SK_ColorRED);
998 paint.setStyle(SkPaint::kStroke_Style);
999 canvas->drawRect(rect, paint);
Jim Van Verth98c5b8b2017-11-09 15:43:46 -05001000 paint.setStyle(SkPaint::kFill_Style);
jvanverth3d6ed3a2016-04-07 11:09:51 -07001001
1002 int x = SkScalarTruncToInt(rect.fLeft) + kGraphPadding;
Jim Van Verth98c5b8b2017-11-09 15:43:46 -05001003 const int xStep = 3;
jvanverth3d6ed3a2016-04-07 11:09:51 -07001004 int i = fCurrentMeasurement;
Jim Van Verth98c5b8b2017-11-09 15:43:46 -05001005 double ms = 0;
1006 double animateMS = 0;
1007 double paintMS = 0;
1008 double flushMS = 0;
1009 int count = 0;
jvanverth3d6ed3a2016-04-07 11:09:51 -07001010 do {
Brian Osman1df161a2017-02-09 12:10:20 -05001011 // Round to nearest values
1012 int animateHeight = (int)(fAnimateTimes[i] * kPixelPerMS + 0.5);
1013 int paintHeight = (int)(fPaintTimes[i] * kPixelPerMS + 0.5);
1014 int flushHeight = (int)(fFlushTimes[i] * kPixelPerMS + 0.5);
1015 int startY = SkScalarTruncToInt(rect.fBottom);
Jim Van Verth98c5b8b2017-11-09 15:43:46 -05001016 int endY = SkTMax(startY - flushHeight, kDisplayPadding + kTextHeight);
Brian Osman1df161a2017-02-09 12:10:20 -05001017 paint.setColor(SK_ColorRED);
1018 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
1019 SkIntToScalar(x), SkIntToScalar(endY), paint);
1020 startY = endY;
Jim Van Verth98c5b8b2017-11-09 15:43:46 -05001021 endY = SkTMax(startY - paintHeight, kDisplayPadding + kTextHeight);
Brian Osman1df161a2017-02-09 12:10:20 -05001022 paint.setColor(SK_ColorGREEN);
1023 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
1024 SkIntToScalar(x), SkIntToScalar(endY), paint);
1025 startY = endY;
Jim Van Verth98c5b8b2017-11-09 15:43:46 -05001026 endY = SkTMax(startY - animateHeight, kDisplayPadding + kTextHeight);
Brian Osman1df161a2017-02-09 12:10:20 -05001027 paint.setColor(SK_ColorMAGENTA);
jvanverth3d6ed3a2016-04-07 11:09:51 -07001028 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
1029 SkIntToScalar(x), SkIntToScalar(endY), paint);
Jim Van Verth98c5b8b2017-11-09 15:43:46 -05001030
1031 double inc = fAnimateTimes[i] + fPaintTimes[i] + fFlushTimes[i];
1032 if (inc > 0) {
1033 ms += inc;
1034 animateMS += fAnimateTimes[i];
1035 paintMS += fPaintTimes[i];
1036 flushMS += fFlushTimes[i];
1037 ++count;
1038 }
1039
jvanverth3d6ed3a2016-04-07 11:09:51 -07001040 i++;
1041 i &= (kMeasurementCount - 1); // fast mod
1042 x += xStep;
1043 } while (i != fCurrentMeasurement);
jvanverth9f372462016-04-06 06:08:59 -07001044
Jim Van Verth98c5b8b2017-11-09 15:43:46 -05001045 paint.setTextSize(16);
1046 SkString mainString;
1047 mainString.appendf("%4.3f ms -> %4.3f ms", ms / SkTMax(1, count),
1048 fCumulativeMeasurementTime / SkTMax(1, fCumulativeMeasurementCount));
1049 paint.setColor(SK_ColorWHITE);
1050 canvas->drawString(mainString.c_str(), rect.fLeft+3, rect.fTop + 14, paint);
1051
1052 SkString animateString;
1053 animateString.appendf("Animate: %4.3f ms", animateMS / SkTMax(1, count));
1054 paint.setColor(0xffff66ff); // pure magenta is hard to read
1055 canvas->drawString(animateString.c_str(), rect.fLeft+3, rect.fTop + 28, paint);
1056
1057 SkString paintString;
1058 paintString.appendf("Paint: %4.3f ms", paintMS / SkTMax(1, count));
1059 paint.setColor(SK_ColorGREEN);
1060 canvas->drawString(paintString.c_str(), rect.fLeft+3, rect.fTop + 42, paint);
1061
1062 SkString flushString;
1063 flushString.appendf("Flush: %4.3f ms", flushMS / SkTMax(1, count));
1064 paint.setColor(0xffff6666); // pure red is hard to read
1065 canvas->drawString(flushString.c_str(), rect.fLeft+3, rect.fTop + 56, paint);
1066
jvanverth9f372462016-04-06 06:08:59 -07001067 canvas->restore();
1068}
1069
Brian Osmana109e392017-02-24 09:49:14 -05001070static ImVec2 ImGui_DragPrimary(const char* label, float* x, float* y,
1071 const ImVec2& pos, const ImVec2& size) {
1072 // Transform primaries ([0, 0] - [0.8, 0.9]) to screen coords (including Y-flip)
1073 ImVec2 center(pos.x + (*x / 0.8f) * size.x, pos.y + (1.0f - (*y / 0.9f)) * size.y);
1074
1075 // Invisible 10x10 button
1076 ImGui::SetCursorScreenPos(ImVec2(center.x - 5, center.y - 5));
1077 ImGui::InvisibleButton(label, ImVec2(10, 10));
1078
1079 if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) {
1080 ImGuiIO& io = ImGui::GetIO();
1081 // Normalized mouse position, relative to our gamut box
1082 ImVec2 mousePosXY((io.MousePos.x - pos.x) / size.x, (io.MousePos.y - pos.y) / size.y);
1083 // Clamp to edge of box, convert back to primary scale
1084 *x = SkTPin(mousePosXY.x, 0.0f, 1.0f) * 0.8f;
1085 *y = SkTPin(1 - mousePosXY.y, 0.0f, 1.0f) * 0.9f;
1086 }
1087
1088 if (ImGui::IsItemHovered()) {
1089 ImGui::SetTooltip("x: %.3f\ny: %.3f", *x, *y);
1090 }
1091
1092 // Return screen coordinates for the caller. We could just return center here, but we'd have
1093 // one frame of lag during drag.
1094 return ImVec2(pos.x + (*x / 0.8f) * size.x, pos.y + (1.0f - (*y / 0.9f)) * size.y);
1095}
1096
1097static void ImGui_Primaries(SkColorSpacePrimaries* primaries, SkPaint* gamutPaint) {
1098 ImDrawList* drawList = ImGui::GetWindowDrawList();
1099
1100 // The gamut image covers a (0.8 x 0.9) shaped region, so fit our image/canvas to the available
1101 // width, and scale the height to maintain aspect ratio.
1102 float canvasWidth = SkTMax(ImGui::GetContentRegionAvailWidth(), 50.0f);
1103 ImVec2 size = ImVec2(canvasWidth, canvasWidth * (0.9f / 0.8f));
1104 ImVec2 pos = ImGui::GetCursorScreenPos();
1105
1106 // Background image. Only draw a subset of the image, to avoid the regions less than zero.
1107 // Simplifes re-mapping math, clipping behavior, and increases resolution in the useful area.
1108 // Magic numbers are pixel locations of the origin and upper-right corner.
1109 drawList->AddImage(gamutPaint, pos, ImVec2(pos.x + size.x, pos.y + size.y),
1110 ImVec2(242, 61), ImVec2(1897, 1922));
1111 ImVec2 endPos = ImGui::GetCursorPos();
1112
1113 // Primary markers
1114 ImVec2 r = ImGui_DragPrimary("R", &primaries->fRX, &primaries->fRY, pos, size);
1115 ImVec2 g = ImGui_DragPrimary("G", &primaries->fGX, &primaries->fGY, pos, size);
1116 ImVec2 b = ImGui_DragPrimary("B", &primaries->fBX, &primaries->fBY, pos, size);
1117 ImVec2 w = ImGui_DragPrimary("W", &primaries->fWX, &primaries->fWY, pos, size);
1118
1119 // Gamut triangle
1120 drawList->AddCircle(r, 5.0f, 0xFF000040);
1121 drawList->AddCircle(g, 5.0f, 0xFF004000);
1122 drawList->AddCircle(b, 5.0f, 0xFF400000);
1123 drawList->AddCircle(w, 5.0f, 0xFFFFFFFF);
1124 drawList->AddTriangle(r, g, b, 0xFFFFFFFF);
1125
1126 // Re-position cursor immediate after the diagram for subsequent controls
1127 ImGui::SetCursorPos(endPos);
1128}
1129
Brian Osmanead517d2017-11-13 15:36:36 -05001130typedef std::function<void(SkCanvas*)> CustomGuiPainter;
1131static SkTArray<CustomGuiPainter> gCustomGuiPainters;
1132
1133static void ImGui_Skia_Callback(const ImVec2& size, CustomGuiPainter painter) {
1134 intptr_t painterIndex = gCustomGuiPainters.count();
1135 gCustomGuiPainters.push_back(painter);
1136 ImGui::Image((ImTextureID)painterIndex, size);
1137}
1138
Brian Osman79086b92017-02-10 13:36:16 -05001139void Viewer::drawImGui(SkCanvas* canvas) {
1140 // Support drawing the ImGui demo window. Superfluous, but gives a good idea of what's possible
1141 if (fShowImGuiTestWindow) {
1142 ImGui::ShowTestWindow(&fShowImGuiTestWindow);
1143 }
1144
1145 if (fShowImGuiDebugWindow) {
Brian Osmana109e392017-02-24 09:49:14 -05001146 // We have some dynamic content that sizes to fill available size. If the scroll bar isn't
1147 // always visible, we can end up in a layout feedback loop.
1148 ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiSetCond_FirstUseEver);
Brian Salomon99a33902017-03-07 15:16:34 -05001149 DisplayParams params = fWindow->getRequestedDisplayParams();
1150 bool paramsChanged = false;
Brian Osmana109e392017-02-24 09:49:14 -05001151 if (ImGui::Begin("Tools", &fShowImGuiDebugWindow,
1152 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
Brian Osman621491e2017-02-28 15:45:01 -05001153 if (ImGui::CollapsingHeader("Backend")) {
1154 int newBackend = static_cast<int>(fBackendType);
1155 ImGui::RadioButton("Raster", &newBackend, sk_app::Window::kRaster_BackendType);
1156 ImGui::SameLine();
1157 ImGui::RadioButton("OpenGL", &newBackend, sk_app::Window::kNativeGL_BackendType);
Brian Salomon194db172017-08-17 14:37:06 -04001158#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
1159 ImGui::SameLine();
1160 ImGui::RadioButton("ANGLE", &newBackend, sk_app::Window::kANGLE_BackendType);
1161#endif
Brian Osman621491e2017-02-28 15:45:01 -05001162#if defined(SK_VULKAN)
1163 ImGui::SameLine();
1164 ImGui::RadioButton("Vulkan", &newBackend, sk_app::Window::kVulkan_BackendType);
1165#endif
1166 if (newBackend != fBackendType) {
1167 fDeferredActions.push_back([=]() {
1168 this->setBackend(static_cast<sk_app::Window::BackendType>(newBackend));
1169 });
1170 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001171
Brian Salomon99a33902017-03-07 15:16:34 -05001172 const GrContext* ctx = fWindow->getGrContext();
1173 bool* inst = &params.fGrContextOptions.fEnableInstancedRendering;
1174 if (ctx && ImGui::Checkbox("Instanced Rendering", inst)) {
1175 paramsChanged = true;
1176 }
Jim Van Verthfbdc0802017-05-02 16:15:53 -04001177 bool* wire = &params.fGrContextOptions.fWireframeMode;
1178 if (ctx && ImGui::Checkbox("Wireframe Mode", wire)) {
1179 paramsChanged = true;
1180 }
Brian Salomon99a33902017-03-07 15:16:34 -05001181
Brian Osman28b12522017-03-08 17:10:24 -05001182 if (ctx) {
1183 int sampleCount = fWindow->sampleCount();
1184 ImGui::Text("MSAA: "); ImGui::SameLine();
1185 ImGui::RadioButton("0", &sampleCount, 0); ImGui::SameLine();
1186 ImGui::RadioButton("4", &sampleCount, 4); ImGui::SameLine();
1187 ImGui::RadioButton("8", &sampleCount, 8); ImGui::SameLine();
1188 ImGui::RadioButton("16", &sampleCount, 16);
1189
1190 if (sampleCount != params.fMSAASampleCount) {
1191 params.fMSAASampleCount = sampleCount;
1192 paramsChanged = true;
1193 }
1194 }
1195
Brian Osman8a9de3d2017-03-01 14:59:05 -05001196 if (ImGui::TreeNode("Path Renderers")) {
Brian Osman8a9de3d2017-03-01 14:59:05 -05001197 GpuPathRenderers prevPr = params.fGrContextOptions.fGpuPathRenderers;
Brian Osman8a9de3d2017-03-01 14:59:05 -05001198 auto prButton = [&](GpuPathRenderers x) {
1199 if (ImGui::RadioButton(gPathRendererNames[x].c_str(), prevPr == x)) {
Brian Salomon99a33902017-03-07 15:16:34 -05001200 if (x != params.fGrContextOptions.fGpuPathRenderers) {
1201 params.fGrContextOptions.fGpuPathRenderers = x;
1202 paramsChanged = true;
1203 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001204 }
1205 };
1206
1207 if (!ctx) {
1208 ImGui::RadioButton("Software", true);
1209 } else if (fWindow->sampleCount()) {
Brian Osman8b0f2652017-08-29 15:18:34 -04001210 prButton(GpuPathRenderers::kDefault);
Brian Osman8a9de3d2017-03-01 14:59:05 -05001211 prButton(GpuPathRenderers::kAll);
1212 if (ctx->caps()->shaderCaps()->pathRenderingSupport()) {
1213 prButton(GpuPathRenderers::kStencilAndCover);
1214 }
1215 if (ctx->caps()->sampleShadingSupport()) {
1216 prButton(GpuPathRenderers::kMSAA);
1217 }
1218 prButton(GpuPathRenderers::kTessellating);
Brian Osman8a9de3d2017-03-01 14:59:05 -05001219 prButton(GpuPathRenderers::kNone);
1220 } else {
Brian Osman8b0f2652017-08-29 15:18:34 -04001221 prButton(GpuPathRenderers::kDefault);
Brian Osman8a9de3d2017-03-01 14:59:05 -05001222 prButton(GpuPathRenderers::kAll);
Chris Dalton1a325d22017-07-14 15:17:41 -06001223 if (GrCoverageCountingPathRenderer::IsSupported(*ctx->caps())) {
1224 prButton(GpuPathRenderers::kCoverageCounting);
1225 }
Jim Van Verth83010462017-03-16 08:45:39 -04001226 prButton(GpuPathRenderers::kSmall);
Brian Osman8a9de3d2017-03-01 14:59:05 -05001227 prButton(GpuPathRenderers::kTessellating);
1228 prButton(GpuPathRenderers::kNone);
1229 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001230 ImGui::TreePop();
1231 }
Brian Osman621491e2017-02-28 15:45:01 -05001232 }
1233
Brian Osmanfce09c52017-11-14 15:32:20 -05001234 if (fShowSlidePicker) {
1235 ImGui::SetNextTreeNodeOpen(true);
1236 }
1237
Brian Osman79086b92017-02-10 13:36:16 -05001238 if (ImGui::CollapsingHeader("Slide")) {
1239 static ImGuiTextFilter filter;
Brian Osmanf479e422017-11-08 13:11:36 -05001240 static ImVector<const char*> filteredSlideNames;
1241 static ImVector<int> filteredSlideIndices;
1242
Brian Osmanfce09c52017-11-14 15:32:20 -05001243 if (fShowSlidePicker) {
1244 ImGui::SetKeyboardFocusHere();
1245 fShowSlidePicker = false;
1246 }
1247
Brian Osman79086b92017-02-10 13:36:16 -05001248 filter.Draw();
Brian Osmanf479e422017-11-08 13:11:36 -05001249 filteredSlideNames.clear();
1250 filteredSlideIndices.clear();
1251 int filteredIndex = 0;
1252 for (int i = 0; i < fSlides.count(); ++i) {
1253 const char* slideName = fSlides[i]->getName().c_str();
1254 if (filter.PassFilter(slideName) || i == fCurrentSlide) {
1255 if (i == fCurrentSlide) {
1256 filteredIndex = filteredSlideIndices.size();
Brian Osman79086b92017-02-10 13:36:16 -05001257 }
Brian Osmanf479e422017-11-08 13:11:36 -05001258 filteredSlideNames.push_back(slideName);
1259 filteredSlideIndices.push_back(i);
Brian Osman79086b92017-02-10 13:36:16 -05001260 }
Brian Osman79086b92017-02-10 13:36:16 -05001261 }
Brian Osmanf479e422017-11-08 13:11:36 -05001262
1263 int previousSlide = fCurrentSlide;
1264 if (ImGui::ListBox("", &filteredIndex, filteredSlideNames.begin(),
1265 filteredSlideNames.size(), 20)) {
1266 fCurrentSlide = filteredSlideIndices[filteredIndex];
1267 setupCurrentSlide(previousSlide);
Brian Osman79086b92017-02-10 13:36:16 -05001268 }
1269 }
Brian Osmana109e392017-02-24 09:49:14 -05001270
1271 if (ImGui::CollapsingHeader("Color Mode")) {
Brian Osman92004802017-03-06 11:47:26 -05001272 ColorMode newMode = fColorMode;
1273 auto cmButton = [&](ColorMode mode, const char* label) {
1274 if (ImGui::RadioButton(label, mode == fColorMode)) {
1275 newMode = mode;
1276 }
1277 };
1278
1279 cmButton(ColorMode::kLegacy, "Legacy 8888");
1280 cmButton(ColorMode::kColorManagedSRGB8888_NonLinearBlending,
1281 "Color Managed 8888 (Nonlinear blending)");
1282 cmButton(ColorMode::kColorManagedSRGB8888, "Color Managed 8888");
1283 cmButton(ColorMode::kColorManagedLinearF16, "Color Managed F16");
1284
1285 if (newMode != fColorMode) {
Brian Osmanfd8f4d52017-02-24 11:57:23 -05001286 // It isn't safe to switch color mode now (in the middle of painting). We might
1287 // tear down the back-end, etc... Defer this change until the next onIdle.
1288 fDeferredActions.push_back([=]() {
Brian Osman92004802017-03-06 11:47:26 -05001289 this->setColorMode(newMode);
Brian Osmanfd8f4d52017-02-24 11:57:23 -05001290 });
Brian Osmana109e392017-02-24 09:49:14 -05001291 }
1292
1293 // Pick from common gamuts:
1294 int primariesIdx = 4; // Default: Custom
1295 for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
1296 if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
1297 primariesIdx = i;
1298 break;
1299 }
1300 }
1301
Brian Osmanfdab5762017-11-09 10:27:55 -05001302 // When we're in xform canvas mode, we can alter the transfer function, too
1303 if (ColorMode::kColorManagedSRGB8888_NonLinearBlending == fColorMode) {
1304 ImGui::SliderFloat("Gamma", &fColorSpaceTransferFn.fG, 0.5f, 3.5f);
1305 }
1306
Brian Osmana109e392017-02-24 09:49:14 -05001307 if (ImGui::Combo("Primaries", &primariesIdx,
1308 "sRGB\0AdobeRGB\0P3\0Rec. 2020\0Custom\0\0")) {
1309 if (primariesIdx >= 0 && primariesIdx <= 3) {
1310 fColorSpacePrimaries = *gNamedPrimaries[primariesIdx].fPrimaries;
1311 }
1312 }
1313
1314 // Allow direct editing of gamut
1315 ImGui_Primaries(&fColorSpacePrimaries, &fImGuiGamutPaint);
1316 }
Brian Osman79086b92017-02-10 13:36:16 -05001317 }
Brian Salomon99a33902017-03-07 15:16:34 -05001318 if (paramsChanged) {
1319 fDeferredActions.push_back([=]() {
1320 fWindow->setRequestedDisplayParams(params);
1321 fWindow->inval();
1322 this->updateTitle();
1323 });
1324 }
Brian Osman79086b92017-02-10 13:36:16 -05001325 ImGui::End();
1326 }
1327
Brian Osmanf6877092017-02-13 09:39:57 -05001328 if (fShowZoomWindow && fLastImage) {
1329 if (ImGui::Begin("Zoom", &fShowZoomWindow, ImVec2(200, 200))) {
Brian Osmanead517d2017-11-13 15:36:36 -05001330 static int zoomFactor = 8;
1331 if (ImGui::Button("<<")) {
1332 zoomFactor = SkTMax(zoomFactor / 2, 4);
1333 }
1334 ImGui::SameLine(); ImGui::Text("%2d", zoomFactor); ImGui::SameLine();
1335 if (ImGui::Button(">>")) {
1336 zoomFactor = SkTMin(zoomFactor * 2, 32);
1337 }
Brian Osmanf6877092017-02-13 09:39:57 -05001338
Brian Osmanf6877092017-02-13 09:39:57 -05001339 ImVec2 mousePos = ImGui::GetMousePos();
1340 ImVec2 avail = ImGui::GetContentRegionAvail();
1341
Brian Osmanead517d2017-11-13 15:36:36 -05001342 uint32_t pixel = 0;
1343 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
1344 if (fLastImage->readPixels(info, &pixel, info.minRowBytes(), mousePos.x, mousePos.y)) {
1345 ImGui::SameLine();
Brian Osman07b56b22017-11-21 14:59:31 -05001346 ImGui::Text("(X, Y): %d, %d RGBA: %x %x %x %x",
1347 sk_float_round2int(mousePos.x), sk_float_round2int(mousePos.y),
1348 SkGetPackedR32(pixel), SkGetPackedG32(pixel),
Brian Osmanead517d2017-11-13 15:36:36 -05001349 SkGetPackedB32(pixel), SkGetPackedA32(pixel));
1350 }
1351
1352 ImGui_Skia_Callback(avail, [=](SkCanvas* c) {
1353 // Translate so the region of the image that's under the mouse cursor is centered
1354 // in the zoom canvas:
1355 c->scale(zoomFactor, zoomFactor);
1356 c->translate(avail.x * 0.5f / zoomFactor - mousePos.x - 0.5f,
1357 avail.y * 0.5f / zoomFactor - mousePos.y - 0.5f);
1358 c->drawImage(this->fLastImage, 0, 0);
1359
1360 SkPaint outline;
1361 outline.setStyle(SkPaint::kStroke_Style);
1362 c->drawRect(SkRect::MakeXYWH(mousePos.x, mousePos.y, 1, 1), outline);
1363 });
Brian Osmanf6877092017-02-13 09:39:57 -05001364 }
1365
1366 ImGui::End();
1367 }
1368
Brian Osman79086b92017-02-10 13:36:16 -05001369 // This causes ImGui to rebuild vertex/index data based on all immediate-mode commands
1370 // (widgets, etc...) that have been issued
1371 ImGui::Render();
1372
1373 // Then we fetch the most recent data, and convert it so we can render with Skia
1374 const ImDrawData* drawData = ImGui::GetDrawData();
1375 SkTDArray<SkPoint> pos;
1376 SkTDArray<SkPoint> uv;
1377 SkTDArray<SkColor> color;
Brian Osman79086b92017-02-10 13:36:16 -05001378
1379 for (int i = 0; i < drawData->CmdListsCount; ++i) {
1380 const ImDrawList* drawList = drawData->CmdLists[i];
1381
1382 // De-interleave all vertex data (sigh), convert to Skia types
1383 pos.rewind(); uv.rewind(); color.rewind();
1384 for (int i = 0; i < drawList->VtxBuffer.size(); ++i) {
1385 const ImDrawVert& vert = drawList->VtxBuffer[i];
1386 pos.push(SkPoint::Make(vert.pos.x, vert.pos.y));
1387 uv.push(SkPoint::Make(vert.uv.x, vert.uv.y));
1388 color.push(vert.col);
1389 }
1390 // ImGui colors are RGBA
1391 SkSwapRB(color.begin(), color.begin(), color.count());
1392
1393 int indexOffset = 0;
1394
1395 // Draw everything with canvas.drawVertices...
1396 for (int j = 0; j < drawList->CmdBuffer.size(); ++j) {
1397 const ImDrawCmd* drawCmd = &drawList->CmdBuffer[j];
1398
Brian Osmanead517d2017-11-13 15:36:36 -05001399 SkAutoCanvasRestore acr(canvas, true);
1400
Brian Osman79086b92017-02-10 13:36:16 -05001401 // TODO: Find min/max index for each draw, so we know how many vertices (sigh)
1402 if (drawCmd->UserCallback) {
1403 drawCmd->UserCallback(drawList, drawCmd);
1404 } else {
Brian Osmanead517d2017-11-13 15:36:36 -05001405 intptr_t idIndex = (intptr_t)drawCmd->TextureId;
1406 if (idIndex < gCustomGuiPainters.count()) {
1407 // Small image IDs are actually indices into a list of callbacks. We directly
1408 // examing the vertex data to deduce the image rectangle, then reconfigure the
1409 // canvas to be clipped and translated so that the callback code gets to use
1410 // Skia to render a widget in the middle of an ImGui panel.
1411 ImDrawIdx rectIndex = drawList->IdxBuffer[indexOffset];
1412 SkPoint tl = pos[rectIndex], br = pos[rectIndex + 2];
1413 canvas->clipRect(SkRect::MakeLTRB(tl.fX, tl.fY, br.fX, br.fY));
1414 canvas->translate(tl.fX, tl.fY);
1415 gCustomGuiPainters[idIndex](canvas);
1416 } else {
1417 SkPaint* paint = static_cast<SkPaint*>(drawCmd->TextureId);
1418 SkASSERT(paint);
Brian Osmanf6877092017-02-13 09:39:57 -05001419
Brian Osmanead517d2017-11-13 15:36:36 -05001420 canvas->clipRect(SkRect::MakeLTRB(drawCmd->ClipRect.x, drawCmd->ClipRect.y,
1421 drawCmd->ClipRect.z, drawCmd->ClipRect.w));
1422 auto vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
1423 drawList->VtxBuffer.size(),
1424 pos.begin(), uv.begin(), color.begin(),
1425 drawCmd->ElemCount,
1426 drawList->IdxBuffer.begin() + indexOffset);
1427 canvas->drawVertices(vertices, SkBlendMode::kModulate, *paint);
1428 indexOffset += drawCmd->ElemCount;
1429 }
Brian Osman79086b92017-02-10 13:36:16 -05001430 }
1431 }
1432 }
Brian Osmanead517d2017-11-13 15:36:36 -05001433
1434 gCustomGuiPainters.reset();
Brian Osman79086b92017-02-10 13:36:16 -05001435}
1436
liyuqian2edb0f42016-07-06 14:11:32 -07001437void Viewer::onIdle() {
Brian Osmanfd8f4d52017-02-24 11:57:23 -05001438 for (int i = 0; i < fDeferredActions.count(); ++i) {
1439 fDeferredActions[i]();
1440 }
1441 fDeferredActions.reset();
1442
Brian Osman1df161a2017-02-09 12:10:20 -05001443 double startTime = SkTime::GetMSecs();
jvanverthc265a922016-04-08 12:51:45 -07001444 fAnimTimer.updateTime();
Brian Osman1df161a2017-02-09 12:10:20 -05001445 bool animateWantsInval = fSlides[fCurrentSlide]->animate(fAnimTimer);
1446 fAnimateTimes[fCurrentMeasurement] = SkTime::GetMSecs() - startTime;
1447
Brian Osman79086b92017-02-10 13:36:16 -05001448 ImGuiIO& io = ImGui::GetIO();
1449 if (animateWantsInval || fDisplayStats || fRefresh || io.MetricsActiveWindows) {
jvanverthc265a922016-04-08 12:51:45 -07001450 fWindow->inval();
1451 }
jvanverth9f372462016-04-06 06:08:59 -07001452}
liyuqiane5a6cd92016-05-27 08:52:52 -07001453
1454void Viewer::updateUIState() {
csmartdalton578f0642017-02-24 16:04:47 -07001455 if (!fWindow) {
1456 return;
1457 }
1458 if (fWindow->sampleCount() < 0) {
1459 return; // Surface hasn't been created yet.
1460 }
1461
liyuqianb73c24b2016-06-03 08:47:23 -07001462 // Slide state
liyuqiane5a6cd92016-05-27 08:52:52 -07001463 Json::Value slideState(Json::objectValue);
1464 slideState[kName] = kSlideStateName;
1465 slideState[kValue] = fSlides[fCurrentSlide]->getName().c_str();
liyuqian1f508fd2016-06-07 06:57:40 -07001466 if (fAllSlideNames.size() == 0) {
1467 for(auto slide : fSlides) {
1468 fAllSlideNames.append(Json::Value(slide->getName().c_str()));
1469 }
liyuqiane5a6cd92016-05-27 08:52:52 -07001470 }
liyuqian1f508fd2016-06-07 06:57:40 -07001471 slideState[kOptions] = fAllSlideNames;
liyuqiane5a6cd92016-05-27 08:52:52 -07001472
liyuqianb73c24b2016-06-03 08:47:23 -07001473 // Backend state
liyuqiane5a6cd92016-05-27 08:52:52 -07001474 Json::Value backendState(Json::objectValue);
1475 backendState[kName] = kBackendStateName;
liyuqian6cb70252016-06-02 12:16:25 -07001476 backendState[kValue] = kBackendTypeStrings[fBackendType];
liyuqiane5a6cd92016-05-27 08:52:52 -07001477 backendState[kOptions] = Json::Value(Json::arrayValue);
liyuqianb73c24b2016-06-03 08:47:23 -07001478 for (auto str : kBackendTypeStrings) {
liyuqian6cb70252016-06-02 12:16:25 -07001479 backendState[kOptions].append(Json::Value(str));
1480 }
liyuqiane5a6cd92016-05-27 08:52:52 -07001481
csmartdalton578f0642017-02-24 16:04:47 -07001482 // MSAA state
1483 Json::Value msaaState(Json::objectValue);
1484 msaaState[kName] = kMSAAStateName;
1485 msaaState[kValue] = fWindow->sampleCount();
1486 msaaState[kOptions] = Json::Value(Json::arrayValue);
1487 if (sk_app::Window::kRaster_BackendType == fBackendType) {
1488 msaaState[kOptions].append(Json::Value(0));
1489 } else {
1490 for (int msaa : {0, 4, 8, 16}) {
1491 msaaState[kOptions].append(Json::Value(msaa));
1492 }
1493 }
1494
csmartdalton61cd31a2017-02-27 17:00:53 -07001495 // Path renderer state
1496 GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
1497 Json::Value prState(Json::objectValue);
1498 prState[kName] = kPathRendererStateName;
1499 prState[kValue] = gPathRendererNames[pr];
1500 prState[kOptions] = Json::Value(Json::arrayValue);
1501 const GrContext* ctx = fWindow->getGrContext();
1502 if (!ctx) {
1503 prState[kOptions].append("Software");
1504 } else if (fWindow->sampleCount()) {
Brian Osman8b0f2652017-08-29 15:18:34 -04001505 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kDefault]);
csmartdalton61cd31a2017-02-27 17:00:53 -07001506 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kAll]);
1507 if (ctx->caps()->shaderCaps()->pathRenderingSupport()) {
1508 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kStencilAndCover]);
1509 }
1510 if (ctx->caps()->sampleShadingSupport()) {
1511 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kMSAA]);
1512 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001513 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kTessellating]);
csmartdalton61cd31a2017-02-27 17:00:53 -07001514 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kNone]);
1515 } else {
Brian Osman8b0f2652017-08-29 15:18:34 -04001516 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kDefault]);
csmartdalton61cd31a2017-02-27 17:00:53 -07001517 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kAll]);
Chris Dalton1a325d22017-07-14 15:17:41 -06001518 if (GrCoverageCountingPathRenderer::IsSupported(*ctx->caps())) {
1519 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kCoverageCounting]);
1520 }
Jim Van Verth83010462017-03-16 08:45:39 -04001521 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kSmall]);
Brian Osman8a9de3d2017-03-01 14:59:05 -05001522 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kTessellating]);
csmartdalton61cd31a2017-02-27 17:00:53 -07001523 prState[kOptions].append(gPathRendererNames[GpuPathRenderers::kNone]);
1524 }
1525
Brian Salomon99a33902017-03-07 15:16:34 -05001526 // Instanced rendering state
1527 Json::Value instState(Json::objectValue);
1528 instState[kName] = kInstancedRenderingStateName;
Yuqian Lid9020422017-07-25 15:01:53 -04001529 instState[kValue] = kOFF;
1530 instState[kOptions] = Json::Value(Json::arrayValue);
Brian Salomon99a33902017-03-07 15:16:34 -05001531 if (ctx) {
1532 if (fWindow->getRequestedDisplayParams().fGrContextOptions.fEnableInstancedRendering) {
1533 instState[kValue] = kON;
Brian Salomon99a33902017-03-07 15:16:34 -05001534 }
Brian Salomon99a33902017-03-07 15:16:34 -05001535 instState[kOptions].append(kOFF);
1536 instState[kOptions].append(kON);
1537 }
1538
liyuqianb73c24b2016-06-03 08:47:23 -07001539 // Softkey state
1540 Json::Value softkeyState(Json::objectValue);
1541 softkeyState[kName] = kSoftkeyStateName;
1542 softkeyState[kValue] = kSoftkeyHint;
1543 softkeyState[kOptions] = Json::Value(Json::arrayValue);
1544 softkeyState[kOptions].append(kSoftkeyHint);
1545 for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
1546 softkeyState[kOptions].append(Json::Value(softkey.c_str()));
1547 }
1548
liyuqian1f508fd2016-06-07 06:57:40 -07001549 // FPS state
1550 Json::Value fpsState(Json::objectValue);
1551 fpsState[kName] = kFpsStateName;
Brian Osman1df161a2017-02-09 12:10:20 -05001552 int idx = (fCurrentMeasurement + (kMeasurementCount - 1)) & (kMeasurementCount - 1);
1553 fpsState[kValue] = SkStringPrintf("%8.3lf ms\n\nA %8.3lf\nP %8.3lf\nF%8.3lf",
1554 fAnimateTimes[idx] + fPaintTimes[idx] + fFlushTimes[idx],
1555 fAnimateTimes[idx],
1556 fPaintTimes[idx],
1557 fFlushTimes[idx]).c_str();
liyuqian1f508fd2016-06-07 06:57:40 -07001558 fpsState[kOptions] = Json::Value(Json::arrayValue);
1559
liyuqiane5a6cd92016-05-27 08:52:52 -07001560 Json::Value state(Json::arrayValue);
1561 state.append(slideState);
1562 state.append(backendState);
csmartdalton578f0642017-02-24 16:04:47 -07001563 state.append(msaaState);
csmartdalton61cd31a2017-02-27 17:00:53 -07001564 state.append(prState);
Brian Salomon99a33902017-03-07 15:16:34 -05001565 state.append(instState);
liyuqianb73c24b2016-06-03 08:47:23 -07001566 state.append(softkeyState);
liyuqian1f508fd2016-06-07 06:57:40 -07001567 state.append(fpsState);
liyuqiane5a6cd92016-05-27 08:52:52 -07001568
Brian Osmaneff04b52017-11-21 13:18:02 -05001569 fWindow->setUIState(state.toStyledString().c_str());
liyuqiane5a6cd92016-05-27 08:52:52 -07001570}
1571
1572void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
liyuqian6cb70252016-06-02 12:16:25 -07001573 // For those who will add more features to handle the state change in this function:
1574 // After the change, please call updateUIState no notify the frontend (e.g., Android app).
1575 // For example, after slide change, updateUIState is called inside setupCurrentSlide;
1576 // after backend change, updateUIState is called in this function.
liyuqiane5a6cd92016-05-27 08:52:52 -07001577 if (stateName.equals(kSlideStateName)) {
1578 int previousSlide = fCurrentSlide;
1579 fCurrentSlide = 0;
1580 for(auto slide : fSlides) {
1581 if (slide->getName().equals(stateValue)) {
Brian Salomon99a33902017-03-07 15:16:34 -05001582 this->setupCurrentSlide(previousSlide);
liyuqiane5a6cd92016-05-27 08:52:52 -07001583 break;
1584 }
1585 fCurrentSlide++;
1586 }
1587 if (fCurrentSlide >= fSlides.count()) {
1588 fCurrentSlide = previousSlide;
1589 SkDebugf("Slide not found: %s", stateValue.c_str());
1590 }
liyuqian6cb70252016-06-02 12:16:25 -07001591 } else if (stateName.equals(kBackendStateName)) {
1592 for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
1593 if (stateValue.equals(kBackendTypeStrings[i])) {
1594 if (fBackendType != i) {
1595 fBackendType = (sk_app::Window::BackendType)i;
1596 fWindow->detach();
Brian Osman70d2f432017-11-08 09:54:10 -05001597 fWindow->attach(backend_type_for_window(fBackendType));
liyuqian6cb70252016-06-02 12:16:25 -07001598 }
1599 break;
1600 }
1601 }
csmartdalton578f0642017-02-24 16:04:47 -07001602 } else if (stateName.equals(kMSAAStateName)) {
1603 DisplayParams params = fWindow->getRequestedDisplayParams();
1604 int sampleCount = atoi(stateValue.c_str());
1605 if (sampleCount != params.fMSAASampleCount) {
1606 params.fMSAASampleCount = sampleCount;
1607 fWindow->setRequestedDisplayParams(params);
1608 fWindow->inval();
Brian Salomon99a33902017-03-07 15:16:34 -05001609 this->updateTitle();
1610 this->updateUIState();
csmartdalton61cd31a2017-02-27 17:00:53 -07001611 }
1612 } else if (stateName.equals(kPathRendererStateName)) {
1613 DisplayParams params = fWindow->getRequestedDisplayParams();
1614 for (const auto& pair : gPathRendererNames) {
1615 if (pair.second == stateValue.c_str()) {
1616 if (params.fGrContextOptions.fGpuPathRenderers != pair.first) {
1617 params.fGrContextOptions.fGpuPathRenderers = pair.first;
1618 fWindow->setRequestedDisplayParams(params);
1619 fWindow->inval();
Brian Salomon99a33902017-03-07 15:16:34 -05001620 this->updateTitle();
1621 this->updateUIState();
csmartdalton61cd31a2017-02-27 17:00:53 -07001622 }
1623 break;
1624 }
csmartdalton578f0642017-02-24 16:04:47 -07001625 }
Brian Salomon99a33902017-03-07 15:16:34 -05001626 } else if (stateName.equals(kInstancedRenderingStateName)) {
1627 DisplayParams params = fWindow->getRequestedDisplayParams();
1628 bool value = !strcmp(stateValue.c_str(), kON);
1629 if (params.fGrContextOptions.fEnableInstancedRendering != value) {
1630 params.fGrContextOptions.fEnableInstancedRendering = value;
1631 fWindow->setRequestedDisplayParams(params);
1632 fWindow->inval();
1633 this->updateTitle();
1634 this->updateUIState();
1635 }
liyuqianb73c24b2016-06-03 08:47:23 -07001636 } else if (stateName.equals(kSoftkeyStateName)) {
1637 if (!stateValue.equals(kSoftkeyHint)) {
1638 fCommands.onSoftkey(stateValue);
Brian Salomon99a33902017-03-07 15:16:34 -05001639 this->updateUIState(); // This is still needed to reset the value to kSoftkeyHint
liyuqianb73c24b2016-06-03 08:47:23 -07001640 }
liyuqian2edb0f42016-07-06 14:11:32 -07001641 } else if (stateName.equals(kRefreshStateName)) {
1642 // This state is actually NOT in the UI state.
1643 // We use this to allow Android to quickly set bool fRefresh.
1644 fRefresh = stateValue.equals(kON);
liyuqiane5a6cd92016-05-27 08:52:52 -07001645 } else {
1646 SkDebugf("Unknown stateName: %s", stateName.c_str());
1647 }
1648}
Brian Osman79086b92017-02-10 13:36:16 -05001649
1650bool Viewer::onKey(sk_app::Window::Key key, sk_app::Window::InputState state, uint32_t modifiers) {
1651 return fCommands.onKey(key, state, modifiers);
1652}
1653
1654bool Viewer::onChar(SkUnichar c, uint32_t modifiers) {
Jim Van Verth6f449692017-02-14 15:16:46 -05001655 if (fSlides[fCurrentSlide]->onChar(c)) {
1656 fWindow->inval();
1657 return true;
1658 }
1659
Brian Osman79086b92017-02-10 13:36:16 -05001660 return fCommands.onChar(c, modifiers);
1661}