blob: 11667798b993b110f8b885d3a493fa8bead6e84d [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
9#include "include/core/SkData.h"
10#include "include/core/SkGraphics.h"
11#include "include/core/SkPictureRecorder.h"
12#include "include/core/SkStream.h"
13#include "include/core/SkSurface.h"
14#include "include/gpu/GrContext.h"
15#include "include/private/SkTo.h"
16#include "include/utils/SkPaintFilterCanvas.h"
17#include "src/core/SkColorSpacePriv.h"
18#include "src/core/SkImagePriv.h"
19#include "src/core/SkMD5.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050020#include "src/core/SkOSFile.h"
21#include "src/core/SkScan.h"
22#include "src/core/SkTaskGroup.h"
Robert Phillipse19babf2020-04-06 13:57:30 -040023#include "src/core/SkTextBlobPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/gpu/GrContextPriv.h"
25#include "src/gpu/GrGpu.h"
26#include "src/gpu/GrPersistentCacheUtils.h"
Chris Dalton77912982019-12-16 11:18:13 -070027#include "src/gpu/GrShaderUtils.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
29#include "src/utils/SkJSONWriter.h"
30#include "src/utils/SkOSPath.h"
31#include "tools/Resources.h"
32#include "tools/ToolUtils.h"
33#include "tools/flags/CommandLineFlags.h"
34#include "tools/flags/CommonFlags.h"
35#include "tools/trace/EventTracingPriv.h"
36#include "tools/viewer/BisectSlide.h"
37#include "tools/viewer/GMSlide.h"
38#include "tools/viewer/ImageSlide.h"
39#include "tools/viewer/ParticlesSlide.h"
40#include "tools/viewer/SKPSlide.h"
41#include "tools/viewer/SampleSlide.h"
Brian Osmand927bd22019-12-18 11:23:12 -050042#include "tools/viewer/SkSLSlide.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050043#include "tools/viewer/SlideDir.h"
44#include "tools/viewer/SvgSlide.h"
45#include "tools/viewer/Viewer.h"
csmartdalton578f0642017-02-24 16:04:47 -070046
Chris Dalton17dc4182020-03-25 16:18:16 -060047#include <cstdlib>
Hal Canaryc640d0d2018-06-13 09:59:02 -040048#include <map>
49
Hal Canary8a001442018-09-19 11:31:27 -040050#include "imgui.h"
Brian Osman0b8bb882019-04-12 11:47:19 -040051#include "misc/cpp/imgui_stdlib.h" // For ImGui support of std::string
Florin Malita3b526b02018-05-25 12:43:51 -040052
Florin Malita87ccf332018-05-04 12:23:24 -040053#if defined(SK_ENABLE_SKOTTIE)
Mike Kleinc0bd9f92019-04-23 12:05:21 -050054 #include "tools/viewer/SkottieSlide.h"
Florin Malita87ccf332018-05-04 12:23:24 -040055#endif
56
Brian Osman5e7fbfd2019-05-03 13:13:35 -040057class CapturingShaderErrorHandler : public GrContextOptions::ShaderErrorHandler {
58public:
59 void compileError(const char* shader, const char* errors) override {
60 fShaders.push_back(SkString(shader));
61 fErrors.push_back(SkString(errors));
62 }
63
64 void reset() {
65 fShaders.reset();
66 fErrors.reset();
67 }
68
69 SkTArray<SkString> fShaders;
70 SkTArray<SkString> fErrors;
71};
72
73static CapturingShaderErrorHandler gShaderErrorHandler;
74
jvanverth34524262016-05-04 13:49:13 -070075using namespace sk_app;
76
csmartdalton61cd31a2017-02-27 17:00:53 -070077static std::map<GpuPathRenderers, std::string> gPathRendererNames;
78
jvanverth9f372462016-04-06 06:08:59 -070079Application* Application::Create(int argc, char** argv, void* platformData) {
jvanverth34524262016-05-04 13:49:13 -070080 return new Viewer(argc, argv, platformData);
jvanverth9f372462016-04-06 06:08:59 -070081}
82
Chris Dalton7a0ebfc2017-10-13 12:35:50 -060083static DEFINE_string(slide, "", "Start on this sample.");
84static DEFINE_bool(list, false, "List samples?");
Jim Van Verth6f449692017-02-14 15:16:46 -050085
Stephen Whitea800ec92019-08-02 15:04:52 -040086#if defined(SK_VULKAN)
jvanverthb8794cc2016-07-27 14:29:18 -070087# define BACKENDS_STR "\"sw\", \"gl\", and \"vk\""
Jim Van Verthbe39f712019-02-08 15:36:14 -050088#elif defined(SK_METAL) && defined(SK_BUILD_FOR_MAC)
89# define BACKENDS_STR "\"sw\", \"gl\", and \"mtl\""
Stephen Whitea800ec92019-08-02 15:04:52 -040090#elif defined(SK_DAWN)
91# define BACKENDS_STR "\"sw\", \"gl\", and \"dawn\""
bsalomon6c471f72016-07-26 12:56:32 -070092#else
93# define BACKENDS_STR "\"sw\" and \"gl\""
94#endif
95
Brian Osman2dd96932016-10-18 15:33:53 -040096static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR ".");
bsalomon6c471f72016-07-26 12:56:32 -070097
Mike Klein5b3f3432019-03-21 11:42:21 -050098static DEFINE_int(msaa, 1, "Number of subpixel samples. 0 for no HW antialiasing.");
csmartdalton008b9d82017-02-22 12:00:42 -070099
Mike Klein84836b72019-03-21 11:31:36 -0500100static DEFINE_string(bisect, "", "Path to a .skp or .svg file to bisect.");
Chris Dalton2d18f412018-02-20 13:23:32 -0700101
Mike Klein84836b72019-03-21 11:31:36 -0500102static DEFINE_string2(file, f, "", "Open a single file for viewing.");
Florin Malita38792ce2018-05-08 10:36:18 -0400103
Mike Kleinc6142d82019-03-25 10:54:59 -0500104static DEFINE_string2(match, m, nullptr,
105 "[~][^]substring[$] [...] of name to run.\n"
106 "Multiple matches may be separated by spaces.\n"
107 "~ causes a matching name to always be skipped\n"
108 "^ requires the start of the name to match\n"
109 "$ requires the end of the name to match\n"
110 "^ and $ requires an exact match\n"
111 "If a name does not match any list entry,\n"
112 "it is skipped unless some list entry starts with ~");
113
Mike Klein19fb3972019-03-21 13:08:08 -0500114#if defined(SK_BUILD_FOR_ANDROID)
115 static DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from.");
Mike Kleinc6142d82019-03-25 10:54:59 -0500116 static DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from.");
117 static DEFINE_string(lotties, "/data/local/tmp/lotties",
118 "Directory to read (Bodymovin) jsons from.");
Mike Klein19fb3972019-03-21 13:08:08 -0500119#else
120 static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
Mike Kleinc6142d82019-03-25 10:54:59 -0500121 static DEFINE_string(skps, "skps", "Directory to read skps from.");
122 static DEFINE_string(lotties, "lotties", "Directory to read (Bodymovin) jsons from.");
Mike Klein19fb3972019-03-21 13:08:08 -0500123#endif
124
Mike Kleinc6142d82019-03-25 10:54:59 -0500125static DEFINE_string(svgs, "", "Directory to read SVGs from, or a single SVG file.");
126
127static DEFINE_int_2(threads, j, -1,
128 "Run threadsafe tests on a threadpool with this many extra threads, "
129 "defaulting to one extra thread per core.");
130
Jim Van Verth7b558182019-11-14 16:47:01 -0500131static DEFINE_bool(redraw, false, "Toggle continuous redraw.");
132
Chris Daltonc8877332020-01-06 09:48:30 -0700133static DEFINE_bool(offscreen, false, "Force rendering to an offscreen surface.");
Mike Reed862818b2020-03-21 15:07:13 -0400134static DEFINE_bool(skvm, false, "Try to use skvm blitters for raster.");
Mike Klein1e0884d2020-04-28 15:04:16 -0500135static DEFINE_bool(dylib, false, "JIT via dylib (much slower compile but easier to debug/profile)");
Mike Kleine42af162020-04-29 07:55:53 -0500136static DEFINE_bool(stats, false, "Display stats overlay on startup.");
Mike Kleinc6142d82019-03-25 10:54:59 -0500137
Brian Salomonf4ba4ec2020-03-19 15:54:28 -0400138#ifndef SK_GL
139static_assert(false, "viewer requires GL backend for raster.")
140#endif
141
Brian Salomon194db172017-08-17 14:37:06 -0400142const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
csmartdalton578f0642017-02-24 16:04:47 -0700143 "OpenGL",
Brian Salomon194db172017-08-17 14:37:06 -0400144#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
145 "ANGLE",
146#endif
Stephen Whitea800ec92019-08-02 15:04:52 -0400147#ifdef SK_DAWN
148 "Dawn",
149#endif
jvanverth063ece72016-06-17 09:29:14 -0700150#ifdef SK_VULKAN
csmartdalton578f0642017-02-24 16:04:47 -0700151 "Vulkan",
jvanverth063ece72016-06-17 09:29:14 -0700152#endif
Jim Van Verthe58d5322019-09-03 09:42:57 -0400153#ifdef SK_METAL
Jim Van Verthbe39f712019-02-08 15:36:14 -0500154 "Metal",
155#endif
csmartdalton578f0642017-02-24 16:04:47 -0700156 "Raster"
jvanverthaf236b52016-05-20 06:01:06 -0700157};
158
bsalomon6c471f72016-07-26 12:56:32 -0700159static sk_app::Window::BackendType get_backend_type(const char* str) {
Stephen Whitea800ec92019-08-02 15:04:52 -0400160#ifdef SK_DAWN
161 if (0 == strcmp(str, "dawn")) {
162 return sk_app::Window::kDawn_BackendType;
163 } else
164#endif
bsalomon6c471f72016-07-26 12:56:32 -0700165#ifdef SK_VULKAN
166 if (0 == strcmp(str, "vk")) {
167 return sk_app::Window::kVulkan_BackendType;
168 } else
169#endif
Brian Salomon194db172017-08-17 14:37:06 -0400170#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
171 if (0 == strcmp(str, "angle")) {
172 return sk_app::Window::kANGLE_BackendType;
173 } else
174#endif
Jim Van Verthe58d5322019-09-03 09:42:57 -0400175#ifdef SK_METAL
176 if (0 == strcmp(str, "mtl")) {
177 return sk_app::Window::kMetal_BackendType;
178 } else
Jim Van Verthbe39f712019-02-08 15:36:14 -0500179#endif
bsalomon6c471f72016-07-26 12:56:32 -0700180 if (0 == strcmp(str, "gl")) {
181 return sk_app::Window::kNativeGL_BackendType;
182 } else if (0 == strcmp(str, "sw")) {
183 return sk_app::Window::kRaster_BackendType;
184 } else {
185 SkDebugf("Unknown backend type, %s, defaulting to sw.", str);
186 return sk_app::Window::kRaster_BackendType;
187 }
188}
189
Brian Osmana109e392017-02-24 09:49:14 -0500190static SkColorSpacePrimaries gSrgbPrimaries = {
191 0.64f, 0.33f,
192 0.30f, 0.60f,
193 0.15f, 0.06f,
194 0.3127f, 0.3290f };
195
196static SkColorSpacePrimaries gAdobePrimaries = {
197 0.64f, 0.33f,
198 0.21f, 0.71f,
199 0.15f, 0.06f,
200 0.3127f, 0.3290f };
201
202static SkColorSpacePrimaries gP3Primaries = {
203 0.680f, 0.320f,
204 0.265f, 0.690f,
205 0.150f, 0.060f,
206 0.3127f, 0.3290f };
207
208static SkColorSpacePrimaries gRec2020Primaries = {
209 0.708f, 0.292f,
210 0.170f, 0.797f,
211 0.131f, 0.046f,
212 0.3127f, 0.3290f };
213
214struct NamedPrimaries {
215 const char* fName;
216 SkColorSpacePrimaries* fPrimaries;
217} gNamedPrimaries[] = {
218 { "sRGB", &gSrgbPrimaries },
219 { "AdobeRGB", &gAdobePrimaries },
220 { "P3", &gP3Primaries },
221 { "Rec. 2020", &gRec2020Primaries },
222};
223
224static bool primaries_equal(const SkColorSpacePrimaries& a, const SkColorSpacePrimaries& b) {
225 return memcmp(&a, &b, sizeof(SkColorSpacePrimaries)) == 0;
226}
227
Brian Osman70d2f432017-11-08 09:54:10 -0500228static Window::BackendType backend_type_for_window(Window::BackendType backendType) {
229 // In raster mode, we still use GL for the window.
230 // This lets us render the GUI faster (and correct).
231 return Window::kRaster_BackendType == backendType ? Window::kNativeGL_BackendType : backendType;
232}
233
Jim Van Verth74826c82019-03-01 14:37:30 -0500234class NullSlide : public Slide {
235 SkISize getDimensions() const override {
236 return SkISize::Make(640, 480);
237 }
238
239 void draw(SkCanvas* canvas) override {
240 canvas->clear(0xffff11ff);
241 }
242};
243
liyuqiane5a6cd92016-05-27 08:52:52 -0700244const char* kName = "name";
245const char* kValue = "value";
246const char* kOptions = "options";
247const char* kSlideStateName = "Slide";
248const char* kBackendStateName = "Backend";
csmartdalton578f0642017-02-24 16:04:47 -0700249const char* kMSAAStateName = "MSAA";
csmartdalton61cd31a2017-02-27 17:00:53 -0700250const char* kPathRendererStateName = "Path renderer";
liyuqianb73c24b2016-06-03 08:47:23 -0700251const char* kSoftkeyStateName = "Softkey";
252const char* kSoftkeyHint = "Please select a softkey";
liyuqian1f508fd2016-06-07 06:57:40 -0700253const char* kFpsStateName = "FPS";
liyuqian6f163d22016-06-13 12:26:45 -0700254const char* kON = "ON";
255const char* kOFF = "OFF";
liyuqian2edb0f42016-07-06 14:11:32 -0700256const char* kRefreshStateName = "Refresh";
liyuqiane5a6cd92016-05-27 08:52:52 -0700257
Mike Reed862818b2020-03-21 15:07:13 -0400258extern bool gUseSkVMBlitter;
Mike Klein1e0884d2020-04-28 15:04:16 -0500259extern bool gSkVMJITViaDylib;
Mike Reed862818b2020-03-21 15:07:13 -0400260
jvanverth34524262016-05-04 13:49:13 -0700261Viewer::Viewer(int argc, char** argv, void* platformData)
Florin Malitaab99c342018-01-16 16:23:03 -0500262 : fCurrentSlide(-1)
263 , fRefresh(false)
Brian Osman3ac99cf2017-12-01 11:23:53 -0500264 , fSaveToSKP(false)
Mike Reed376d8122019-03-14 11:39:02 -0400265 , fShowSlideDimensions(false)
Brian Osman79086b92017-02-10 13:36:16 -0500266 , fShowImGuiDebugWindow(false)
Brian Osmanfce09c52017-11-14 15:32:20 -0500267 , fShowSlidePicker(false)
Brian Osman79086b92017-02-10 13:36:16 -0500268 , fShowImGuiTestWindow(false)
Brian Osmanf6877092017-02-13 09:39:57 -0500269 , fShowZoomWindow(false)
Ben Wagner3627d2e2018-06-26 14:23:20 -0400270 , fZoomWindowFixed(false)
271 , fZoomWindowLocation{0.0f, 0.0f}
Brian Osmanf6877092017-02-13 09:39:57 -0500272 , fLastImage(nullptr)
Brian Osmanb63f6002018-07-24 18:01:53 -0400273 , fZoomUI(false)
jvanverth063ece72016-06-17 09:29:14 -0700274 , fBackendType(sk_app::Window::kNativeGL_BackendType)
Brian Osman92004802017-03-06 11:47:26 -0500275 , fColorMode(ColorMode::kLegacy)
Brian Osmana109e392017-02-24 09:49:14 -0500276 , fColorSpacePrimaries(gSrgbPrimaries)
Brian Osmanfdab5762017-11-09 10:27:55 -0500277 // Our UI can only tweak gamma (currently), so start out gamma-only
Brian Osman82ebe042019-01-04 17:03:00 -0500278 , fColorSpaceTransferFn(SkNamedTransferFn::k2Dot2)
egdaniel2a0bb0a2016-04-11 08:30:40 -0700279 , fZoomLevel(0.0f)
Ben Wagnerd02a74d2018-04-23 12:55:06 -0400280 , fRotation(0.0f)
Ben Wagner897dfa22018-08-09 15:18:46 -0400281 , fOffset{0.5f, 0.5f}
Brian Osmanb53f48c2017-06-07 10:00:30 -0400282 , fGestureDevice(GestureDevice::kNone)
Brian Osmane9ed0f02018-11-26 14:50:05 -0500283 , fTiled(false)
284 , fDrawTileBoundaries(false)
285 , fTileScale{0.25f, 0.25f}
Brian Osman805a7272018-05-02 15:40:20 -0400286 , fPerspectiveMode(kPerspective_Off)
jvanverthc265a922016-04-08 12:51:45 -0700287{
Greg Daniel285db442016-10-14 09:12:53 -0400288 SkGraphics::Init();
csmartdalton61cd31a2017-02-27 17:00:53 -0700289
Chris Dalton37ae4b02019-12-28 14:51:11 -0700290 gPathRendererNames[GpuPathRenderers::kDefault] = "Default Path Renderers";
Chris Dalton0a22b1e2020-03-26 11:52:15 -0600291 gPathRendererNames[GpuPathRenderers::kTessellation] = "Tessellation";
Brian Osmanf09e35e2017-12-15 14:48:09 -0500292 gPathRendererNames[GpuPathRenderers::kStencilAndCover] = "NV_path_rendering";
Brian Osmanf09e35e2017-12-15 14:48:09 -0500293 gPathRendererNames[GpuPathRenderers::kSmall] = "Small paths (cached sdf or alpha masks)";
Chris Daltonc3318f02019-07-19 14:20:53 -0600294 gPathRendererNames[GpuPathRenderers::kCoverageCounting] = "CCPR";
Chris Dalton17dc4182020-03-25 16:18:16 -0600295 gPathRendererNames[GpuPathRenderers::kTriangulating] = "Triangulating";
Brian Osmanf09e35e2017-12-15 14:48:09 -0500296 gPathRendererNames[GpuPathRenderers::kNone] = "Software masks";
csmartdalton61cd31a2017-02-27 17:00:53 -0700297
jvanverth2bb3b6d2016-04-08 07:24:09 -0700298 SkDebugf("Command line arguments: ");
299 for (int i = 1; i < argc; ++i) {
300 SkDebugf("%s ", argv[i]);
301 }
302 SkDebugf("\n");
303
Mike Klein88544fb2019-03-20 10:50:33 -0500304 CommandLineFlags::Parse(argc, argv);
Greg Daniel9fcc7432016-11-29 16:35:19 -0500305#ifdef SK_BUILD_FOR_ANDROID
Brian Salomon96789b32017-05-26 12:06:21 -0400306 SetResourcePath("/data/local/tmp/resources");
Greg Daniel9fcc7432016-11-29 16:35:19 -0500307#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -0700308
Mike Reed862818b2020-03-21 15:07:13 -0400309 gUseSkVMBlitter = FLAGS_skvm;
Mike Klein1e0884d2020-04-28 15:04:16 -0500310 gSkVMJITViaDylib = FLAGS_dylib;
Mike Reed862818b2020-03-21 15:07:13 -0400311
Mike Klein19cc0f62019-03-22 15:30:07 -0500312 ToolUtils::SetDefaultFontMgr();
Ben Wagner483c7722018-02-20 17:06:07 -0500313
Brian Osmanbc8150f2017-07-24 11:38:01 -0400314 initializeEventTracingForTools();
Brian Osman53136aa2017-07-20 15:43:35 -0400315 static SkTaskGroup::Enabler kTaskGroupEnabler(FLAGS_threads);
Greg Daniel285db442016-10-14 09:12:53 -0400316
bsalomon6c471f72016-07-26 12:56:32 -0700317 fBackendType = get_backend_type(FLAGS_backend[0]);
jvanverth9f372462016-04-06 06:08:59 -0700318 fWindow = Window::CreateNativeWindow(platformData);
jvanverth9f372462016-04-06 06:08:59 -0700319
csmartdalton578f0642017-02-24 16:04:47 -0700320 DisplayParams displayParams;
321 displayParams.fMSAASampleCount = FLAGS_msaa;
Chris Dalton040238b2017-12-18 14:22:34 -0700322 SetCtxOptionsFromCommonFlags(&displayParams.fGrContextOptions);
Brian Osman0b8bb882019-04-12 11:47:19 -0400323 displayParams.fGrContextOptions.fPersistentCache = &fPersistentCache;
Brian Osmana66081d2019-09-03 14:59:26 -0400324 displayParams.fGrContextOptions.fShaderCacheStrategy =
325 GrContextOptions::ShaderCacheStrategy::kBackendSource;
Brian Osman5e7fbfd2019-05-03 13:13:35 -0400326 displayParams.fGrContextOptions.fShaderErrorHandler = &gShaderErrorHandler;
327 displayParams.fGrContextOptions.fSuppressPrints = true;
csmartdalton578f0642017-02-24 16:04:47 -0700328 fWindow->setRequestedDisplayParams(displayParams);
Jim Van Verth7b558182019-11-14 16:47:01 -0500329 fRefresh = FLAGS_redraw;
csmartdalton578f0642017-02-24 16:04:47 -0700330
Brian Osman56a24812017-12-19 11:15:16 -0500331 // Configure timers
Mike Kleine42af162020-04-29 07:55:53 -0500332 fStatsLayer.setActive(FLAGS_stats);
Brian Osman56a24812017-12-19 11:15:16 -0500333 fAnimateTimer = fStatsLayer.addTimer("Animate", SK_ColorMAGENTA, 0xffff66ff);
334 fPaintTimer = fStatsLayer.addTimer("Paint", SK_ColorGREEN);
335 fFlushTimer = fStatsLayer.addTimer("Flush", SK_ColorRED, 0xffff6666);
336
jvanverth9f372462016-04-06 06:08:59 -0700337 // register callbacks
brianosman622c8d52016-05-10 06:50:49 -0700338 fCommands.attach(fWindow);
Brian Osman80fc07e2017-12-08 16:45:43 -0500339 fWindow->pushLayer(this);
Brian Osman56a24812017-12-19 11:15:16 -0500340 fWindow->pushLayer(&fStatsLayer);
Brian Osmand67e5182017-12-08 16:46:09 -0500341 fWindow->pushLayer(&fImGuiLayer);
jvanverth9f372462016-04-06 06:08:59 -0700342
brianosman622c8d52016-05-10 06:50:49 -0700343 // add key-bindings
Brian Osman79086b92017-02-10 13:36:16 -0500344 fCommands.addCommand(' ', "GUI", "Toggle Debug GUI", [this]() {
345 this->fShowImGuiDebugWindow = !this->fShowImGuiDebugWindow;
346 fWindow->inval();
347 });
Brian Osmanfce09c52017-11-14 15:32:20 -0500348 // Command to jump directly to the slide picker and give it focus
349 fCommands.addCommand('/', "GUI", "Jump to slide picker", [this]() {
350 this->fShowImGuiDebugWindow = true;
351 this->fShowSlidePicker = true;
352 fWindow->inval();
353 });
354 // Alias that to Backspace, to match SampleApp
Hal Canaryb1f411a2019-08-29 10:39:22 -0400355 fCommands.addCommand(skui::Key::kBack, "Backspace", "GUI", "Jump to slide picker", [this]() {
Brian Osmanfce09c52017-11-14 15:32:20 -0500356 this->fShowImGuiDebugWindow = true;
357 this->fShowSlidePicker = true;
358 fWindow->inval();
359 });
Brian Osman79086b92017-02-10 13:36:16 -0500360 fCommands.addCommand('g', "GUI", "Toggle GUI Demo", [this]() {
361 this->fShowImGuiTestWindow = !this->fShowImGuiTestWindow;
362 fWindow->inval();
363 });
Brian Osmanf6877092017-02-13 09:39:57 -0500364 fCommands.addCommand('z', "GUI", "Toggle zoom window", [this]() {
365 this->fShowZoomWindow = !this->fShowZoomWindow;
366 fWindow->inval();
367 });
Ben Wagner3627d2e2018-06-26 14:23:20 -0400368 fCommands.addCommand('Z', "GUI", "Toggle zoom window state", [this]() {
369 this->fZoomWindowFixed = !this->fZoomWindowFixed;
370 fWindow->inval();
371 });
Greg Danield0794cc2019-03-27 16:23:26 -0400372 fCommands.addCommand('v', "VSync", "Toggle vsync on/off", [this]() {
373 DisplayParams params = fWindow->getRequestedDisplayParams();
374 params.fDisableVsync = !params.fDisableVsync;
375 fWindow->setRequestedDisplayParams(params);
376 this->updateTitle();
377 fWindow->inval();
378 });
Mike Reedf702ed42019-07-22 17:00:49 -0400379 fCommands.addCommand('r', "Redraw", "Toggle redraw", [this]() {
380 fRefresh = !fRefresh;
381 fWindow->inval();
382 });
brianosman622c8d52016-05-10 06:50:49 -0700383 fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
Brian Osman56a24812017-12-19 11:15:16 -0500384 fStatsLayer.setActive(!fStatsLayer.getActive());
brianosman622c8d52016-05-10 06:50:49 -0700385 fWindow->inval();
386 });
Jim Van Verth90dcce52017-11-03 13:36:07 -0400387 fCommands.addCommand('0', "Overlays", "Reset stats", [this]() {
Brian Osman56a24812017-12-19 11:15:16 -0500388 fStatsLayer.resetMeasurements();
Jim Van Verth90dcce52017-11-03 13:36:07 -0400389 this->updateTitle();
390 fWindow->inval();
391 });
Brian Osmanf750fbc2017-02-08 10:47:28 -0500392 fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() {
Brian Osman92004802017-03-06 11:47:26 -0500393 switch (fColorMode) {
394 case ColorMode::kLegacy:
Brian Osman03115dc2018-11-26 13:55:19 -0500395 this->setColorMode(ColorMode::kColorManaged8888);
Brian Osman92004802017-03-06 11:47:26 -0500396 break;
Brian Osman03115dc2018-11-26 13:55:19 -0500397 case ColorMode::kColorManaged8888:
398 this->setColorMode(ColorMode::kColorManagedF16);
Brian Osman92004802017-03-06 11:47:26 -0500399 break;
Brian Osman03115dc2018-11-26 13:55:19 -0500400 case ColorMode::kColorManagedF16:
Brian Salomon8391bac2019-09-18 11:22:44 -0400401 this->setColorMode(ColorMode::kColorManagedF16Norm);
402 break;
403 case ColorMode::kColorManagedF16Norm:
Brian Osman92004802017-03-06 11:47:26 -0500404 this->setColorMode(ColorMode::kLegacy);
405 break;
Brian Osmanf750fbc2017-02-08 10:47:28 -0500406 }
brianosman622c8d52016-05-10 06:50:49 -0700407 });
Chris Dalton1215cda2019-12-17 21:44:04 -0700408 fCommands.addCommand('w', "Modes", "Toggle wireframe", [this]() {
409 DisplayParams params = fWindow->getRequestedDisplayParams();
410 params.fGrContextOptions.fWireframeMode = !params.fGrContextOptions.fWireframeMode;
411 fWindow->setRequestedDisplayParams(params);
412 fWindow->inval();
413 });
Hal Canaryb1f411a2019-08-29 10:39:22 -0400414 fCommands.addCommand(skui::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
Florin Malitaab99c342018-01-16 16:23:03 -0500415 this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ? fCurrentSlide + 1 : 0);
brianosman622c8d52016-05-10 06:50:49 -0700416 });
Hal Canaryb1f411a2019-08-29 10:39:22 -0400417 fCommands.addCommand(skui::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
Florin Malitaab99c342018-01-16 16:23:03 -0500418 this->setCurrentSlide(fCurrentSlide > 0 ? fCurrentSlide - 1 : fSlides.count() - 1);
brianosman622c8d52016-05-10 06:50:49 -0700419 });
Hal Canaryb1f411a2019-08-29 10:39:22 -0400420 fCommands.addCommand(skui::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
brianosman622c8d52016-05-10 06:50:49 -0700421 this->changeZoomLevel(1.f / 32.f);
422 fWindow->inval();
423 });
Hal Canaryb1f411a2019-08-29 10:39:22 -0400424 fCommands.addCommand(skui::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
brianosman622c8d52016-05-10 06:50:49 -0700425 this->changeZoomLevel(-1.f / 32.f);
426 fWindow->inval();
427 });
jvanverthaf236b52016-05-20 06:01:06 -0700428 fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
Brian Salomon194db172017-08-17 14:37:06 -0400429 sk_app::Window::BackendType newBackend = (sk_app::Window::BackendType)(
430 (fBackendType + 1) % sk_app::Window::kBackendTypeCount);
Jim Van Verthd63c1022017-01-05 13:50:49 -0500431 // Switching to and from Vulkan is problematic on Linux so disabled for now
Brian Salomon194db172017-08-17 14:37:06 -0400432#if defined(SK_BUILD_FOR_UNIX) && defined(SK_VULKAN)
433 if (newBackend == sk_app::Window::kVulkan_BackendType) {
434 newBackend = (sk_app::Window::BackendType)((newBackend + 1) %
435 sk_app::Window::kBackendTypeCount);
436 } else if (fBackendType == sk_app::Window::kVulkan_BackendType) {
437 newBackend = sk_app::Window::kVulkan_BackendType;
Jim Van Verthd63c1022017-01-05 13:50:49 -0500438 }
439#endif
Brian Osman621491e2017-02-28 15:45:01 -0500440 this->setBackend(newBackend);
jvanverthaf236b52016-05-20 06:01:06 -0700441 });
Brian Osman3ac99cf2017-12-01 11:23:53 -0500442 fCommands.addCommand('K', "IO", "Save slide to SKP", [this]() {
443 fSaveToSKP = true;
444 fWindow->inval();
445 });
Mike Reed376d8122019-03-14 11:39:02 -0400446 fCommands.addCommand('&', "Overlays", "Show slide dimensios", [this]() {
447 fShowSlideDimensions = !fShowSlideDimensions;
448 fWindow->inval();
449 });
Ben Wagner37c54032018-04-13 14:30:23 -0400450 fCommands.addCommand('G', "Modes", "Geometry", [this]() {
451 DisplayParams params = fWindow->getRequestedDisplayParams();
452 uint32_t flags = params.fSurfaceProps.flags();
453 if (!fPixelGeometryOverrides) {
454 fPixelGeometryOverrides = true;
455 params.fSurfaceProps = SkSurfaceProps(flags, kUnknown_SkPixelGeometry);
456 } else {
457 switch (params.fSurfaceProps.pixelGeometry()) {
458 case kUnknown_SkPixelGeometry:
459 params.fSurfaceProps = SkSurfaceProps(flags, kRGB_H_SkPixelGeometry);
460 break;
461 case kRGB_H_SkPixelGeometry:
462 params.fSurfaceProps = SkSurfaceProps(flags, kBGR_H_SkPixelGeometry);
463 break;
464 case kBGR_H_SkPixelGeometry:
465 params.fSurfaceProps = SkSurfaceProps(flags, kRGB_V_SkPixelGeometry);
466 break;
467 case kRGB_V_SkPixelGeometry:
468 params.fSurfaceProps = SkSurfaceProps(flags, kBGR_V_SkPixelGeometry);
469 break;
470 case kBGR_V_SkPixelGeometry:
471 params.fSurfaceProps = SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
472 fPixelGeometryOverrides = false;
473 break;
474 }
475 }
476 fWindow->setRequestedDisplayParams(params);
477 this->updateTitle();
478 fWindow->inval();
479 });
Ben Wagner9613e452019-01-23 10:34:59 -0500480 fCommands.addCommand('H', "Font", "Hinting mode", [this]() {
Mike Reed3ae47332019-01-04 10:11:46 -0500481 if (!fFontOverrides.fHinting) {
482 fFontOverrides.fHinting = true;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400483 fFont.setHinting(SkFontHinting::kNone);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500484 } else {
Mike Reed3ae47332019-01-04 10:11:46 -0500485 switch (fFont.getHinting()) {
Ben Wagner5785e4a2019-05-07 16:50:29 -0400486 case SkFontHinting::kNone:
487 fFont.setHinting(SkFontHinting::kSlight);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500488 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400489 case SkFontHinting::kSlight:
490 fFont.setHinting(SkFontHinting::kNormal);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500491 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400492 case SkFontHinting::kNormal:
493 fFont.setHinting(SkFontHinting::kFull);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500494 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400495 case SkFontHinting::kFull:
496 fFont.setHinting(SkFontHinting::kNone);
Mike Reed3ae47332019-01-04 10:11:46 -0500497 fFontOverrides.fHinting = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500498 break;
499 }
500 }
501 this->updateTitle();
502 fWindow->inval();
503 });
504 fCommands.addCommand('A', "Paint", "Antialias Mode", [this]() {
Ben Wagner9613e452019-01-23 10:34:59 -0500505 if (!fPaintOverrides.fAntiAlias) {
506 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
507 fPaintOverrides.fAntiAlias = true;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500508 fPaint.setAntiAlias(false);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500509 gSkUseAnalyticAA = gSkForceAnalyticAA = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500510 } else {
511 fPaint.setAntiAlias(true);
Ben Wagner9613e452019-01-23 10:34:59 -0500512 switch (fPaintOverrides.fAntiAliasState) {
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500513 case SkPaintFields::AntiAliasState::Alias:
Ben Wagner9613e452019-01-23 10:34:59 -0500514 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Normal;
Ben Wagnera580fb32018-04-17 11:16:32 -0400515 gSkUseAnalyticAA = gSkForceAnalyticAA = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500516 break;
517 case SkPaintFields::AntiAliasState::Normal:
Ben Wagner9613e452019-01-23 10:34:59 -0500518 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::AnalyticAAEnabled;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500519 gSkUseAnalyticAA = true;
Ben Wagnera580fb32018-04-17 11:16:32 -0400520 gSkForceAnalyticAA = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500521 break;
522 case SkPaintFields::AntiAliasState::AnalyticAAEnabled:
Ben Wagner9613e452019-01-23 10:34:59 -0500523 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::AnalyticAAForced;
Ben Wagnera580fb32018-04-17 11:16:32 -0400524 gSkUseAnalyticAA = gSkForceAnalyticAA = true;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500525 break;
526 case SkPaintFields::AntiAliasState::AnalyticAAForced:
Ben Wagner9613e452019-01-23 10:34:59 -0500527 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
528 fPaintOverrides.fAntiAlias = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500529 gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA;
530 gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500531 break;
532 }
533 }
534 this->updateTitle();
535 fWindow->inval();
536 });
Ben Wagner37c54032018-04-13 14:30:23 -0400537 fCommands.addCommand('D', "Modes", "DFT", [this]() {
538 DisplayParams params = fWindow->getRequestedDisplayParams();
539 uint32_t flags = params.fSurfaceProps.flags();
540 flags ^= SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
541 params.fSurfaceProps = SkSurfaceProps(flags, params.fSurfaceProps.pixelGeometry());
542 fWindow->setRequestedDisplayParams(params);
543 this->updateTitle();
544 fWindow->inval();
545 });
Ben Wagner9613e452019-01-23 10:34:59 -0500546 fCommands.addCommand('L', "Font", "Subpixel Antialias Mode", [this]() {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500547 if (!fFontOverrides.fEdging) {
548 fFontOverrides.fEdging = true;
549 fFont.setEdging(SkFont::Edging::kAlias);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500550 } else {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500551 switch (fFont.getEdging()) {
552 case SkFont::Edging::kAlias:
553 fFont.setEdging(SkFont::Edging::kAntiAlias);
554 break;
555 case SkFont::Edging::kAntiAlias:
556 fFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
557 break;
558 case SkFont::Edging::kSubpixelAntiAlias:
559 fFont.setEdging(SkFont::Edging::kAlias);
560 fFontOverrides.fEdging = false;
561 break;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500562 }
563 }
564 this->updateTitle();
565 fWindow->inval();
566 });
Ben Wagner9613e452019-01-23 10:34:59 -0500567 fCommands.addCommand('S', "Font", "Subpixel Position Mode", [this]() {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500568 if (!fFontOverrides.fSubpixel) {
569 fFontOverrides.fSubpixel = true;
570 fFont.setSubpixel(false);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500571 } else {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500572 if (!fFont.isSubpixel()) {
573 fFont.setSubpixel(true);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500574 } else {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500575 fFontOverrides.fSubpixel = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500576 }
577 }
578 this->updateTitle();
579 fWindow->inval();
580 });
Ben Wagner54aa8842019-08-27 16:20:39 -0400581 fCommands.addCommand('B', "Font", "Baseline Snapping", [this]() {
582 if (!fFontOverrides.fBaselineSnap) {
583 fFontOverrides.fBaselineSnap = true;
584 fFont.setBaselineSnap(false);
585 } else {
586 if (!fFont.isBaselineSnap()) {
587 fFont.setBaselineSnap(true);
588 } else {
589 fFontOverrides.fBaselineSnap = false;
590 }
591 }
592 this->updateTitle();
593 fWindow->inval();
594 });
Brian Osman805a7272018-05-02 15:40:20 -0400595 fCommands.addCommand('p', "Transform", "Toggle Perspective Mode", [this]() {
596 fPerspectiveMode = (kPerspective_Real == fPerspectiveMode) ? kPerspective_Fake
597 : kPerspective_Real;
598 this->updateTitle();
599 fWindow->inval();
600 });
601 fCommands.addCommand('P', "Transform", "Toggle Perspective", [this]() {
602 fPerspectiveMode = (kPerspective_Off == fPerspectiveMode) ? kPerspective_Real
603 : kPerspective_Off;
604 this->updateTitle();
605 fWindow->inval();
606 });
Brian Osman207d4102019-01-10 09:40:58 -0500607 fCommands.addCommand('a', "Transform", "Toggle Animation", [this]() {
608 fAnimTimer.togglePauseResume();
609 });
Brian Osmanb63f6002018-07-24 18:01:53 -0400610 fCommands.addCommand('u', "GUI", "Zoom UI", [this]() {
611 fZoomUI = !fZoomUI;
612 fStatsLayer.setDisplayScale(fZoomUI ? 2.0f : 1.0f);
613 fWindow->inval();
614 });
Mike Reed59295352020-03-12 13:56:34 -0400615 fCommands.addCommand('$', "ViaSerialize", "Toggle ViaSerialize", [this]() {
616 fDrawViaSerialize = !fDrawViaSerialize;
617 this->updateTitle();
618 fWindow->inval();
619 });
Mike Reed862818b2020-03-21 15:07:13 -0400620 fCommands.addCommand('!', "SkVM", "Toggle SkVM", [this]() {
621 gUseSkVMBlitter = !gUseSkVMBlitter;
622 this->updateTitle();
623 fWindow->inval();
624 });
Yuqian Lib2ba6642017-11-22 12:07:41 -0500625
jvanverth2bb3b6d2016-04-08 07:24:09 -0700626 // set up slides
627 this->initSlides();
Jim Van Verth6f449692017-02-14 15:16:46 -0500628 if (FLAGS_list) {
629 this->listNames();
630 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700631
Brian Osman9bb47cf2018-04-26 15:55:00 -0400632 fPerspectivePoints[0].set(0, 0);
633 fPerspectivePoints[1].set(1, 0);
634 fPerspectivePoints[2].set(0, 1);
635 fPerspectivePoints[3].set(1, 1);
djsollen12d62a72016-04-21 07:59:44 -0700636 fAnimTimer.run();
637
Hal Canaryc465d132017-12-08 10:21:31 -0500638 auto gamutImage = GetResourceAsImage("images/gamut.png");
Brian Osmana109e392017-02-24 09:49:14 -0500639 if (gamutImage) {
Mike Reed0acd7952017-04-28 11:12:19 -0400640 fImGuiGamutPaint.setShader(gamutImage->makeShader());
Brian Osmana109e392017-02-24 09:49:14 -0500641 }
642 fImGuiGamutPaint.setColor(SK_ColorWHITE);
643 fImGuiGamutPaint.setFilterQuality(kLow_SkFilterQuality);
644
jongdeok.kim804f17e2019-02-26 14:39:23 +0900645 fWindow->attach(backend_type_for_window(fBackendType));
Jim Van Verth74826c82019-03-01 14:37:30 -0500646 this->setCurrentSlide(this->startupSlide());
jvanverth9f372462016-04-06 06:08:59 -0700647}
648
jvanverth34524262016-05-04 13:49:13 -0700649void Viewer::initSlides() {
Florin Malita0ffa3222018-04-05 14:34:45 -0400650 using SlideFactory = sk_sp<Slide>(*)(const SkString& name, const SkString& path);
651 static const struct {
652 const char* fExtension;
653 const char* fDirName;
Mike Klein88544fb2019-03-20 10:50:33 -0500654 const CommandLineFlags::StringArray& fFlags;
Florin Malita0ffa3222018-04-05 14:34:45 -0400655 const SlideFactory fFactory;
656 } gExternalSlidesInfo[] = {
657 { ".skp", "skp-dir", FLAGS_skps,
658 [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
659 return sk_make_sp<SKPSlide>(name, path);}
660 },
661 { ".jpg", "jpg-dir", FLAGS_jpgs,
662 [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
663 return sk_make_sp<ImageSlide>(name, path);}
664 },
Florin Malita87ccf332018-05-04 12:23:24 -0400665#if defined(SK_ENABLE_SKOTTIE)
Eric Boren8c172ba2018-07-19 13:27:49 -0400666 { ".json", "skottie-dir", FLAGS_lotties,
Florin Malita0ffa3222018-04-05 14:34:45 -0400667 [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
668 return sk_make_sp<SkottieSlide>(name, path);}
669 },
Florin Malita87ccf332018-05-04 12:23:24 -0400670#endif
Florin Malita5d3ff432018-07-31 16:38:43 -0400671#if defined(SK_XML)
Florin Malita0ffa3222018-04-05 14:34:45 -0400672 { ".svg", "svg-dir", FLAGS_svgs,
673 [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
674 return sk_make_sp<SvgSlide>(name, path);}
675 },
Florin Malita5d3ff432018-07-31 16:38:43 -0400676#endif
Florin Malita0ffa3222018-04-05 14:34:45 -0400677 };
jvanverthc265a922016-04-08 12:51:45 -0700678
Brian Salomon343553a2018-09-05 15:41:23 -0400679 SkTArray<sk_sp<Slide>> dirSlides;
jvanverthc265a922016-04-08 12:51:45 -0700680
Mike Klein88544fb2019-03-20 10:50:33 -0500681 const auto addSlide =
682 [&](const SkString& name, const SkString& path, const SlideFactory& fact) {
683 if (CommandLineFlags::ShouldSkip(FLAGS_match, name.c_str())) {
684 return;
685 }
liyuqian6f163d22016-06-13 12:26:45 -0700686
Mike Klein88544fb2019-03-20 10:50:33 -0500687 if (auto slide = fact(name, path)) {
688 dirSlides.push_back(slide);
689 fSlides.push_back(std::move(slide));
690 }
691 };
Florin Malita76a076b2018-02-15 18:40:48 -0500692
Florin Malita38792ce2018-05-08 10:36:18 -0400693 if (!FLAGS_file.isEmpty()) {
694 // single file mode
695 const SkString file(FLAGS_file[0]);
696
697 if (sk_exists(file.c_str(), kRead_SkFILE_Flag)) {
698 for (const auto& sinfo : gExternalSlidesInfo) {
699 if (file.endsWith(sinfo.fExtension)) {
700 addSlide(SkOSPath::Basename(file.c_str()), file, sinfo.fFactory);
701 return;
702 }
703 }
704
705 fprintf(stderr, "Unsupported file type \"%s\"\n", file.c_str());
706 } else {
707 fprintf(stderr, "Cannot read \"%s\"\n", file.c_str());
708 }
709
710 return;
711 }
712
713 // Bisect slide.
714 if (!FLAGS_bisect.isEmpty()) {
715 sk_sp<BisectSlide> bisect = BisectSlide::Create(FLAGS_bisect[0]);
Mike Klein88544fb2019-03-20 10:50:33 -0500716 if (bisect && !CommandLineFlags::ShouldSkip(FLAGS_match, bisect->getName().c_str())) {
Florin Malita38792ce2018-05-08 10:36:18 -0400717 if (FLAGS_bisect.count() >= 2) {
718 for (const char* ch = FLAGS_bisect[1]; *ch; ++ch) {
719 bisect->onChar(*ch);
720 }
721 }
722 fSlides.push_back(std::move(bisect));
723 }
724 }
725
726 // GMs
727 int firstGM = fSlides.count();
Hal Canary972eba32018-07-30 17:07:07 -0400728 for (skiagm::GMFactory gmFactory : skiagm::GMRegistry::Range()) {
Ben Wagner406ff502019-08-12 16:39:24 -0400729 std::unique_ptr<skiagm::GM> gm = gmFactory();
Mike Klein88544fb2019-03-20 10:50:33 -0500730 if (!CommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
Ben Wagner406ff502019-08-12 16:39:24 -0400731 sk_sp<Slide> slide(new GMSlide(std::move(gm)));
Florin Malita38792ce2018-05-08 10:36:18 -0400732 fSlides.push_back(std::move(slide));
733 }
Florin Malita38792ce2018-05-08 10:36:18 -0400734 }
735 // reverse gms
736 int numGMs = fSlides.count() - firstGM;
737 for (int i = 0; i < numGMs/2; ++i) {
738 std::swap(fSlides[firstGM + i], fSlides[fSlides.count() - i - 1]);
739 }
740
741 // samples
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400742 for (const SampleFactory factory : SampleRegistry::Range()) {
743 sk_sp<Slide> slide(new SampleSlide(factory));
Mike Klein88544fb2019-03-20 10:50:33 -0500744 if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
Florin Malita38792ce2018-05-08 10:36:18 -0400745 fSlides.push_back(slide);
746 }
Florin Malita38792ce2018-05-08 10:36:18 -0400747 }
748
Brian Osman7c979f52019-02-12 13:27:51 -0500749 // Particle demo
750 {
751 // TODO: Convert this to a sample
752 sk_sp<Slide> slide(new ParticlesSlide());
Mike Klein88544fb2019-03-20 10:50:33 -0500753 if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
Brian Osman7c979f52019-02-12 13:27:51 -0500754 fSlides.push_back(std::move(slide));
755 }
756 }
757
Brian Osmand927bd22019-12-18 11:23:12 -0500758 // Runtime shader editor
759 {
760 sk_sp<Slide> slide(new SkSLSlide());
761 if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
762 fSlides.push_back(std::move(slide));
763 }
764 }
765
Florin Malita0ffa3222018-04-05 14:34:45 -0400766 for (const auto& info : gExternalSlidesInfo) {
767 for (const auto& flag : info.fFlags) {
768 if (SkStrEndsWith(flag.c_str(), info.fExtension)) {
769 // single file
770 addSlide(SkOSPath::Basename(flag.c_str()), flag, info.fFactory);
771 } else {
772 // directory
Florin Malita0ffa3222018-04-05 14:34:45 -0400773 SkString name;
Tyler Denniston31dc4812020-04-09 11:17:21 -0400774 SkTArray<SkString> sortedFilenames;
775 SkOSFile::Iter it(flag.c_str(), info.fExtension);
Florin Malita0ffa3222018-04-05 14:34:45 -0400776 while (it.next(&name)) {
Tyler Denniston31dc4812020-04-09 11:17:21 -0400777 sortedFilenames.push_back(name);
778 }
779 if (sortedFilenames.count()) {
780 SkTQSort(sortedFilenames.begin(), sortedFilenames.end() - 1,
781 [](const SkString& a, const SkString& b) {
782 return strcmp(a.c_str(), b.c_str()) < 0;
783 });
784 }
785 for (const SkString& filename : sortedFilenames) {
786 addSlide(filename, SkOSPath::Join(flag.c_str(), filename.c_str()),
787 info.fFactory);
Florin Malita0ffa3222018-04-05 14:34:45 -0400788 }
Florin Malitac659c2c2018-04-05 11:57:21 -0400789 }
Florin Malita0ffa3222018-04-05 14:34:45 -0400790 if (!dirSlides.empty()) {
791 fSlides.push_back(
792 sk_make_sp<SlideDir>(SkStringPrintf("%s[%s]", info.fDirName, flag.c_str()),
793 std::move(dirSlides)));
Mike Klein16885072018-12-11 09:54:31 -0500794 dirSlides.reset(); // NOLINT(bugprone-use-after-move)
Florin Malita0ffa3222018-04-05 14:34:45 -0400795 }
Florin Malitac659c2c2018-04-05 11:57:21 -0400796 }
797 }
Jim Van Verth74826c82019-03-01 14:37:30 -0500798
799 if (!fSlides.count()) {
800 sk_sp<Slide> slide(new NullSlide());
801 fSlides.push_back(std::move(slide));
802 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700803}
804
805
jvanverth34524262016-05-04 13:49:13 -0700806Viewer::~Viewer() {
jvanverth9f372462016-04-06 06:08:59 -0700807 fWindow->detach();
808 delete fWindow;
809}
810
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500811struct SkPaintTitleUpdater {
812 SkPaintTitleUpdater(SkString* title) : fTitle(title), fCount(0) {}
813 void append(const char* s) {
814 if (fCount == 0) {
815 fTitle->append(" {");
816 } else {
817 fTitle->append(", ");
818 }
819 fTitle->append(s);
820 ++fCount;
821 }
822 void done() {
823 if (fCount > 0) {
824 fTitle->append("}");
825 }
826 }
827 SkString* fTitle;
828 int fCount;
829};
830
brianosman05de2162016-05-06 13:28:57 -0700831void Viewer::updateTitle() {
csmartdalton578f0642017-02-24 16:04:47 -0700832 if (!fWindow) {
833 return;
834 }
Brian Salomonbdecacf2018-02-02 20:32:49 -0500835 if (fWindow->sampleCount() < 1) {
csmartdalton578f0642017-02-24 16:04:47 -0700836 return; // Surface hasn't been created yet.
837 }
838
jvanverth34524262016-05-04 13:49:13 -0700839 SkString title("Viewer: ");
jvanverthc265a922016-04-08 12:51:45 -0700840 title.append(fSlides[fCurrentSlide]->getName());
brianosmanb109b8c2016-06-16 13:03:24 -0700841
Mike Kleine5acd752019-03-22 09:57:16 -0500842 if (gSkUseAnalyticAA) {
Yuqian Li399b3c22017-08-03 11:08:15 -0400843 if (gSkForceAnalyticAA) {
844 title.append(" <FAAA>");
845 } else {
846 title.append(" <AAA>");
847 }
848 }
Mike Reed59295352020-03-12 13:56:34 -0400849 if (fDrawViaSerialize) {
850 title.append(" <serialize>");
851 }
Mike Reed862818b2020-03-21 15:07:13 -0400852 if (gUseSkVMBlitter) {
853 title.append(" <skvm>");
854 }
Yuqian Li399b3c22017-08-03 11:08:15 -0400855
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500856 SkPaintTitleUpdater paintTitle(&title);
Ben Wagner9613e452019-01-23 10:34:59 -0500857 auto paintFlag = [this, &paintTitle](bool SkPaintFields::* flag,
858 bool (SkPaint::* isFlag)() const,
Ben Wagner99a78dc2018-05-09 18:23:51 -0400859 const char* on, const char* off)
860 {
Ben Wagner9613e452019-01-23 10:34:59 -0500861 if (fPaintOverrides.*flag) {
Ben Wagner99a78dc2018-05-09 18:23:51 -0400862 paintTitle.append((fPaint.*isFlag)() ? on : off);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500863 }
Ben Wagner99a78dc2018-05-09 18:23:51 -0400864 };
865
Ben Wagner9613e452019-01-23 10:34:59 -0500866 auto fontFlag = [this, &paintTitle](bool SkFontFields::* flag, bool (SkFont::* isFlag)() const,
867 const char* on, const char* off)
868 {
869 if (fFontOverrides.*flag) {
870 paintTitle.append((fFont.*isFlag)() ? on : off);
871 }
872 };
873
874 paintFlag(&SkPaintFields::fAntiAlias, &SkPaint::isAntiAlias, "Antialias", "Alias");
875 paintFlag(&SkPaintFields::fDither, &SkPaint::isDither, "DITHER", "No Dither");
Ben Wagnerd10a78f2019-03-07 13:14:26 -0500876 if (fPaintOverrides.fFilterQuality) {
877 switch (fPaint.getFilterQuality()) {
878 case kNone_SkFilterQuality:
879 paintTitle.append("NoFilter");
880 break;
881 case kLow_SkFilterQuality:
882 paintTitle.append("LowFilter");
883 break;
884 case kMedium_SkFilterQuality:
885 paintTitle.append("MediumFilter");
886 break;
887 case kHigh_SkFilterQuality:
888 paintTitle.append("HighFilter");
889 break;
890 }
891 }
Ben Wagner9613e452019-01-23 10:34:59 -0500892
893 fontFlag(&SkFontFields::fForceAutoHinting, &SkFont::isForceAutoHinting,
894 "Force Autohint", "No Force Autohint");
895 fontFlag(&SkFontFields::fEmbolden, &SkFont::isEmbolden, "Fake Bold", "No Fake Bold");
Ben Wagnerc17de1d2019-08-26 16:59:09 -0400896 fontFlag(&SkFontFields::fBaselineSnap, &SkFont::isBaselineSnap, "BaseSnap", "No BaseSnap");
Ben Wagner9613e452019-01-23 10:34:59 -0500897 fontFlag(&SkFontFields::fLinearMetrics, &SkFont::isLinearMetrics,
898 "Linear Metrics", "Non-Linear Metrics");
899 fontFlag(&SkFontFields::fEmbeddedBitmaps, &SkFont::isEmbeddedBitmaps,
900 "Bitmap Text", "No Bitmap Text");
901 fontFlag(&SkFontFields::fSubpixel, &SkFont::isSubpixel, "Subpixel Text", "Pixel Text");
902
903 if (fFontOverrides.fEdging) {
904 switch (fFont.getEdging()) {
905 case SkFont::Edging::kAlias:
906 paintTitle.append("Alias Text");
907 break;
908 case SkFont::Edging::kAntiAlias:
909 paintTitle.append("Antialias Text");
910 break;
911 case SkFont::Edging::kSubpixelAntiAlias:
912 paintTitle.append("Subpixel Antialias Text");
913 break;
914 }
915 }
Ben Wagner99a78dc2018-05-09 18:23:51 -0400916
Mike Reed3ae47332019-01-04 10:11:46 -0500917 if (fFontOverrides.fHinting) {
918 switch (fFont.getHinting()) {
Ben Wagner5785e4a2019-05-07 16:50:29 -0400919 case SkFontHinting::kNone:
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500920 paintTitle.append("No Hinting");
921 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400922 case SkFontHinting::kSlight:
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500923 paintTitle.append("Slight Hinting");
924 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400925 case SkFontHinting::kNormal:
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500926 paintTitle.append("Normal Hinting");
927 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400928 case SkFontHinting::kFull:
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500929 paintTitle.append("Full Hinting");
930 break;
931 }
932 }
933 paintTitle.done();
934
Brian Osman92004802017-03-06 11:47:26 -0500935 switch (fColorMode) {
936 case ColorMode::kLegacy:
937 title.append(" Legacy 8888");
938 break;
Brian Osman03115dc2018-11-26 13:55:19 -0500939 case ColorMode::kColorManaged8888:
Brian Osman92004802017-03-06 11:47:26 -0500940 title.append(" ColorManaged 8888");
941 break;
Brian Osman03115dc2018-11-26 13:55:19 -0500942 case ColorMode::kColorManagedF16:
Brian Osman92004802017-03-06 11:47:26 -0500943 title.append(" ColorManaged F16");
944 break;
Brian Salomon8391bac2019-09-18 11:22:44 -0400945 case ColorMode::kColorManagedF16Norm:
946 title.append(" ColorManaged F16 Norm");
947 break;
Brian Osman92004802017-03-06 11:47:26 -0500948 }
Brian Osmanf750fbc2017-02-08 10:47:28 -0500949
Brian Osman92004802017-03-06 11:47:26 -0500950 if (ColorMode::kLegacy != fColorMode) {
Brian Osmana109e392017-02-24 09:49:14 -0500951 int curPrimaries = -1;
952 for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
953 if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
954 curPrimaries = i;
955 break;
956 }
957 }
Brian Osman03115dc2018-11-26 13:55:19 -0500958 title.appendf(" %s Gamma %f",
959 curPrimaries >= 0 ? gNamedPrimaries[curPrimaries].fName : "Custom",
Brian Osman82ebe042019-01-04 17:03:00 -0500960 fColorSpaceTransferFn.g);
brianosman05de2162016-05-06 13:28:57 -0700961 }
Brian Osmanf750fbc2017-02-08 10:47:28 -0500962
Ben Wagner37c54032018-04-13 14:30:23 -0400963 const DisplayParams& params = fWindow->getRequestedDisplayParams();
964 if (fPixelGeometryOverrides) {
965 switch (params.fSurfaceProps.pixelGeometry()) {
966 case kUnknown_SkPixelGeometry:
967 title.append( " Flat");
968 break;
969 case kRGB_H_SkPixelGeometry:
970 title.append( " RGB");
971 break;
972 case kBGR_H_SkPixelGeometry:
973 title.append( " BGR");
974 break;
975 case kRGB_V_SkPixelGeometry:
976 title.append( " RGBV");
977 break;
978 case kBGR_V_SkPixelGeometry:
979 title.append( " BGRV");
980 break;
981 }
982 }
983
984 if (params.fSurfaceProps.isUseDeviceIndependentFonts()) {
985 title.append(" DFT");
986 }
987
csmartdalton578f0642017-02-24 16:04:47 -0700988 title.append(" [");
jvanverthaf236b52016-05-20 06:01:06 -0700989 title.append(kBackendTypeStrings[fBackendType]);
Brian Salomonbdecacf2018-02-02 20:32:49 -0500990 int msaa = fWindow->sampleCount();
991 if (msaa > 1) {
csmartdalton578f0642017-02-24 16:04:47 -0700992 title.appendf(" MSAA: %i", msaa);
993 }
994 title.append("]");
csmartdalton61cd31a2017-02-27 17:00:53 -0700995
996 GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
Chris Dalton37ae4b02019-12-28 14:51:11 -0700997 if (GpuPathRenderers::kDefault != pr) {
csmartdalton61cd31a2017-02-27 17:00:53 -0700998 title.appendf(" [Path renderer: %s]", gPathRendererNames[pr].c_str());
999 }
1000
Brian Osman805a7272018-05-02 15:40:20 -04001001 if (kPerspective_Real == fPerspectiveMode) {
1002 title.append(" Perpsective (Real)");
1003 } else if (kPerspective_Fake == fPerspectiveMode) {
1004 title.append(" Perspective (Fake)");
1005 }
1006
brianosman05de2162016-05-06 13:28:57 -07001007 fWindow->setTitle(title.c_str());
1008}
1009
Florin Malitaab99c342018-01-16 16:23:03 -05001010int Viewer::startupSlide() const {
Jim Van Verth6f449692017-02-14 15:16:46 -05001011
1012 if (!FLAGS_slide.isEmpty()) {
1013 int count = fSlides.count();
1014 for (int i = 0; i < count; i++) {
1015 if (fSlides[i]->getName().equals(FLAGS_slide[0])) {
Florin Malitaab99c342018-01-16 16:23:03 -05001016 return i;
Jim Van Verth6f449692017-02-14 15:16:46 -05001017 }
1018 }
1019
1020 fprintf(stderr, "Unknown slide \"%s\"\n", FLAGS_slide[0]);
1021 this->listNames();
1022 }
1023
Florin Malitaab99c342018-01-16 16:23:03 -05001024 return 0;
Jim Van Verth6f449692017-02-14 15:16:46 -05001025}
1026
Florin Malitaab99c342018-01-16 16:23:03 -05001027void Viewer::listNames() const {
Jim Van Verth6f449692017-02-14 15:16:46 -05001028 SkDebugf("All Slides:\n");
Florin Malitaab99c342018-01-16 16:23:03 -05001029 for (const auto& slide : fSlides) {
1030 SkDebugf(" %s\n", slide->getName().c_str());
Jim Van Verth6f449692017-02-14 15:16:46 -05001031 }
1032}
1033
Florin Malitaab99c342018-01-16 16:23:03 -05001034void Viewer::setCurrentSlide(int slide) {
1035 SkASSERT(slide >= 0 && slide < fSlides.count());
liyuqian6f163d22016-06-13 12:26:45 -07001036
Florin Malitaab99c342018-01-16 16:23:03 -05001037 if (slide == fCurrentSlide) {
1038 return;
1039 }
1040
1041 if (fCurrentSlide >= 0) {
1042 fSlides[fCurrentSlide]->unload();
1043 }
1044
1045 fSlides[slide]->load(SkIntToScalar(fWindow->width()),
1046 SkIntToScalar(fWindow->height()));
1047 fCurrentSlide = slide;
1048 this->setupCurrentSlide();
1049}
1050
1051void Viewer::setupCurrentSlide() {
Jim Van Verth0848fb02018-01-22 13:39:30 -05001052 if (fCurrentSlide >= 0) {
1053 // prepare dimensions for image slides
1054 fGesture.resetTouchState();
1055 fDefaultMatrix.reset();
liyuqiane46e4f02016-05-20 07:32:19 -07001056
Jim Van Verth0848fb02018-01-22 13:39:30 -05001057 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1058 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
1059 const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
Brian Osman42bb6ac2017-06-05 08:46:04 -04001060
Jim Van Verth0848fb02018-01-22 13:39:30 -05001061 // Start with a matrix that scales the slide to the available screen space
1062 if (fWindow->scaleContentToFit()) {
1063 if (windowRect.width() > 0 && windowRect.height() > 0) {
1064 fDefaultMatrix.setRectToRect(slideBounds, windowRect, SkMatrix::kStart_ScaleToFit);
1065 }
liyuqiane46e4f02016-05-20 07:32:19 -07001066 }
Jim Van Verth0848fb02018-01-22 13:39:30 -05001067
1068 // Prevent the user from dragging content so far outside the window they can't find it again
Yuqian Li755778c2018-03-28 16:23:31 -04001069 fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
Jim Van Verth0848fb02018-01-22 13:39:30 -05001070
1071 this->updateTitle();
1072 this->updateUIState();
1073
1074 fStatsLayer.resetMeasurements();
1075
1076 fWindow->inval();
liyuqiane46e4f02016-05-20 07:32:19 -07001077 }
jvanverthc265a922016-04-08 12:51:45 -07001078}
1079
Brian Osmanaba642c2020-02-06 12:52:25 -05001080#define MAX_ZOOM_LEVEL 8.0f
1081#define MIN_ZOOM_LEVEL -8.0f
jvanverthc265a922016-04-08 12:51:45 -07001082
jvanverth34524262016-05-04 13:49:13 -07001083void Viewer::changeZoomLevel(float delta) {
jvanverthc265a922016-04-08 12:51:45 -07001084 fZoomLevel += delta;
Brian Osmanaba642c2020-02-06 12:52:25 -05001085 fZoomLevel = SkTPin(fZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001086 this->preTouchMatrixChanged();
1087}
Yuqian Li755778c2018-03-28 16:23:31 -04001088
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001089void Viewer::preTouchMatrixChanged() {
1090 // Update the trans limit as the transform changes.
Yuqian Li755778c2018-03-28 16:23:31 -04001091 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1092 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
1093 const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
1094 fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
1095}
1096
Brian Osman805a7272018-05-02 15:40:20 -04001097SkMatrix Viewer::computePerspectiveMatrix() {
1098 SkScalar w = fWindow->width(), h = fWindow->height();
1099 SkPoint orthoPts[4] = { { 0, 0 }, { w, 0 }, { 0, h }, { w, h } };
1100 SkPoint perspPts[4] = {
1101 { fPerspectivePoints[0].fX * w, fPerspectivePoints[0].fY * h },
1102 { fPerspectivePoints[1].fX * w, fPerspectivePoints[1].fY * h },
1103 { fPerspectivePoints[2].fX * w, fPerspectivePoints[2].fY * h },
1104 { fPerspectivePoints[3].fX * w, fPerspectivePoints[3].fY * h }
1105 };
1106 SkMatrix m;
1107 m.setPolyToPoly(orthoPts, perspPts, 4);
1108 return m;
1109}
1110
Yuqian Li755778c2018-03-28 16:23:31 -04001111SkMatrix Viewer::computePreTouchMatrix() {
1112 SkMatrix m = fDefaultMatrix;
Ben Wagnercc8eb862019-03-21 16:50:22 -04001113
1114 SkScalar zoomScale = exp(fZoomLevel);
Ben Wagner897dfa22018-08-09 15:18:46 -04001115 m.preTranslate((fOffset.x() - 0.5f) * 2.0f, (fOffset.y() - 0.5f) * 2.0f);
Yuqian Li755778c2018-03-28 16:23:31 -04001116 m.preScale(zoomScale, zoomScale);
Brian Osmanbdaf97b2018-04-26 16:22:42 -04001117
1118 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1119 m.preRotate(fRotation, slideSize.width() * 0.5f, slideSize.height() * 0.5f);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001120
Brian Osman805a7272018-05-02 15:40:20 -04001121 if (kPerspective_Real == fPerspectiveMode) {
1122 SkMatrix persp = this->computePerspectiveMatrix();
Brian Osmanbdaf97b2018-04-26 16:22:42 -04001123 m.postConcat(persp);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001124 }
1125
Yuqian Li755778c2018-03-28 16:23:31 -04001126 return m;
jvanverthc265a922016-04-08 12:51:45 -07001127}
1128
liyuqiand3cdbca2016-05-17 12:44:20 -07001129SkMatrix Viewer::computeMatrix() {
Yuqian Li755778c2018-03-28 16:23:31 -04001130 SkMatrix m = fGesture.localM();
liyuqiand3cdbca2016-05-17 12:44:20 -07001131 m.preConcat(fGesture.globalM());
Yuqian Li755778c2018-03-28 16:23:31 -04001132 m.preConcat(this->computePreTouchMatrix());
liyuqiand3cdbca2016-05-17 12:44:20 -07001133 return m;
jvanverthc265a922016-04-08 12:51:45 -07001134}
1135
Brian Osman621491e2017-02-28 15:45:01 -05001136void Viewer::setBackend(sk_app::Window::BackendType backendType) {
Brian Osman5bee3902019-05-07 09:55:45 -04001137 fPersistentCache.reset();
1138 fCachedGLSL.reset();
Brian Osman621491e2017-02-28 15:45:01 -05001139 fBackendType = backendType;
1140
1141 fWindow->detach();
1142
Brian Osman70d2f432017-11-08 09:54:10 -05001143#if defined(SK_BUILD_FOR_WIN)
Brian Salomon194db172017-08-17 14:37:06 -04001144 // Switching between OpenGL, Vulkan, and ANGLE in the same window is problematic at this point
1145 // on Windows, so we just delete the window and recreate it.
Brian Osman70d2f432017-11-08 09:54:10 -05001146 DisplayParams params = fWindow->getRequestedDisplayParams();
1147 delete fWindow;
1148 fWindow = Window::CreateNativeWindow(nullptr);
Brian Osman621491e2017-02-28 15:45:01 -05001149
Brian Osman70d2f432017-11-08 09:54:10 -05001150 // re-register callbacks
1151 fCommands.attach(fWindow);
Brian Osman80fc07e2017-12-08 16:45:43 -05001152 fWindow->pushLayer(this);
Brian Osman56a24812017-12-19 11:15:16 -05001153 fWindow->pushLayer(&fStatsLayer);
Brian Osmand67e5182017-12-08 16:46:09 -05001154 fWindow->pushLayer(&fImGuiLayer);
1155
Brian Osman70d2f432017-11-08 09:54:10 -05001156 // Don't allow the window to re-attach. If we're in MSAA mode, the params we grabbed above
1157 // will still include our correct sample count. But the re-created fWindow will lose that
1158 // information. On Windows, we need to re-create the window when changing sample count,
1159 // so we'll incorrectly detect that situation, then re-initialize the window in GL mode,
1160 // rendering this tear-down step pointless (and causing the Vulkan window context to fail
1161 // as if we had never changed windows at all).
1162 fWindow->setRequestedDisplayParams(params, false);
Brian Osman621491e2017-02-28 15:45:01 -05001163#endif
1164
Brian Osman70d2f432017-11-08 09:54:10 -05001165 fWindow->attach(backend_type_for_window(fBackendType));
Brian Osman621491e2017-02-28 15:45:01 -05001166}
1167
Brian Osman92004802017-03-06 11:47:26 -05001168void Viewer::setColorMode(ColorMode colorMode) {
1169 fColorMode = colorMode;
Brian Osmanf750fbc2017-02-08 10:47:28 -05001170 this->updateTitle();
1171 fWindow->inval();
1172}
1173
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001174class OveridePaintFilterCanvas : public SkPaintFilterCanvas {
1175public:
Mike Reed3ae47332019-01-04 10:11:46 -05001176 OveridePaintFilterCanvas(SkCanvas* canvas, SkPaint* paint, Viewer::SkPaintFields* pfields,
1177 SkFont* font, Viewer::SkFontFields* ffields)
1178 : SkPaintFilterCanvas(canvas), fPaint(paint), fPaintOverrides(pfields), fFont(font), fFontOverrides(ffields)
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001179 { }
Ben Wagner41e40472018-09-24 13:01:54 -04001180 const SkTextBlob* filterTextBlob(const SkPaint& paint, const SkTextBlob* blob,
1181 sk_sp<SkTextBlob>* cache) {
1182 bool blobWillChange = false;
1183 for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
Mike Reed3ae47332019-01-04 10:11:46 -05001184 SkTCopyOnFirstWrite<SkFont> filteredFont(it.font());
1185 bool shouldDraw = this->filterFont(&filteredFont);
1186 if (it.font() != *filteredFont || !shouldDraw) {
Ben Wagner41e40472018-09-24 13:01:54 -04001187 blobWillChange = true;
1188 break;
1189 }
1190 }
1191 if (!blobWillChange) {
1192 return blob;
1193 }
1194
1195 SkTextBlobBuilder builder;
1196 for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
Mike Reed3ae47332019-01-04 10:11:46 -05001197 SkTCopyOnFirstWrite<SkFont> filteredFont(it.font());
1198 bool shouldDraw = this->filterFont(&filteredFont);
Ben Wagner41e40472018-09-24 13:01:54 -04001199 if (!shouldDraw) {
1200 continue;
1201 }
1202
Mike Reed3ae47332019-01-04 10:11:46 -05001203 SkFont font = *filteredFont;
Mike Reed6d595682018-12-05 17:28:14 -05001204
Ben Wagner41e40472018-09-24 13:01:54 -04001205 const SkTextBlobBuilder::RunBuffer& runBuffer
1206 = it.positioning() == SkTextBlobRunIterator::kDefault_Positioning
Mike Reed6d595682018-12-05 17:28:14 -05001207 ? SkTextBlobBuilderPriv::AllocRunText(&builder, font,
Ben Wagner0bb2db12019-03-06 18:19:08 -05001208 it.glyphCount(), it.offset().x(),it.offset().y(), it.textSize(), SkString())
Ben Wagner41e40472018-09-24 13:01:54 -04001209 : it.positioning() == SkTextBlobRunIterator::kHorizontal_Positioning
Mike Reed6d595682018-12-05 17:28:14 -05001210 ? SkTextBlobBuilderPriv::AllocRunTextPosH(&builder, font,
Ben Wagner0bb2db12019-03-06 18:19:08 -05001211 it.glyphCount(), it.offset().y(), it.textSize(), SkString())
Ben Wagner41e40472018-09-24 13:01:54 -04001212 : it.positioning() == SkTextBlobRunIterator::kFull_Positioning
Mike Reed6d595682018-12-05 17:28:14 -05001213 ? SkTextBlobBuilderPriv::AllocRunTextPos(&builder, font,
Ben Wagner41e40472018-09-24 13:01:54 -04001214 it.glyphCount(), it.textSize(), SkString())
1215 : (SkASSERT_RELEASE(false), SkTextBlobBuilder::RunBuffer());
1216 uint32_t glyphCount = it.glyphCount();
1217 if (it.glyphs()) {
1218 size_t glyphSize = sizeof(decltype(*it.glyphs()));
1219 memcpy(runBuffer.glyphs, it.glyphs(), glyphCount * glyphSize);
1220 }
1221 if (it.pos()) {
1222 size_t posSize = sizeof(decltype(*it.pos()));
1223 uint8_t positioning = it.positioning();
1224 memcpy(runBuffer.pos, it.pos(), glyphCount * positioning * posSize);
1225 }
1226 if (it.text()) {
1227 size_t textSize = sizeof(decltype(*it.text()));
1228 uint32_t textCount = it.textSize();
1229 memcpy(runBuffer.utf8text, it.text(), textCount * textSize);
1230 }
1231 if (it.clusters()) {
1232 size_t clusterSize = sizeof(decltype(*it.clusters()));
1233 memcpy(runBuffer.clusters, it.clusters(), glyphCount * clusterSize);
1234 }
1235 }
1236 *cache = builder.make();
1237 return cache->get();
1238 }
1239 void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
1240 const SkPaint& paint) override {
1241 sk_sp<SkTextBlob> cache;
1242 this->SkPaintFilterCanvas::onDrawTextBlob(
1243 this->filterTextBlob(paint, blob, &cache), x, y, paint);
1244 }
Mike Reed3ae47332019-01-04 10:11:46 -05001245 bool filterFont(SkTCopyOnFirstWrite<SkFont>* font) const {
Ben Wagner15a8d572019-03-21 13:35:44 -04001246 if (fFontOverrides->fSize) {
Mike Reed3ae47332019-01-04 10:11:46 -05001247 font->writable()->setSize(fFont->getSize());
1248 }
Ben Wagner15a8d572019-03-21 13:35:44 -04001249 if (fFontOverrides->fScaleX) {
1250 font->writable()->setScaleX(fFont->getScaleX());
1251 }
1252 if (fFontOverrides->fSkewX) {
1253 font->writable()->setSkewX(fFont->getSkewX());
1254 }
Mike Reed3ae47332019-01-04 10:11:46 -05001255 if (fFontOverrides->fHinting) {
1256 font->writable()->setHinting(fFont->getHinting());
1257 }
Ben Wagner9613e452019-01-23 10:34:59 -05001258 if (fFontOverrides->fEdging) {
1259 font->writable()->setEdging(fFont->getEdging());
Hal Canary02738a82019-01-21 18:51:32 +00001260 }
Ben Wagner9613e452019-01-23 10:34:59 -05001261 if (fFontOverrides->fEmbolden) {
1262 font->writable()->setEmbolden(fFont->isEmbolden());
Hal Canary02738a82019-01-21 18:51:32 +00001263 }
Ben Wagnerc17de1d2019-08-26 16:59:09 -04001264 if (fFontOverrides->fBaselineSnap) {
1265 font->writable()->setBaselineSnap(fFont->isBaselineSnap());
1266 }
Ben Wagner9613e452019-01-23 10:34:59 -05001267 if (fFontOverrides->fLinearMetrics) {
1268 font->writable()->setLinearMetrics(fFont->isLinearMetrics());
Hal Canary02738a82019-01-21 18:51:32 +00001269 }
Ben Wagner9613e452019-01-23 10:34:59 -05001270 if (fFontOverrides->fSubpixel) {
1271 font->writable()->setSubpixel(fFont->isSubpixel());
Hal Canary02738a82019-01-21 18:51:32 +00001272 }
Ben Wagner9613e452019-01-23 10:34:59 -05001273 if (fFontOverrides->fEmbeddedBitmaps) {
1274 font->writable()->setEmbeddedBitmaps(fFont->isEmbeddedBitmaps());
Hal Canary02738a82019-01-21 18:51:32 +00001275 }
Ben Wagner9613e452019-01-23 10:34:59 -05001276 if (fFontOverrides->fForceAutoHinting) {
1277 font->writable()->setForceAutoHinting(fFont->isForceAutoHinting());
Hal Canary02738a82019-01-21 18:51:32 +00001278 }
Ben Wagner9613e452019-01-23 10:34:59 -05001279
Mike Reed3ae47332019-01-04 10:11:46 -05001280 return true;
1281 }
Ben Wagnerf55fa0d2018-08-27 18:11:57 -04001282 bool onFilter(SkPaint& paint) const override {
Ben Wagner9613e452019-01-23 10:34:59 -05001283 if (fPaintOverrides->fAntiAlias) {
Ben Wagnerf55fa0d2018-08-27 18:11:57 -04001284 paint.setAntiAlias(fPaint->isAntiAlias());
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001285 }
Ben Wagner9613e452019-01-23 10:34:59 -05001286 if (fPaintOverrides->fDither) {
Ben Wagnerf55fa0d2018-08-27 18:11:57 -04001287 paint.setDither(fPaint->isDither());
Ben Wagner99a78dc2018-05-09 18:23:51 -04001288 }
Ben Wagnerd10a78f2019-03-07 13:14:26 -05001289 if (fPaintOverrides->fFilterQuality) {
Ben Wagnerf55fa0d2018-08-27 18:11:57 -04001290 paint.setFilterQuality(fPaint->getFilterQuality());
Ben Wagnerd10a78f2019-03-07 13:14:26 -05001291 }
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001292 return true;
1293 }
1294 SkPaint* fPaint;
1295 Viewer::SkPaintFields* fPaintOverrides;
Mike Reed3ae47332019-01-04 10:11:46 -05001296 SkFont* fFont;
1297 Viewer::SkFontFields* fFontOverrides;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001298};
1299
Robert Phillips9882dae2019-03-04 11:00:10 -05001300void Viewer::drawSlide(SkSurface* surface) {
Jim Van Verth74826c82019-03-01 14:37:30 -05001301 if (fCurrentSlide < 0) {
1302 return;
1303 }
1304
Robert Phillips9882dae2019-03-04 11:00:10 -05001305 SkAutoCanvasRestore autorestore(surface->getCanvas(), false);
Brian Salomonbf52e3d2017-02-22 15:21:11 -05001306
Brian Osmanf750fbc2017-02-08 10:47:28 -05001307 // By default, we render directly into the window's surface/canvas
Robert Phillips9882dae2019-03-04 11:00:10 -05001308 SkSurface* slideSurface = surface;
1309 SkCanvas* slideCanvas = surface->getCanvas();
Brian Osmanf6877092017-02-13 09:39:57 -05001310 fLastImage.reset();
jvanverth3d6ed3a2016-04-07 11:09:51 -07001311
Brian Osmane0d4fba2017-03-15 10:24:55 -04001312 // If we're in any of the color managed modes, construct the color space we're going to use
Brian Osman03115dc2018-11-26 13:55:19 -05001313 sk_sp<SkColorSpace> colorSpace = nullptr;
Brian Osmane0d4fba2017-03-15 10:24:55 -04001314 if (ColorMode::kLegacy != fColorMode) {
Brian Osman82ebe042019-01-04 17:03:00 -05001315 skcms_Matrix3x3 toXYZ;
Brian Osmane0d4fba2017-03-15 10:24:55 -04001316 SkAssertResult(fColorSpacePrimaries.toXYZD50(&toXYZ));
Brian Osman03115dc2018-11-26 13:55:19 -05001317 colorSpace = SkColorSpace::MakeRGB(fColorSpaceTransferFn, toXYZ);
Brian Osmane0d4fba2017-03-15 10:24:55 -04001318 }
1319
Brian Osman3ac99cf2017-12-01 11:23:53 -05001320 if (fSaveToSKP) {
1321 SkPictureRecorder recorder;
1322 SkCanvas* recorderCanvas = recorder.beginRecording(
1323 SkRect::Make(fSlides[fCurrentSlide]->getDimensions()));
Brian Osman3ac99cf2017-12-01 11:23:53 -05001324 fSlides[fCurrentSlide]->draw(recorderCanvas);
1325 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1326 SkFILEWStream stream("sample_app.skp");
1327 picture->serialize(&stream);
1328 fSaveToSKP = false;
1329 }
1330
Brian Osmane9ed0f02018-11-26 14:50:05 -05001331 // Grab some things we'll need to make surfaces (for tiling or general offscreen rendering)
Brian Salomon8391bac2019-09-18 11:22:44 -04001332 SkColorType colorType;
1333 switch (fColorMode) {
1334 case ColorMode::kLegacy:
1335 case ColorMode::kColorManaged8888:
1336 colorType = kN32_SkColorType;
1337 break;
1338 case ColorMode::kColorManagedF16:
1339 colorType = kRGBA_F16_SkColorType;
1340 break;
1341 case ColorMode::kColorManagedF16Norm:
1342 colorType = kRGBA_F16Norm_SkColorType;
1343 break;
1344 }
Brian Osmane9ed0f02018-11-26 14:50:05 -05001345
1346 auto make_surface = [=](int w, int h) {
Robert Phillips9882dae2019-03-04 11:00:10 -05001347 SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1348 slideCanvas->getProps(&props);
1349
Brian Osmane9ed0f02018-11-26 14:50:05 -05001350 SkImageInfo info = SkImageInfo::Make(w, h, colorType, kPremul_SkAlphaType, colorSpace);
1351 return Window::kRaster_BackendType == this->fBackendType
1352 ? SkSurface::MakeRaster(info, &props)
Robert Phillips9882dae2019-03-04 11:00:10 -05001353 : slideCanvas->makeSurface(info, &props);
Brian Osmane9ed0f02018-11-26 14:50:05 -05001354 };
1355
Brian Osman03115dc2018-11-26 13:55:19 -05001356 // We need to render offscreen if we're...
1357 // ... in fake perspective or zooming (so we have a snapped copy of the results)
1358 // ... in any raster mode, because the window surface is actually GL
1359 // ... in any color managed mode, because we always make the window surface with no color space
Chris Daltonc8877332020-01-06 09:48:30 -07001360 // ... or if the user explicitly requested offscreen rendering
Brian Osmanf750fbc2017-02-08 10:47:28 -05001361 sk_sp<SkSurface> offscreenSurface = nullptr;
Brian Osman03115dc2018-11-26 13:55:19 -05001362 if (kPerspective_Fake == fPerspectiveMode ||
Brian Osman92004802017-03-06 11:47:26 -05001363 fShowZoomWindow ||
Brian Osman03115dc2018-11-26 13:55:19 -05001364 Window::kRaster_BackendType == fBackendType ||
Chris Daltonc8877332020-01-06 09:48:30 -07001365 colorSpace != nullptr ||
1366 FLAGS_offscreen) {
Brian Osmane0d4fba2017-03-15 10:24:55 -04001367
Brian Osmane9ed0f02018-11-26 14:50:05 -05001368 offscreenSurface = make_surface(fWindow->width(), fWindow->height());
Robert Phillips9882dae2019-03-04 11:00:10 -05001369 slideSurface = offscreenSurface.get();
Mike Klein48b64902018-07-25 13:28:44 -04001370 slideCanvas = offscreenSurface->getCanvas();
Brian Osmanf750fbc2017-02-08 10:47:28 -05001371 }
1372
Mike Reed59295352020-03-12 13:56:34 -04001373 SkPictureRecorder recorder;
1374 SkCanvas* recorderRestoreCanvas = nullptr;
1375 if (fDrawViaSerialize) {
1376 recorderRestoreCanvas = slideCanvas;
1377 slideCanvas = recorder.beginRecording(
1378 SkRect::Make(fSlides[fCurrentSlide]->getDimensions()));
1379 }
1380
Brian Salomonbf52e3d2017-02-22 15:21:11 -05001381 int count = slideCanvas->save();
Brian Osmanf750fbc2017-02-08 10:47:28 -05001382 slideCanvas->clear(SK_ColorWHITE);
Brian Osman1df161a2017-02-09 12:10:20 -05001383 // Time the painting logic of the slide
Brian Osman56a24812017-12-19 11:15:16 -05001384 fStatsLayer.beginTiming(fPaintTimer);
Brian Osmane9ed0f02018-11-26 14:50:05 -05001385 if (fTiled) {
1386 int tileW = SkScalarCeilToInt(fWindow->width() * fTileScale.width());
1387 int tileH = SkScalarCeilToInt(fWindow->height() * fTileScale.height());
Brian Osmane9ed0f02018-11-26 14:50:05 -05001388 for (int y = 0; y < fWindow->height(); y += tileH) {
1389 for (int x = 0; x < fWindow->width(); x += tileW) {
Florin Malitaf0d5ea12020-02-19 09:23:08 -05001390 SkAutoCanvasRestore acr(slideCanvas, true);
1391 slideCanvas->clipRect(SkRect::MakeXYWH(x, y, tileW, tileH));
1392 fSlides[fCurrentSlide]->draw(slideCanvas);
Brian Osmane9ed0f02018-11-26 14:50:05 -05001393 }
1394 }
1395
1396 // Draw borders between tiles
1397 if (fDrawTileBoundaries) {
1398 SkPaint border;
1399 border.setColor(0x60FF00FF);
1400 border.setStyle(SkPaint::kStroke_Style);
1401 for (int y = 0; y < fWindow->height(); y += tileH) {
1402 for (int x = 0; x < fWindow->width(); x += tileW) {
1403 slideCanvas->drawRect(SkRect::MakeXYWH(x, y, tileW, tileH), border);
1404 }
1405 }
1406 }
1407 } else {
1408 slideCanvas->concat(this->computeMatrix());
1409 if (kPerspective_Real == fPerspectiveMode) {
1410 slideCanvas->clipRect(SkRect::MakeWH(fWindow->width(), fWindow->height()));
1411 }
Mike Reed3ae47332019-01-04 10:11:46 -05001412 OveridePaintFilterCanvas filterCanvas(slideCanvas, &fPaint, &fPaintOverrides, &fFont, &fFontOverrides);
Brian Osmane9ed0f02018-11-26 14:50:05 -05001413 fSlides[fCurrentSlide]->draw(&filterCanvas);
1414 }
Brian Osman56a24812017-12-19 11:15:16 -05001415 fStatsLayer.endTiming(fPaintTimer);
Brian Salomonbf52e3d2017-02-22 15:21:11 -05001416 slideCanvas->restoreToCount(count);
Brian Osman1df161a2017-02-09 12:10:20 -05001417
Mike Reed59295352020-03-12 13:56:34 -04001418 if (recorderRestoreCanvas) {
1419 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1420 auto data = picture->serialize();
1421 slideCanvas = recorderRestoreCanvas;
1422 slideCanvas->drawPicture(SkPicture::MakeFromData(data.get()));
1423 }
1424
Brian Osman1df161a2017-02-09 12:10:20 -05001425 // Force a flush so we can time that, too
Brian Osman56a24812017-12-19 11:15:16 -05001426 fStatsLayer.beginTiming(fFlushTimer);
Robert Phillips9882dae2019-03-04 11:00:10 -05001427 slideSurface->flush();
Brian Osman56a24812017-12-19 11:15:16 -05001428 fStatsLayer.endTiming(fFlushTimer);
Brian Osmanf750fbc2017-02-08 10:47:28 -05001429
1430 // If we rendered offscreen, snap an image and push the results to the window's canvas
1431 if (offscreenSurface) {
Brian Osmanf6877092017-02-13 09:39:57 -05001432 fLastImage = offscreenSurface->makeImageSnapshot();
Brian Osmanf750fbc2017-02-08 10:47:28 -05001433
Robert Phillips9882dae2019-03-04 11:00:10 -05001434 SkCanvas* canvas = surface->getCanvas();
Brian Salomonbf52e3d2017-02-22 15:21:11 -05001435 SkPaint paint;
1436 paint.setBlendMode(SkBlendMode::kSrc);
Brian Osman805a7272018-05-02 15:40:20 -04001437 int prePerspectiveCount = canvas->save();
1438 if (kPerspective_Fake == fPerspectiveMode) {
1439 paint.setFilterQuality(kHigh_SkFilterQuality);
1440 canvas->clear(SK_ColorWHITE);
1441 canvas->concat(this->computePerspectiveMatrix());
1442 }
Brian Osman03115dc2018-11-26 13:55:19 -05001443 canvas->drawImage(fLastImage, 0, 0, &paint);
Brian Osman805a7272018-05-02 15:40:20 -04001444 canvas->restoreToCount(prePerspectiveCount);
liyuqian74959a12016-06-16 14:10:34 -07001445 }
Mike Reed376d8122019-03-14 11:39:02 -04001446
1447 if (fShowSlideDimensions) {
1448 SkRect r = SkRect::Make(fSlides[fCurrentSlide]->getDimensions());
1449 SkPaint paint;
1450 paint.setColor(0x40FFFF00);
1451 surface->getCanvas()->drawRect(r, paint);
1452 }
liyuqian6f163d22016-06-13 12:26:45 -07001453}
1454
Christopher Dalton443ec1b2017-02-24 13:22:53 -07001455void Viewer::onBackendCreated() {
Florin Malitaab99c342018-01-16 16:23:03 -05001456 this->setupCurrentSlide();
Christopher Dalton443ec1b2017-02-24 13:22:53 -07001457 fWindow->show();
Christopher Dalton443ec1b2017-02-24 13:22:53 -07001458}
Jim Van Verth6f449692017-02-14 15:16:46 -05001459
Robert Phillips9882dae2019-03-04 11:00:10 -05001460void Viewer::onPaint(SkSurface* surface) {
1461 this->drawSlide(surface);
jvanverthc265a922016-04-08 12:51:45 -07001462
Robert Phillips9882dae2019-03-04 11:00:10 -05001463 fCommands.drawHelp(surface->getCanvas());
liyuqian2edb0f42016-07-06 14:11:32 -07001464
Brian Osmand67e5182017-12-08 16:46:09 -05001465 this->drawImGui();
Chris Dalton89305752018-11-01 10:52:34 -06001466
1467 if (GrContext* ctx = fWindow->getGrContext()) {
1468 // Clean out cache items that haven't been used in more than 10 seconds.
1469 ctx->performDeferredCleanup(std::chrono::seconds(10));
1470 }
jvanverth3d6ed3a2016-04-07 11:09:51 -07001471}
1472
Ben Wagnera1915972018-08-09 15:06:19 -04001473void Viewer::onResize(int width, int height) {
Jim Van Verthb35c6552018-08-13 10:42:17 -04001474 if (fCurrentSlide >= 0) {
1475 fSlides[fCurrentSlide]->resize(width, height);
1476 }
Ben Wagnera1915972018-08-09 15:06:19 -04001477}
1478
Florin Malitacefc1b92018-02-19 21:43:47 -05001479SkPoint Viewer::mapEvent(float x, float y) {
1480 const auto m = this->computeMatrix();
1481 SkMatrix inv;
1482
1483 SkAssertResult(m.invert(&inv));
1484
1485 return inv.mapXY(x, y);
1486}
1487
Hal Canaryb1f411a2019-08-29 10:39:22 -04001488bool Viewer::onTouch(intptr_t owner, skui::InputState state, float x, float y) {
Brian Osmanb53f48c2017-06-07 10:00:30 -04001489 if (GestureDevice::kMouse == fGestureDevice) {
1490 return false;
1491 }
Florin Malitacefc1b92018-02-19 21:43:47 -05001492
1493 const auto slidePt = this->mapEvent(x, y);
Hal Canaryb1f411a2019-08-29 10:39:22 -04001494 if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, skui::ModifierKey::kNone)) {
Florin Malitacefc1b92018-02-19 21:43:47 -05001495 fWindow->inval();
1496 return true;
1497 }
1498
liyuqiand3cdbca2016-05-17 12:44:20 -07001499 void* castedOwner = reinterpret_cast<void*>(owner);
1500 switch (state) {
Hal Canaryb1f411a2019-08-29 10:39:22 -04001501 case skui::InputState::kUp: {
liyuqiand3cdbca2016-05-17 12:44:20 -07001502 fGesture.touchEnd(castedOwner);
Jim Van Verth234e5a22018-07-23 13:46:01 -04001503#if defined(SK_BUILD_FOR_IOS)
1504 // TODO: move IOS swipe detection higher up into the platform code
1505 SkPoint dir;
1506 if (fGesture.isFling(&dir)) {
1507 // swiping left or right
1508 if (SkTAbs(dir.fX) > SkTAbs(dir.fY)) {
1509 if (dir.fX < 0) {
1510 this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ?
1511 fCurrentSlide + 1 : 0);
1512 } else {
1513 this->setCurrentSlide(fCurrentSlide > 0 ?
1514 fCurrentSlide - 1 : fSlides.count() - 1);
1515 }
1516 }
1517 fGesture.reset();
1518 }
1519#endif
liyuqiand3cdbca2016-05-17 12:44:20 -07001520 break;
1521 }
Hal Canaryb1f411a2019-08-29 10:39:22 -04001522 case skui::InputState::kDown: {
Brian Osman42bb6ac2017-06-05 08:46:04 -04001523 fGesture.touchBegin(castedOwner, x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -07001524 break;
1525 }
Hal Canaryb1f411a2019-08-29 10:39:22 -04001526 case skui::InputState::kMove: {
Brian Osman42bb6ac2017-06-05 08:46:04 -04001527 fGesture.touchMoved(castedOwner, x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -07001528 break;
1529 }
Jim Van Verthd0cf5da2019-09-09 16:53:39 -04001530 default: {
1531 // kLeft and kRight are only for swipes
1532 SkASSERT(false);
1533 break;
1534 }
liyuqiand3cdbca2016-05-17 12:44:20 -07001535 }
Brian Osmanb53f48c2017-06-07 10:00:30 -04001536 fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kTouch : GestureDevice::kNone;
liyuqiand3cdbca2016-05-17 12:44:20 -07001537 fWindow->inval();
1538 return true;
1539}
1540
Hal Canaryb1f411a2019-08-29 10:39:22 -04001541bool Viewer::onMouse(int x, int y, skui::InputState state, skui::ModifierKey modifiers) {
Brian Osman16c81a12017-12-20 11:58:34 -05001542 if (GestureDevice::kTouch == fGestureDevice) {
1543 return false;
Brian Osman80fc07e2017-12-08 16:45:43 -05001544 }
Brian Osman16c81a12017-12-20 11:58:34 -05001545
Florin Malitacefc1b92018-02-19 21:43:47 -05001546 const auto slidePt = this->mapEvent(x, y);
1547 if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, modifiers)) {
1548 fWindow->inval();
1549 return true;
Brian Osman16c81a12017-12-20 11:58:34 -05001550 }
1551
1552 switch (state) {
Hal Canaryb1f411a2019-08-29 10:39:22 -04001553 case skui::InputState::kUp: {
Brian Osman16c81a12017-12-20 11:58:34 -05001554 fGesture.touchEnd(nullptr);
1555 break;
1556 }
Hal Canaryb1f411a2019-08-29 10:39:22 -04001557 case skui::InputState::kDown: {
Brian Osman16c81a12017-12-20 11:58:34 -05001558 fGesture.touchBegin(nullptr, x, y);
1559 break;
1560 }
Hal Canaryb1f411a2019-08-29 10:39:22 -04001561 case skui::InputState::kMove: {
Brian Osman16c81a12017-12-20 11:58:34 -05001562 fGesture.touchMoved(nullptr, x, y);
1563 break;
1564 }
Jim Van Verthd0cf5da2019-09-09 16:53:39 -04001565 default: {
1566 SkASSERT(false); // shouldn't see kRight or kLeft here
1567 break;
1568 }
Brian Osman16c81a12017-12-20 11:58:34 -05001569 }
1570 fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone;
1571
Hal Canaryb1f411a2019-08-29 10:39:22 -04001572 if (state != skui::InputState::kMove || fGesture.isBeingTouched()) {
Brian Osman16c81a12017-12-20 11:58:34 -05001573 fWindow->inval();
1574 }
Jim Van Verthe7705782017-05-04 14:00:59 -04001575 return true;
1576}
1577
Jim Van Verthd0cf5da2019-09-09 16:53:39 -04001578bool Viewer::onFling(skui::InputState state) {
1579 if (skui::InputState::kRight == state) {
1580 this->setCurrentSlide(fCurrentSlide > 0 ? fCurrentSlide - 1 : fSlides.count() - 1);
1581 return true;
1582 } else if (skui::InputState::kLeft == state) {
1583 this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ? fCurrentSlide + 1 : 0);
1584 return true;
1585 }
1586 return false;
1587}
1588
1589bool Viewer::onPinch(skui::InputState state, float scale, float x, float y) {
1590 switch (state) {
1591 case skui::InputState::kDown:
1592 fGesture.startZoom();
1593 return true;
1594 break;
1595 case skui::InputState::kMove:
1596 fGesture.updateZoom(scale, x, y, x, y);
1597 return true;
1598 break;
1599 case skui::InputState::kUp:
1600 fGesture.endZoom();
1601 return true;
1602 break;
1603 default:
1604 SkASSERT(false);
1605 break;
1606 }
1607
1608 return false;
1609}
1610
Brian Osmana109e392017-02-24 09:49:14 -05001611static void ImGui_Primaries(SkColorSpacePrimaries* primaries, SkPaint* gamutPaint) {
Brian Osman535c5e32019-02-09 16:32:58 -05001612 // The gamut image covers a (0.8 x 0.9) shaped region
1613 ImGui::DragCanvas dc(primaries, { 0.0f, 0.9f }, { 0.8f, 0.0f });
Brian Osmana109e392017-02-24 09:49:14 -05001614
1615 // Background image. Only draw a subset of the image, to avoid the regions less than zero.
1616 // Simplifes re-mapping math, clipping behavior, and increases resolution in the useful area.
1617 // Magic numbers are pixel locations of the origin and upper-right corner.
Brian Osman535c5e32019-02-09 16:32:58 -05001618 dc.fDrawList->AddImage(gamutPaint, dc.fPos,
1619 ImVec2(dc.fPos.x + dc.fSize.x, dc.fPos.y + dc.fSize.y),
1620 ImVec2(242, 61), ImVec2(1897, 1922));
Brian Osmana109e392017-02-24 09:49:14 -05001621
Brian Osman535c5e32019-02-09 16:32:58 -05001622 dc.dragPoint((SkPoint*)(&primaries->fRX), true, 0xFF000040);
1623 dc.dragPoint((SkPoint*)(&primaries->fGX), true, 0xFF004000);
1624 dc.dragPoint((SkPoint*)(&primaries->fBX), true, 0xFF400000);
1625 dc.dragPoint((SkPoint*)(&primaries->fWX), true);
1626 dc.fDrawList->AddPolyline(dc.fScreenPoints.begin(), 3, 0xFFFFFFFF, true, 1.5f);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001627}
1628
Ben Wagner3627d2e2018-06-26 14:23:20 -04001629static bool ImGui_DragLocation(SkPoint* pt) {
Brian Osman535c5e32019-02-09 16:32:58 -05001630 ImGui::DragCanvas dc(pt);
1631 dc.fillColor(IM_COL32(0, 0, 0, 128));
1632 dc.dragPoint(pt);
1633 return dc.fDragging;
Ben Wagner3627d2e2018-06-26 14:23:20 -04001634}
1635
Brian Osman9bb47cf2018-04-26 15:55:00 -04001636static bool ImGui_DragQuad(SkPoint* pts) {
Brian Osman535c5e32019-02-09 16:32:58 -05001637 ImGui::DragCanvas dc(pts);
1638 dc.fillColor(IM_COL32(0, 0, 0, 128));
Brian Osman9bb47cf2018-04-26 15:55:00 -04001639
Brian Osman535c5e32019-02-09 16:32:58 -05001640 for (int i = 0; i < 4; ++i) {
1641 dc.dragPoint(pts + i);
1642 }
Brian Osman9bb47cf2018-04-26 15:55:00 -04001643
Brian Osman535c5e32019-02-09 16:32:58 -05001644 dc.fDrawList->AddLine(dc.fScreenPoints[0], dc.fScreenPoints[1], 0xFFFFFFFF);
1645 dc.fDrawList->AddLine(dc.fScreenPoints[1], dc.fScreenPoints[3], 0xFFFFFFFF);
1646 dc.fDrawList->AddLine(dc.fScreenPoints[3], dc.fScreenPoints[2], 0xFFFFFFFF);
1647 dc.fDrawList->AddLine(dc.fScreenPoints[2], dc.fScreenPoints[0], 0xFFFFFFFF);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001648
Brian Osman535c5e32019-02-09 16:32:58 -05001649 return dc.fDragging;
Brian Osmana109e392017-02-24 09:49:14 -05001650}
1651
Brian Osmand67e5182017-12-08 16:46:09 -05001652void Viewer::drawImGui() {
Brian Osman79086b92017-02-10 13:36:16 -05001653 // Support drawing the ImGui demo window. Superfluous, but gives a good idea of what's possible
1654 if (fShowImGuiTestWindow) {
Brian Osman7197e052018-06-29 14:30:48 -04001655 ImGui::ShowDemoWindow(&fShowImGuiTestWindow);
Brian Osman79086b92017-02-10 13:36:16 -05001656 }
1657
1658 if (fShowImGuiDebugWindow) {
Brian Osmana109e392017-02-24 09:49:14 -05001659 // We have some dynamic content that sizes to fill available size. If the scroll bar isn't
1660 // always visible, we can end up in a layout feedback loop.
Brian Osman7197e052018-06-29 14:30:48 -04001661 ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
Brian Salomon99a33902017-03-07 15:16:34 -05001662 DisplayParams params = fWindow->getRequestedDisplayParams();
1663 bool paramsChanged = false;
Brian Osman0b8bb882019-04-12 11:47:19 -04001664 const GrContext* ctx = fWindow->getGrContext();
1665
Brian Osmana109e392017-02-24 09:49:14 -05001666 if (ImGui::Begin("Tools", &fShowImGuiDebugWindow,
1667 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
Brian Osman621491e2017-02-28 15:45:01 -05001668 if (ImGui::CollapsingHeader("Backend")) {
1669 int newBackend = static_cast<int>(fBackendType);
1670 ImGui::RadioButton("Raster", &newBackend, sk_app::Window::kRaster_BackendType);
1671 ImGui::SameLine();
1672 ImGui::RadioButton("OpenGL", &newBackend, sk_app::Window::kNativeGL_BackendType);
Brian Salomon194db172017-08-17 14:37:06 -04001673#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
1674 ImGui::SameLine();
1675 ImGui::RadioButton("ANGLE", &newBackend, sk_app::Window::kANGLE_BackendType);
1676#endif
Stephen Whitea800ec92019-08-02 15:04:52 -04001677#if defined(SK_DAWN)
1678 ImGui::SameLine();
1679 ImGui::RadioButton("Dawn", &newBackend, sk_app::Window::kDawn_BackendType);
1680#endif
Brian Osman621491e2017-02-28 15:45:01 -05001681#if defined(SK_VULKAN)
1682 ImGui::SameLine();
1683 ImGui::RadioButton("Vulkan", &newBackend, sk_app::Window::kVulkan_BackendType);
1684#endif
Jim Van Verthe58d5322019-09-03 09:42:57 -04001685#if defined(SK_METAL)
Jim Van Verthbe39f712019-02-08 15:36:14 -05001686 ImGui::SameLine();
1687 ImGui::RadioButton("Metal", &newBackend, sk_app::Window::kMetal_BackendType);
1688#endif
Brian Osman621491e2017-02-28 15:45:01 -05001689 if (newBackend != fBackendType) {
1690 fDeferredActions.push_back([=]() {
1691 this->setBackend(static_cast<sk_app::Window::BackendType>(newBackend));
1692 });
1693 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001694
Jim Van Verthfbdc0802017-05-02 16:15:53 -04001695 bool* wire = &params.fGrContextOptions.fWireframeMode;
1696 if (ctx && ImGui::Checkbox("Wireframe Mode", wire)) {
1697 paramsChanged = true;
1698 }
Brian Salomon99a33902017-03-07 15:16:34 -05001699
Brian Osman28b12522017-03-08 17:10:24 -05001700 if (ctx) {
1701 int sampleCount = fWindow->sampleCount();
1702 ImGui::Text("MSAA: "); ImGui::SameLine();
Brian Salomonbdecacf2018-02-02 20:32:49 -05001703 ImGui::RadioButton("1", &sampleCount, 1); ImGui::SameLine();
Brian Osman28b12522017-03-08 17:10:24 -05001704 ImGui::RadioButton("4", &sampleCount, 4); ImGui::SameLine();
1705 ImGui::RadioButton("8", &sampleCount, 8); ImGui::SameLine();
1706 ImGui::RadioButton("16", &sampleCount, 16);
1707
1708 if (sampleCount != params.fMSAASampleCount) {
1709 params.fMSAASampleCount = sampleCount;
1710 paramsChanged = true;
1711 }
1712 }
1713
Ben Wagner37c54032018-04-13 14:30:23 -04001714 int pixelGeometryIdx = 0;
1715 if (fPixelGeometryOverrides) {
1716 pixelGeometryIdx = params.fSurfaceProps.pixelGeometry() + 1;
1717 }
1718 if (ImGui::Combo("Pixel Geometry", &pixelGeometryIdx,
1719 "Default\0Flat\0RGB\0BGR\0RGBV\0BGRV\0\0"))
1720 {
1721 uint32_t flags = params.fSurfaceProps.flags();
1722 if (pixelGeometryIdx == 0) {
1723 fPixelGeometryOverrides = false;
1724 params.fSurfaceProps = SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
1725 } else {
1726 fPixelGeometryOverrides = true;
1727 SkPixelGeometry pixelGeometry = SkTo<SkPixelGeometry>(pixelGeometryIdx - 1);
1728 params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
1729 }
1730 paramsChanged = true;
1731 }
1732
1733 bool useDFT = params.fSurfaceProps.isUseDeviceIndependentFonts();
1734 if (ImGui::Checkbox("DFT", &useDFT)) {
1735 uint32_t flags = params.fSurfaceProps.flags();
1736 if (useDFT) {
1737 flags |= SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
1738 } else {
1739 flags &= ~SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
1740 }
1741 SkPixelGeometry pixelGeometry = params.fSurfaceProps.pixelGeometry();
1742 params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
1743 paramsChanged = true;
1744 }
1745
Brian Osman8a9de3d2017-03-01 14:59:05 -05001746 if (ImGui::TreeNode("Path Renderers")) {
Brian Osman8a9de3d2017-03-01 14:59:05 -05001747 GpuPathRenderers prevPr = params.fGrContextOptions.fGpuPathRenderers;
Brian Osman8a9de3d2017-03-01 14:59:05 -05001748 auto prButton = [&](GpuPathRenderers x) {
1749 if (ImGui::RadioButton(gPathRendererNames[x].c_str(), prevPr == x)) {
Brian Salomon99a33902017-03-07 15:16:34 -05001750 if (x != params.fGrContextOptions.fGpuPathRenderers) {
1751 params.fGrContextOptions.fGpuPathRenderers = x;
1752 paramsChanged = true;
1753 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001754 }
1755 };
1756
1757 if (!ctx) {
1758 ImGui::RadioButton("Software", true);
Brian Osman8a9de3d2017-03-01 14:59:05 -05001759 } else {
Chris Dalton37ae4b02019-12-28 14:51:11 -07001760 const auto* caps = ctx->priv().caps();
1761 prButton(GpuPathRenderers::kDefault);
1762 if (fWindow->sampleCount() > 1 || caps->mixedSamplesSupport()) {
Chris Daltonb832ce62020-01-06 19:49:37 -07001763 if (caps->shaderCaps()->tessellationSupport()) {
Chris Dalton0a22b1e2020-03-26 11:52:15 -06001764 prButton(GpuPathRenderers::kTessellation);
Chris Daltonb832ce62020-01-06 19:49:37 -07001765 }
Chris Dalton37ae4b02019-12-28 14:51:11 -07001766 if (caps->shaderCaps()->pathRenderingSupport()) {
1767 prButton(GpuPathRenderers::kStencilAndCover);
1768 }
Chris Dalton1a325d22017-07-14 15:17:41 -06001769 }
Chris Dalton37ae4b02019-12-28 14:51:11 -07001770 if (1 == fWindow->sampleCount()) {
1771 if (GrCoverageCountingPathRenderer::IsSupported(*caps)) {
1772 prButton(GpuPathRenderers::kCoverageCounting);
1773 }
1774 prButton(GpuPathRenderers::kSmall);
1775 }
Chris Dalton17dc4182020-03-25 16:18:16 -06001776 prButton(GpuPathRenderers::kTriangulating);
Brian Osman8a9de3d2017-03-01 14:59:05 -05001777 prButton(GpuPathRenderers::kNone);
1778 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001779 ImGui::TreePop();
1780 }
Brian Osman621491e2017-02-28 15:45:01 -05001781 }
1782
Ben Wagner964571d2019-03-08 12:35:06 -05001783 if (ImGui::CollapsingHeader("Tiling")) {
1784 ImGui::Checkbox("Enable", &fTiled);
1785 ImGui::Checkbox("Draw Boundaries", &fDrawTileBoundaries);
1786 ImGui::SliderFloat("Horizontal", &fTileScale.fWidth, 0.1f, 1.0f);
1787 ImGui::SliderFloat("Vertical", &fTileScale.fHeight, 0.1f, 1.0f);
1788 }
1789
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001790 if (ImGui::CollapsingHeader("Transform")) {
1791 float zoom = fZoomLevel;
1792 if (ImGui::SliderFloat("Zoom", &zoom, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
1793 fZoomLevel = zoom;
1794 this->preTouchMatrixChanged();
1795 paramsChanged = true;
1796 }
1797 float deg = fRotation;
Ben Wagnercb139352018-05-04 10:33:04 -04001798 if (ImGui::SliderFloat("Rotate", &deg, -30, 360, "%.3f deg")) {
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001799 fRotation = deg;
1800 this->preTouchMatrixChanged();
1801 paramsChanged = true;
1802 }
Ben Wagner3627d2e2018-06-26 14:23:20 -04001803 if (ImGui::CollapsingHeader("Subpixel offset", ImGuiTreeNodeFlags_NoTreePushOnOpen)) {
1804 if (ImGui_DragLocation(&fOffset)) {
1805 this->preTouchMatrixChanged();
1806 paramsChanged = true;
1807 }
Ben Wagner897dfa22018-08-09 15:18:46 -04001808 } else if (fOffset != SkVector{0.5f, 0.5f}) {
1809 this->preTouchMatrixChanged();
1810 paramsChanged = true;
1811 fOffset = {0.5f, 0.5f};
Ben Wagner3627d2e2018-06-26 14:23:20 -04001812 }
Brian Osman805a7272018-05-02 15:40:20 -04001813 int perspectiveMode = static_cast<int>(fPerspectiveMode);
1814 if (ImGui::Combo("Perspective", &perspectiveMode, "Off\0Real\0Fake\0\0")) {
1815 fPerspectiveMode = static_cast<PerspectiveMode>(perspectiveMode);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001816 this->preTouchMatrixChanged();
Ben Wagner3627d2e2018-06-26 14:23:20 -04001817 paramsChanged = true;
Brian Osman9bb47cf2018-04-26 15:55:00 -04001818 }
Ben Wagner3627d2e2018-06-26 14:23:20 -04001819 if (perspectiveMode != kPerspective_Off && ImGui_DragQuad(fPerspectivePoints)) {
Brian Osman9bb47cf2018-04-26 15:55:00 -04001820 this->preTouchMatrixChanged();
Ben Wagner3627d2e2018-06-26 14:23:20 -04001821 paramsChanged = true;
Brian Osman9bb47cf2018-04-26 15:55:00 -04001822 }
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001823 }
1824
Ben Wagnera580fb32018-04-17 11:16:32 -04001825 if (ImGui::CollapsingHeader("Paint")) {
Ben Wagnera580fb32018-04-17 11:16:32 -04001826 int aliasIdx = 0;
Ben Wagner9613e452019-01-23 10:34:59 -05001827 if (fPaintOverrides.fAntiAlias) {
1828 aliasIdx = SkTo<int>(fPaintOverrides.fAntiAliasState) + 1;
Ben Wagnera580fb32018-04-17 11:16:32 -04001829 }
1830 if (ImGui::Combo("Anti-Alias", &aliasIdx,
Mike Kleine5acd752019-03-22 09:57:16 -05001831 "Default\0Alias\0Normal\0AnalyticAAEnabled\0AnalyticAAForced\0\0"))
Ben Wagnera580fb32018-04-17 11:16:32 -04001832 {
1833 gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA;
1834 gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA;
Ben Wagnera580fb32018-04-17 11:16:32 -04001835 if (aliasIdx == 0) {
Ben Wagner9613e452019-01-23 10:34:59 -05001836 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
1837 fPaintOverrides.fAntiAlias = false;
Ben Wagnera580fb32018-04-17 11:16:32 -04001838 } else {
Ben Wagner9613e452019-01-23 10:34:59 -05001839 fPaintOverrides.fAntiAlias = true;
1840 fPaintOverrides.fAntiAliasState = SkTo<SkPaintFields::AntiAliasState>(aliasIdx-1);
Ben Wagnera580fb32018-04-17 11:16:32 -04001841 fPaint.setAntiAlias(aliasIdx > 1);
Ben Wagner9613e452019-01-23 10:34:59 -05001842 switch (fPaintOverrides.fAntiAliasState) {
Ben Wagnera580fb32018-04-17 11:16:32 -04001843 case SkPaintFields::AntiAliasState::Alias:
1844 break;
1845 case SkPaintFields::AntiAliasState::Normal:
1846 break;
1847 case SkPaintFields::AntiAliasState::AnalyticAAEnabled:
1848 gSkUseAnalyticAA = true;
1849 gSkForceAnalyticAA = false;
Ben Wagnera580fb32018-04-17 11:16:32 -04001850 break;
1851 case SkPaintFields::AntiAliasState::AnalyticAAForced:
1852 gSkUseAnalyticAA = gSkForceAnalyticAA = true;
Ben Wagnera580fb32018-04-17 11:16:32 -04001853 break;
1854 }
1855 }
1856 paramsChanged = true;
1857 }
1858
Ben Wagner99a78dc2018-05-09 18:23:51 -04001859 auto paintFlag = [this, &paramsChanged](const char* label, const char* items,
Ben Wagner9613e452019-01-23 10:34:59 -05001860 bool SkPaintFields::* flag,
Ben Wagner99a78dc2018-05-09 18:23:51 -04001861 bool (SkPaint::* isFlag)() const,
1862 void (SkPaint::* setFlag)(bool) )
Ben Wagnera580fb32018-04-17 11:16:32 -04001863 {
Ben Wagner99a78dc2018-05-09 18:23:51 -04001864 int itemIndex = 0;
Ben Wagner9613e452019-01-23 10:34:59 -05001865 if (fPaintOverrides.*flag) {
Ben Wagner99a78dc2018-05-09 18:23:51 -04001866 itemIndex = (fPaint.*isFlag)() ? 2 : 1;
Ben Wagnera580fb32018-04-17 11:16:32 -04001867 }
Ben Wagner99a78dc2018-05-09 18:23:51 -04001868 if (ImGui::Combo(label, &itemIndex, items)) {
1869 if (itemIndex == 0) {
Ben Wagner9613e452019-01-23 10:34:59 -05001870 fPaintOverrides.*flag = false;
Ben Wagner99a78dc2018-05-09 18:23:51 -04001871 } else {
Ben Wagner9613e452019-01-23 10:34:59 -05001872 fPaintOverrides.*flag = true;
Ben Wagner99a78dc2018-05-09 18:23:51 -04001873 (fPaint.*setFlag)(itemIndex == 2);
1874 }
1875 paramsChanged = true;
1876 }
1877 };
Ben Wagnera580fb32018-04-17 11:16:32 -04001878
Ben Wagner99a78dc2018-05-09 18:23:51 -04001879 paintFlag("Dither",
1880 "Default\0No Dither\0Dither\0\0",
Ben Wagner9613e452019-01-23 10:34:59 -05001881 &SkPaintFields::fDither,
Ben Wagner99a78dc2018-05-09 18:23:51 -04001882 &SkPaint::isDither, &SkPaint::setDither);
Ben Wagnerd10a78f2019-03-07 13:14:26 -05001883
1884 int filterQualityIdx = 0;
1885 if (fPaintOverrides.fFilterQuality) {
1886 filterQualityIdx = SkTo<int>(fPaint.getFilterQuality()) + 1;
1887 }
1888 if (ImGui::Combo("Filter Quality", &filterQualityIdx,
1889 "Default\0None\0Low\0Medium\0High\0\0"))
1890 {
1891 if (filterQualityIdx == 0) {
1892 fPaintOverrides.fFilterQuality = false;
1893 fPaint.setFilterQuality(kNone_SkFilterQuality);
1894 } else {
1895 fPaint.setFilterQuality(SkTo<SkFilterQuality>(filterQualityIdx - 1));
1896 fPaintOverrides.fFilterQuality = true;
1897 }
1898 paramsChanged = true;
1899 }
Ben Wagner9613e452019-01-23 10:34:59 -05001900 }
Hal Canary02738a82019-01-21 18:51:32 +00001901
Ben Wagner9613e452019-01-23 10:34:59 -05001902 if (ImGui::CollapsingHeader("Font")) {
1903 int hintingIdx = 0;
1904 if (fFontOverrides.fHinting) {
1905 hintingIdx = SkTo<int>(fFont.getHinting()) + 1;
1906 }
1907 if (ImGui::Combo("Hinting", &hintingIdx,
1908 "Default\0None\0Slight\0Normal\0Full\0\0"))
1909 {
1910 if (hintingIdx == 0) {
1911 fFontOverrides.fHinting = false;
Ben Wagner5785e4a2019-05-07 16:50:29 -04001912 fFont.setHinting(SkFontHinting::kNone);
Ben Wagner9613e452019-01-23 10:34:59 -05001913 } else {
1914 fFont.setHinting(SkTo<SkFontHinting>(hintingIdx - 1));
1915 fFontOverrides.fHinting = true;
1916 }
1917 paramsChanged = true;
1918 }
Hal Canary02738a82019-01-21 18:51:32 +00001919
Ben Wagner9613e452019-01-23 10:34:59 -05001920 auto fontFlag = [this, &paramsChanged](const char* label, const char* items,
1921 bool SkFontFields::* flag,
1922 bool (SkFont::* isFlag)() const,
1923 void (SkFont::* setFlag)(bool) )
1924 {
1925 int itemIndex = 0;
1926 if (fFontOverrides.*flag) {
1927 itemIndex = (fFont.*isFlag)() ? 2 : 1;
1928 }
1929 if (ImGui::Combo(label, &itemIndex, items)) {
1930 if (itemIndex == 0) {
1931 fFontOverrides.*flag = false;
1932 } else {
1933 fFontOverrides.*flag = true;
1934 (fFont.*setFlag)(itemIndex == 2);
1935 }
1936 paramsChanged = true;
1937 }
1938 };
Hal Canary02738a82019-01-21 18:51:32 +00001939
Ben Wagner9613e452019-01-23 10:34:59 -05001940 fontFlag("Fake Bold Glyphs",
1941 "Default\0No Fake Bold\0Fake Bold\0\0",
1942 &SkFontFields::fEmbolden,
1943 &SkFont::isEmbolden, &SkFont::setEmbolden);
Hal Canary02738a82019-01-21 18:51:32 +00001944
Ben Wagnerc17de1d2019-08-26 16:59:09 -04001945 fontFlag("Baseline Snapping",
1946 "Default\0No Baseline Snapping\0Baseline Snapping\0\0",
1947 &SkFontFields::fBaselineSnap,
1948 &SkFont::isBaselineSnap, &SkFont::setBaselineSnap);
1949
Ben Wagner9613e452019-01-23 10:34:59 -05001950 fontFlag("Linear Text",
1951 "Default\0No Linear Text\0Linear Text\0\0",
1952 &SkFontFields::fLinearMetrics,
1953 &SkFont::isLinearMetrics, &SkFont::setLinearMetrics);
Hal Canary02738a82019-01-21 18:51:32 +00001954
Ben Wagner9613e452019-01-23 10:34:59 -05001955 fontFlag("Subpixel Position Glyphs",
1956 "Default\0Pixel Text\0Subpixel Text\0\0",
1957 &SkFontFields::fSubpixel,
1958 &SkFont::isSubpixel, &SkFont::setSubpixel);
1959
1960 fontFlag("Embedded Bitmap Text",
1961 "Default\0No Embedded Bitmaps\0Embedded Bitmaps\0\0",
1962 &SkFontFields::fEmbeddedBitmaps,
1963 &SkFont::isEmbeddedBitmaps, &SkFont::setEmbeddedBitmaps);
1964
1965 fontFlag("Force Auto-Hinting",
1966 "Default\0No Force Auto-Hinting\0Force Auto-Hinting\0\0",
1967 &SkFontFields::fForceAutoHinting,
1968 &SkFont::isForceAutoHinting, &SkFont::setForceAutoHinting);
1969
1970 int edgingIdx = 0;
1971 if (fFontOverrides.fEdging) {
1972 edgingIdx = SkTo<int>(fFont.getEdging()) + 1;
1973 }
1974 if (ImGui::Combo("Edging", &edgingIdx,
1975 "Default\0Alias\0Antialias\0Subpixel Antialias\0\0"))
1976 {
1977 if (edgingIdx == 0) {
1978 fFontOverrides.fEdging = false;
1979 fFont.setEdging(SkFont::Edging::kAlias);
1980 } else {
1981 fFont.setEdging(SkTo<SkFont::Edging>(edgingIdx-1));
1982 fFontOverrides.fEdging = true;
1983 }
1984 paramsChanged = true;
1985 }
1986
Ben Wagner15a8d572019-03-21 13:35:44 -04001987 ImGui::Checkbox("Override Size", &fFontOverrides.fSize);
1988 if (fFontOverrides.fSize) {
1989 ImGui::DragFloat2("TextRange", fFontOverrides.fSizeRange,
Ben Wagnerd2ae4df2018-06-07 17:54:07 -04001990 0.001f, -10.0f, 300.0f, "%.6f", 2.0f);
Mike Reed3ae47332019-01-04 10:11:46 -05001991 float textSize = fFont.getSize();
Ben Wagnerd2ae4df2018-06-07 17:54:07 -04001992 if (ImGui::DragFloat("TextSize", &textSize, 0.001f,
Ben Wagner15a8d572019-03-21 13:35:44 -04001993 fFontOverrides.fSizeRange[0],
1994 fFontOverrides.fSizeRange[1],
Ben Wagnerd2ae4df2018-06-07 17:54:07 -04001995 "%.6f", 2.0f))
1996 {
Mike Reed3ae47332019-01-04 10:11:46 -05001997 fFont.setSize(textSize);
Ben Wagner15a8d572019-03-21 13:35:44 -04001998 paramsChanged = true;
1999 }
2000 }
2001
2002 ImGui::Checkbox("Override ScaleX", &fFontOverrides.fScaleX);
2003 if (fFontOverrides.fScaleX) {
2004 float scaleX = fFont.getScaleX();
2005 if (ImGui::SliderFloat("ScaleX", &scaleX, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
2006 fFont.setScaleX(scaleX);
2007 paramsChanged = true;
2008 }
2009 }
2010
2011 ImGui::Checkbox("Override SkewX", &fFontOverrides.fSkewX);
2012 if (fFontOverrides.fSkewX) {
2013 float skewX = fFont.getSkewX();
2014 if (ImGui::SliderFloat("SkewX", &skewX, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
2015 fFont.setSkewX(skewX);
Ben Wagnerd2ae4df2018-06-07 17:54:07 -04002016 paramsChanged = true;
2017 }
2018 }
Ben Wagnera580fb32018-04-17 11:16:32 -04002019 }
2020
Mike Reed81f60ec2018-05-15 10:09:52 -04002021 {
2022 SkMetaData controls;
2023 if (fSlides[fCurrentSlide]->onGetControls(&controls)) {
2024 if (ImGui::CollapsingHeader("Current Slide")) {
2025 SkMetaData::Iter iter(controls);
2026 const char* name;
2027 SkMetaData::Type type;
2028 int count;
Brian Osman61fb4bb2018-08-03 11:14:02 -04002029 while ((name = iter.next(&type, &count)) != nullptr) {
Mike Reed81f60ec2018-05-15 10:09:52 -04002030 if (type == SkMetaData::kScalar_Type) {
2031 float val[3];
2032 SkASSERT(count == 3);
2033 controls.findScalars(name, &count, val);
2034 if (ImGui::SliderFloat(name, &val[0], val[1], val[2])) {
2035 controls.setScalars(name, 3, val);
Mike Reed81f60ec2018-05-15 10:09:52 -04002036 }
Ben Wagner110c7032019-03-22 17:03:59 -04002037 } else if (type == SkMetaData::kBool_Type) {
2038 bool val;
2039 SkASSERT(count == 1);
2040 controls.findBool(name, &val);
2041 if (ImGui::Checkbox(name, &val)) {
2042 controls.setBool(name, val);
2043 }
Mike Reed81f60ec2018-05-15 10:09:52 -04002044 }
2045 }
Brian Osman61fb4bb2018-08-03 11:14:02 -04002046 fSlides[fCurrentSlide]->onSetControls(controls);
Mike Reed81f60ec2018-05-15 10:09:52 -04002047 }
2048 }
2049 }
2050
Ben Wagner7a3c6742018-04-23 10:01:07 -04002051 if (fShowSlidePicker) {
2052 ImGui::SetNextTreeNodeOpen(true);
2053 }
Brian Osman79086b92017-02-10 13:36:16 -05002054 if (ImGui::CollapsingHeader("Slide")) {
2055 static ImGuiTextFilter filter;
Brian Osmanf479e422017-11-08 13:11:36 -05002056 static ImVector<const char*> filteredSlideNames;
2057 static ImVector<int> filteredSlideIndices;
2058
Brian Osmanfce09c52017-11-14 15:32:20 -05002059 if (fShowSlidePicker) {
2060 ImGui::SetKeyboardFocusHere();
2061 fShowSlidePicker = false;
2062 }
2063
Brian Osman79086b92017-02-10 13:36:16 -05002064 filter.Draw();
Brian Osmanf479e422017-11-08 13:11:36 -05002065 filteredSlideNames.clear();
2066 filteredSlideIndices.clear();
2067 int filteredIndex = 0;
2068 for (int i = 0; i < fSlides.count(); ++i) {
2069 const char* slideName = fSlides[i]->getName().c_str();
2070 if (filter.PassFilter(slideName) || i == fCurrentSlide) {
2071 if (i == fCurrentSlide) {
2072 filteredIndex = filteredSlideIndices.size();
Brian Osman79086b92017-02-10 13:36:16 -05002073 }
Brian Osmanf479e422017-11-08 13:11:36 -05002074 filteredSlideNames.push_back(slideName);
2075 filteredSlideIndices.push_back(i);
Brian Osman79086b92017-02-10 13:36:16 -05002076 }
Brian Osman79086b92017-02-10 13:36:16 -05002077 }
Brian Osmanf479e422017-11-08 13:11:36 -05002078
Brian Osmanf479e422017-11-08 13:11:36 -05002079 if (ImGui::ListBox("", &filteredIndex, filteredSlideNames.begin(),
2080 filteredSlideNames.size(), 20)) {
Florin Malitaab99c342018-01-16 16:23:03 -05002081 this->setCurrentSlide(filteredSlideIndices[filteredIndex]);
Brian Osman79086b92017-02-10 13:36:16 -05002082 }
2083 }
Brian Osmana109e392017-02-24 09:49:14 -05002084
2085 if (ImGui::CollapsingHeader("Color Mode")) {
Brian Osman92004802017-03-06 11:47:26 -05002086 ColorMode newMode = fColorMode;
2087 auto cmButton = [&](ColorMode mode, const char* label) {
2088 if (ImGui::RadioButton(label, mode == fColorMode)) {
2089 newMode = mode;
2090 }
2091 };
2092
2093 cmButton(ColorMode::kLegacy, "Legacy 8888");
Brian Osman03115dc2018-11-26 13:55:19 -05002094 cmButton(ColorMode::kColorManaged8888, "Color Managed 8888");
2095 cmButton(ColorMode::kColorManagedF16, "Color Managed F16");
Brian Salomon8391bac2019-09-18 11:22:44 -04002096 cmButton(ColorMode::kColorManagedF16Norm, "Color Managed F16 Norm");
Brian Osman92004802017-03-06 11:47:26 -05002097
2098 if (newMode != fColorMode) {
Brian Osman03115dc2018-11-26 13:55:19 -05002099 this->setColorMode(newMode);
Brian Osmana109e392017-02-24 09:49:14 -05002100 }
2101
2102 // Pick from common gamuts:
2103 int primariesIdx = 4; // Default: Custom
2104 for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
2105 if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
2106 primariesIdx = i;
2107 break;
2108 }
2109 }
2110
Brian Osman03115dc2018-11-26 13:55:19 -05002111 // Let user adjust the gamma
Brian Osman82ebe042019-01-04 17:03:00 -05002112 ImGui::SliderFloat("Gamma", &fColorSpaceTransferFn.g, 0.5f, 3.5f);
Brian Osmanfdab5762017-11-09 10:27:55 -05002113
Brian Osmana109e392017-02-24 09:49:14 -05002114 if (ImGui::Combo("Primaries", &primariesIdx,
2115 "sRGB\0AdobeRGB\0P3\0Rec. 2020\0Custom\0\0")) {
2116 if (primariesIdx >= 0 && primariesIdx <= 3) {
2117 fColorSpacePrimaries = *gNamedPrimaries[primariesIdx].fPrimaries;
2118 }
2119 }
2120
2121 // Allow direct editing of gamut
2122 ImGui_Primaries(&fColorSpacePrimaries, &fImGuiGamutPaint);
2123 }
Brian Osman207d4102019-01-10 09:40:58 -05002124
2125 if (ImGui::CollapsingHeader("Animation")) {
Hal Canary41248072019-07-11 16:32:53 -04002126 bool isPaused = AnimTimer::kPaused_State == fAnimTimer.state();
Brian Osman207d4102019-01-10 09:40:58 -05002127 if (ImGui::Checkbox("Pause", &isPaused)) {
2128 fAnimTimer.togglePauseResume();
2129 }
Brian Osman707d2022019-01-10 11:27:34 -05002130
2131 float speed = fAnimTimer.getSpeed();
2132 if (ImGui::DragFloat("Speed", &speed, 0.1f)) {
2133 fAnimTimer.setSpeed(speed);
2134 }
Brian Osman207d4102019-01-10 09:40:58 -05002135 }
Brian Osman0b8bb882019-04-12 11:47:19 -04002136
Brian Osmanfd7657c2019-04-25 11:34:07 -04002137 bool backendIsGL = Window::kNativeGL_BackendType == fBackendType
2138#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
2139 || Window::kANGLE_BackendType == fBackendType
2140#endif
2141 ;
2142
2143 // HACK: If we get here when SKSL caching isn't enabled, and we're on a backend other
2144 // than GL, we need to force it on. Just do that on the first frame after the backend
2145 // switch, then resume normal operation.
Brian Osmana66081d2019-09-03 14:59:26 -04002146 if (!backendIsGL &&
2147 params.fGrContextOptions.fShaderCacheStrategy !=
2148 GrContextOptions::ShaderCacheStrategy::kSkSL) {
2149 params.fGrContextOptions.fShaderCacheStrategy =
2150 GrContextOptions::ShaderCacheStrategy::kSkSL;
Brian Osmanfd7657c2019-04-25 11:34:07 -04002151 paramsChanged = true;
2152 fPersistentCache.reset();
2153 } else if (ImGui::CollapsingHeader("Shaders")) {
Brian Osman0b8bb882019-04-12 11:47:19 -04002154 // To re-load shaders from the currently active programs, we flush all caches on one
2155 // frame, then set a flag to poll the cache on the next frame.
2156 static bool gLoadPending = false;
2157 if (gLoadPending) {
2158 auto collectShaders = [this](sk_sp<const SkData> key, sk_sp<SkData> data,
2159 int hitCount) {
2160 CachedGLSL& entry(fCachedGLSL.push_back());
2161 entry.fKey = key;
2162 SkMD5 hash;
2163 hash.write(key->bytes(), key->size());
2164 SkMD5::Digest digest = hash.finish();
2165 for (int i = 0; i < 16; ++i) {
2166 entry.fKeyString.appendf("%02x", digest.data[i]);
2167 }
2168
Brian Osmana66081d2019-09-03 14:59:26 -04002169 SkReader32 reader(data->data(), data->size());
Brian Osman1facd5e2020-03-16 16:21:24 -04002170 entry.fShaderType = GrPersistentCacheUtils::GetType(&reader);
Brian Osmana66081d2019-09-03 14:59:26 -04002171 GrPersistentCacheUtils::UnpackCachedShaders(&reader, entry.fShader,
2172 entry.fInputs,
2173 kGrShaderTypeCount);
Brian Osman0b8bb882019-04-12 11:47:19 -04002174 };
2175 fCachedGLSL.reset();
2176 fPersistentCache.foreach(collectShaders);
2177 gLoadPending = false;
2178 }
2179
2180 // Defer actually doing the load/save logic so that we can trigger a save when we
2181 // start or finish hovering on a tree node in the list below:
2182 bool doLoad = ImGui::Button("Load"); ImGui::SameLine();
Brian Osmanfd7657c2019-04-25 11:34:07 -04002183 bool doSave = ImGui::Button("Save");
2184 if (backendIsGL) {
2185 ImGui::SameLine();
Brian Osmana66081d2019-09-03 14:59:26 -04002186 bool sksl = params.fGrContextOptions.fShaderCacheStrategy ==
2187 GrContextOptions::ShaderCacheStrategy::kSkSL;
2188 if (ImGui::Checkbox("SkSL", &sksl)) {
2189 params.fGrContextOptions.fShaderCacheStrategy = sksl
2190 ? GrContextOptions::ShaderCacheStrategy::kSkSL
2191 : GrContextOptions::ShaderCacheStrategy::kBackendSource;
Brian Osmanfd7657c2019-04-25 11:34:07 -04002192 paramsChanged = true;
2193 doLoad = true;
2194 fDeferredActions.push_back([=]() { fPersistentCache.reset(); });
2195 }
Brian Osmancbc33b82019-04-19 14:16:19 -04002196 }
Brian Osman0b8bb882019-04-12 11:47:19 -04002197
2198 ImGui::BeginChild("##ScrollingRegion");
2199 for (auto& entry : fCachedGLSL) {
2200 bool inTreeNode = ImGui::TreeNode(entry.fKeyString.c_str());
2201 bool hovered = ImGui::IsItemHovered();
2202 if (hovered != entry.fHovered) {
2203 // Force a save to patch the highlight shader in/out
2204 entry.fHovered = hovered;
2205 doSave = true;
2206 }
2207 if (inTreeNode) {
2208 // Full width, and a reasonable amount of space for each shader.
2209 ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 20.0f);
2210 ImGui::InputTextMultiline("##VP", &entry.fShader[kVertex_GrShaderType],
2211 boxSize);
2212 ImGui::InputTextMultiline("##FP", &entry.fShader[kFragment_GrShaderType],
2213 boxSize);
2214 ImGui::TreePop();
2215 }
2216 }
2217 ImGui::EndChild();
2218
2219 if (doLoad) {
2220 fPersistentCache.reset();
2221 fWindow->getGrContext()->priv().getGpu()->resetShaderCacheForTesting();
2222 gLoadPending = true;
2223 }
2224 if (doSave) {
2225 // The hovered item (if any) gets a special shader to make it identifiable
Brian Osman5bee3902019-05-07 09:55:45 -04002226 auto shaderCaps = ctx->priv().caps()->shaderCaps();
Brian Osmana66081d2019-09-03 14:59:26 -04002227 bool sksl = params.fGrContextOptions.fShaderCacheStrategy ==
2228 GrContextOptions::ShaderCacheStrategy::kSkSL;
Brian Osman5bee3902019-05-07 09:55:45 -04002229
Brian Osman072e6fc2019-06-12 11:35:41 -04002230 SkSL::String highlight;
2231 if (!sksl) {
2232 highlight = shaderCaps->versionDeclString();
2233 if (shaderCaps->usesPrecisionModifiers()) {
2234 highlight.append("precision mediump float;\n");
2235 }
Brian Osman5bee3902019-05-07 09:55:45 -04002236 }
2237 const char* f4Type = sksl ? "half4" : "vec4";
Brian Osmancbc33b82019-04-19 14:16:19 -04002238 highlight.appendf("out %s sk_FragColor;\n"
2239 "void main() { sk_FragColor = %s(1, 0, 1, 0.5); }",
2240 f4Type, f4Type);
Brian Osman0b8bb882019-04-12 11:47:19 -04002241
2242 fPersistentCache.reset();
2243 fWindow->getGrContext()->priv().getGpu()->resetShaderCacheForTesting();
2244 for (auto& entry : fCachedGLSL) {
2245 SkSL::String backup = entry.fShader[kFragment_GrShaderType];
2246 if (entry.fHovered) {
2247 entry.fShader[kFragment_GrShaderType] = highlight;
2248 }
2249
Brian Osmana085a412019-04-25 09:44:43 -04002250 auto data = GrPersistentCacheUtils::PackCachedShaders(entry.fShaderType,
2251 entry.fShader,
2252 entry.fInputs,
Brian Osman4524e842019-09-24 16:03:41 -04002253 kGrShaderTypeCount);
Brian Osman0b8bb882019-04-12 11:47:19 -04002254 fPersistentCache.store(*entry.fKey, *data);
2255
2256 entry.fShader[kFragment_GrShaderType] = backup;
2257 }
2258 }
2259 }
Brian Osman79086b92017-02-10 13:36:16 -05002260 }
Brian Salomon99a33902017-03-07 15:16:34 -05002261 if (paramsChanged) {
2262 fDeferredActions.push_back([=]() {
2263 fWindow->setRequestedDisplayParams(params);
2264 fWindow->inval();
2265 this->updateTitle();
2266 });
2267 }
Brian Osman79086b92017-02-10 13:36:16 -05002268 ImGui::End();
2269 }
2270
Brian Osman5e7fbfd2019-05-03 13:13:35 -04002271 if (gShaderErrorHandler.fErrors.count()) {
2272 ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
2273 ImGui::Begin("Shader Errors");
2274 for (int i = 0; i < gShaderErrorHandler.fErrors.count(); ++i) {
2275 ImGui::TextWrapped("%s", gShaderErrorHandler.fErrors[i].c_str());
Chris Dalton77912982019-12-16 11:18:13 -07002276 SkSL::String sksl(gShaderErrorHandler.fShaders[i].c_str());
2277 GrShaderUtils::VisitLineByLine(sksl, [](int lineNumber, const char* lineText) {
2278 ImGui::TextWrapped("%4i\t%s\n", lineNumber, lineText);
2279 });
Brian Osman5e7fbfd2019-05-03 13:13:35 -04002280 }
2281 ImGui::End();
2282 gShaderErrorHandler.reset();
2283 }
2284
Brian Osmanf6877092017-02-13 09:39:57 -05002285 if (fShowZoomWindow && fLastImage) {
Brian Osman7197e052018-06-29 14:30:48 -04002286 ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_FirstUseEver);
2287 if (ImGui::Begin("Zoom", &fShowZoomWindow)) {
Brian Osmanead517d2017-11-13 15:36:36 -05002288 static int zoomFactor = 8;
2289 if (ImGui::Button("<<")) {
Brian Osman788b9162020-02-07 10:36:46 -05002290 zoomFactor = std::max(zoomFactor / 2, 4);
Brian Osmanead517d2017-11-13 15:36:36 -05002291 }
2292 ImGui::SameLine(); ImGui::Text("%2d", zoomFactor); ImGui::SameLine();
2293 if (ImGui::Button(">>")) {
Brian Osman788b9162020-02-07 10:36:46 -05002294 zoomFactor = std::min(zoomFactor * 2, 32);
Brian Osmanead517d2017-11-13 15:36:36 -05002295 }
Brian Osmanf6877092017-02-13 09:39:57 -05002296
Ben Wagner3627d2e2018-06-26 14:23:20 -04002297 if (!fZoomWindowFixed) {
2298 ImVec2 mousePos = ImGui::GetMousePos();
2299 fZoomWindowLocation = SkPoint::Make(mousePos.x, mousePos.y);
2300 }
2301 SkScalar x = fZoomWindowLocation.x();
2302 SkScalar y = fZoomWindowLocation.y();
2303 int xInt = SkScalarRoundToInt(x);
2304 int yInt = SkScalarRoundToInt(y);
Brian Osmanf6877092017-02-13 09:39:57 -05002305 ImVec2 avail = ImGui::GetContentRegionAvail();
2306
Brian Osmanead517d2017-11-13 15:36:36 -05002307 uint32_t pixel = 0;
2308 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
Ben Wagner3627d2e2018-06-26 14:23:20 -04002309 if (fLastImage->readPixels(info, &pixel, info.minRowBytes(), xInt, yInt)) {
Brian Osmanead517d2017-11-13 15:36:36 -05002310 ImGui::SameLine();
Brian Osman22eeb3c2019-02-20 10:13:06 -05002311 ImGui::Text("(X, Y): %d, %d RGBA: %X %X %X %X",
Ben Wagner3627d2e2018-06-26 14:23:20 -04002312 xInt, yInt,
Brian Osman07b56b22017-11-21 14:59:31 -05002313 SkGetPackedR32(pixel), SkGetPackedG32(pixel),
Brian Osmanead517d2017-11-13 15:36:36 -05002314 SkGetPackedB32(pixel), SkGetPackedA32(pixel));
2315 }
2316
Brian Osmand67e5182017-12-08 16:46:09 -05002317 fImGuiLayer.skiaWidget(avail, [=](SkCanvas* c) {
Brian Osmanead517d2017-11-13 15:36:36 -05002318 // Translate so the region of the image that's under the mouse cursor is centered
2319 // in the zoom canvas:
2320 c->scale(zoomFactor, zoomFactor);
Ben Wagner3627d2e2018-06-26 14:23:20 -04002321 c->translate(avail.x * 0.5f / zoomFactor - x - 0.5f,
2322 avail.y * 0.5f / zoomFactor - y - 0.5f);
Brian Osmanead517d2017-11-13 15:36:36 -05002323 c->drawImage(this->fLastImage, 0, 0);
2324
2325 SkPaint outline;
2326 outline.setStyle(SkPaint::kStroke_Style);
Ben Wagner3627d2e2018-06-26 14:23:20 -04002327 c->drawRect(SkRect::MakeXYWH(x, y, 1, 1), outline);
Brian Osmanead517d2017-11-13 15:36:36 -05002328 });
Brian Osmanf6877092017-02-13 09:39:57 -05002329 }
2330
2331 ImGui::End();
2332 }
Brian Osman79086b92017-02-10 13:36:16 -05002333}
2334
liyuqian2edb0f42016-07-06 14:11:32 -07002335void Viewer::onIdle() {
Brian Osmanfd8f4d52017-02-24 11:57:23 -05002336 for (int i = 0; i < fDeferredActions.count(); ++i) {
2337 fDeferredActions[i]();
2338 }
2339 fDeferredActions.reset();
2340
Brian Osman56a24812017-12-19 11:15:16 -05002341 fStatsLayer.beginTiming(fAnimateTimer);
jvanverthc265a922016-04-08 12:51:45 -07002342 fAnimTimer.updateTime();
Hal Canary41248072019-07-11 16:32:53 -04002343 bool animateWantsInval = fSlides[fCurrentSlide]->animate(fAnimTimer.nanos());
Brian Osman56a24812017-12-19 11:15:16 -05002344 fStatsLayer.endTiming(fAnimateTimer);
Brian Osman1df161a2017-02-09 12:10:20 -05002345
Brian Osman79086b92017-02-10 13:36:16 -05002346 ImGuiIO& io = ImGui::GetIO();
Brian Osmanffee60f2018-08-03 13:03:19 -04002347 // ImGui always has at least one "active" window, which is the default "Debug" window. It may
2348 // not be visible, though. So we need to redraw if there is at least one visible window, or
2349 // more than one active window. Newly created windows are active but not visible for one frame
2350 // while they determine their layout and sizing.
2351 if (animateWantsInval || fStatsLayer.getActive() || fRefresh ||
2352 io.MetricsActiveWindows > 1 || io.MetricsRenderWindows > 0) {
jvanverthc265a922016-04-08 12:51:45 -07002353 fWindow->inval();
2354 }
jvanverth9f372462016-04-06 06:08:59 -07002355}
liyuqiane5a6cd92016-05-27 08:52:52 -07002356
Florin Malitab632df72018-06-18 21:23:06 -04002357template <typename OptionsFunc>
2358static void WriteStateObject(SkJSONWriter& writer, const char* name, const char* value,
2359 OptionsFunc&& optionsFunc) {
2360 writer.beginObject();
2361 {
2362 writer.appendString(kName , name);
2363 writer.appendString(kValue, value);
2364
2365 writer.beginArray(kOptions);
2366 {
2367 optionsFunc(writer);
2368 }
2369 writer.endArray();
2370 }
2371 writer.endObject();
2372}
2373
2374
liyuqiane5a6cd92016-05-27 08:52:52 -07002375void Viewer::updateUIState() {
csmartdalton578f0642017-02-24 16:04:47 -07002376 if (!fWindow) {
2377 return;
2378 }
Brian Salomonbdecacf2018-02-02 20:32:49 -05002379 if (fWindow->sampleCount() < 1) {
csmartdalton578f0642017-02-24 16:04:47 -07002380 return; // Surface hasn't been created yet.
2381 }
2382
Florin Malitab632df72018-06-18 21:23:06 -04002383 SkDynamicMemoryWStream memStream;
2384 SkJSONWriter writer(&memStream);
2385 writer.beginArray();
2386
liyuqianb73c24b2016-06-03 08:47:23 -07002387 // Slide state
Florin Malitab632df72018-06-18 21:23:06 -04002388 WriteStateObject(writer, kSlideStateName, fSlides[fCurrentSlide]->getName().c_str(),
2389 [this](SkJSONWriter& writer) {
2390 for(const auto& slide : fSlides) {
2391 writer.appendString(slide->getName().c_str());
2392 }
2393 });
liyuqiane5a6cd92016-05-27 08:52:52 -07002394
liyuqianb73c24b2016-06-03 08:47:23 -07002395 // Backend state
Florin Malitab632df72018-06-18 21:23:06 -04002396 WriteStateObject(writer, kBackendStateName, kBackendTypeStrings[fBackendType],
2397 [](SkJSONWriter& writer) {
2398 for (const auto& str : kBackendTypeStrings) {
2399 writer.appendString(str);
2400 }
2401 });
liyuqiane5a6cd92016-05-27 08:52:52 -07002402
csmartdalton578f0642017-02-24 16:04:47 -07002403 // MSAA state
Florin Malitab632df72018-06-18 21:23:06 -04002404 const auto countString = SkStringPrintf("%d", fWindow->sampleCount());
2405 WriteStateObject(writer, kMSAAStateName, countString.c_str(),
2406 [this](SkJSONWriter& writer) {
2407 writer.appendS32(0);
2408
2409 if (sk_app::Window::kRaster_BackendType == fBackendType) {
2410 return;
2411 }
2412
2413 for (int msaa : {4, 8, 16}) {
2414 writer.appendS32(msaa);
2415 }
2416 });
csmartdalton578f0642017-02-24 16:04:47 -07002417
csmartdalton61cd31a2017-02-27 17:00:53 -07002418 // Path renderer state
2419 GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
Florin Malitab632df72018-06-18 21:23:06 -04002420 WriteStateObject(writer, kPathRendererStateName, gPathRendererNames[pr].c_str(),
2421 [this](SkJSONWriter& writer) {
2422 const GrContext* ctx = fWindow->getGrContext();
2423 if (!ctx) {
2424 writer.appendString("Software");
2425 } else {
Robert Phillips9da87e02019-02-04 13:26:26 -05002426 const auto* caps = ctx->priv().caps();
Chris Dalton37ae4b02019-12-28 14:51:11 -07002427 writer.appendString(gPathRendererNames[GpuPathRenderers::kDefault].c_str());
2428 if (fWindow->sampleCount() > 1 || caps->mixedSamplesSupport()) {
Chris Daltonb832ce62020-01-06 19:49:37 -07002429 if (caps->shaderCaps()->tessellationSupport()) {
2430 writer.appendString(
Chris Dalton0a22b1e2020-03-26 11:52:15 -06002431 gPathRendererNames[GpuPathRenderers::kTessellation].c_str());
Chris Daltonb832ce62020-01-06 19:49:37 -07002432 }
Florin Malitab632df72018-06-18 21:23:06 -04002433 if (caps->shaderCaps()->pathRenderingSupport()) {
2434 writer.appendString(
Chris Dalton37ae4b02019-12-28 14:51:11 -07002435 gPathRendererNames[GpuPathRenderers::kStencilAndCover].c_str());
Florin Malitab632df72018-06-18 21:23:06 -04002436 }
Chris Dalton37ae4b02019-12-28 14:51:11 -07002437 }
2438 if (1 == fWindow->sampleCount()) {
Florin Malitab632df72018-06-18 21:23:06 -04002439 if(GrCoverageCountingPathRenderer::IsSupported(*caps)) {
2440 writer.appendString(
2441 gPathRendererNames[GpuPathRenderers::kCoverageCounting].c_str());
2442 }
2443 writer.appendString(gPathRendererNames[GpuPathRenderers::kSmall].c_str());
2444 }
Chris Dalton17dc4182020-03-25 16:18:16 -06002445 writer.appendString(gPathRendererNames[GpuPathRenderers::kTriangulating].c_str());
Chris Dalton37ae4b02019-12-28 14:51:11 -07002446 writer.appendString(gPathRendererNames[GpuPathRenderers::kNone].c_str());
Florin Malitab632df72018-06-18 21:23:06 -04002447 }
2448 });
csmartdalton61cd31a2017-02-27 17:00:53 -07002449
liyuqianb73c24b2016-06-03 08:47:23 -07002450 // Softkey state
Florin Malitab632df72018-06-18 21:23:06 -04002451 WriteStateObject(writer, kSoftkeyStateName, kSoftkeyHint,
2452 [this](SkJSONWriter& writer) {
2453 writer.appendString(kSoftkeyHint);
2454 for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
2455 writer.appendString(softkey.c_str());
2456 }
2457 });
liyuqianb73c24b2016-06-03 08:47:23 -07002458
Florin Malitab632df72018-06-18 21:23:06 -04002459 writer.endArray();
2460 writer.flush();
liyuqiane5a6cd92016-05-27 08:52:52 -07002461
Florin Malitab632df72018-06-18 21:23:06 -04002462 auto data = memStream.detachAsData();
2463
2464 // TODO: would be cool to avoid this copy
2465 const SkString cstring(static_cast<const char*>(data->data()), data->size());
2466
2467 fWindow->setUIState(cstring.c_str());
liyuqiane5a6cd92016-05-27 08:52:52 -07002468}
2469
2470void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
liyuqian6cb70252016-06-02 12:16:25 -07002471 // For those who will add more features to handle the state change in this function:
2472 // After the change, please call updateUIState no notify the frontend (e.g., Android app).
2473 // For example, after slide change, updateUIState is called inside setupCurrentSlide;
2474 // after backend change, updateUIState is called in this function.
liyuqiane5a6cd92016-05-27 08:52:52 -07002475 if (stateName.equals(kSlideStateName)) {
Florin Malitaab99c342018-01-16 16:23:03 -05002476 for (int i = 0; i < fSlides.count(); ++i) {
2477 if (fSlides[i]->getName().equals(stateValue)) {
2478 this->setCurrentSlide(i);
2479 return;
liyuqiane5a6cd92016-05-27 08:52:52 -07002480 }
liyuqiane5a6cd92016-05-27 08:52:52 -07002481 }
Florin Malitaab99c342018-01-16 16:23:03 -05002482
2483 SkDebugf("Slide not found: %s", stateValue.c_str());
liyuqian6cb70252016-06-02 12:16:25 -07002484 } else if (stateName.equals(kBackendStateName)) {
2485 for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
2486 if (stateValue.equals(kBackendTypeStrings[i])) {
2487 if (fBackendType != i) {
2488 fBackendType = (sk_app::Window::BackendType)i;
2489 fWindow->detach();
Brian Osman70d2f432017-11-08 09:54:10 -05002490 fWindow->attach(backend_type_for_window(fBackendType));
liyuqian6cb70252016-06-02 12:16:25 -07002491 }
2492 break;
2493 }
2494 }
csmartdalton578f0642017-02-24 16:04:47 -07002495 } else if (stateName.equals(kMSAAStateName)) {
2496 DisplayParams params = fWindow->getRequestedDisplayParams();
2497 int sampleCount = atoi(stateValue.c_str());
2498 if (sampleCount != params.fMSAASampleCount) {
2499 params.fMSAASampleCount = sampleCount;
2500 fWindow->setRequestedDisplayParams(params);
2501 fWindow->inval();
Brian Salomon99a33902017-03-07 15:16:34 -05002502 this->updateTitle();
2503 this->updateUIState();
csmartdalton61cd31a2017-02-27 17:00:53 -07002504 }
2505 } else if (stateName.equals(kPathRendererStateName)) {
2506 DisplayParams params = fWindow->getRequestedDisplayParams();
2507 for (const auto& pair : gPathRendererNames) {
2508 if (pair.second == stateValue.c_str()) {
2509 if (params.fGrContextOptions.fGpuPathRenderers != pair.first) {
2510 params.fGrContextOptions.fGpuPathRenderers = pair.first;
2511 fWindow->setRequestedDisplayParams(params);
2512 fWindow->inval();
Brian Salomon99a33902017-03-07 15:16:34 -05002513 this->updateTitle();
2514 this->updateUIState();
csmartdalton61cd31a2017-02-27 17:00:53 -07002515 }
2516 break;
2517 }
csmartdalton578f0642017-02-24 16:04:47 -07002518 }
liyuqianb73c24b2016-06-03 08:47:23 -07002519 } else if (stateName.equals(kSoftkeyStateName)) {
2520 if (!stateValue.equals(kSoftkeyHint)) {
2521 fCommands.onSoftkey(stateValue);
Brian Salomon99a33902017-03-07 15:16:34 -05002522 this->updateUIState(); // This is still needed to reset the value to kSoftkeyHint
liyuqianb73c24b2016-06-03 08:47:23 -07002523 }
liyuqian2edb0f42016-07-06 14:11:32 -07002524 } else if (stateName.equals(kRefreshStateName)) {
2525 // This state is actually NOT in the UI state.
2526 // We use this to allow Android to quickly set bool fRefresh.
2527 fRefresh = stateValue.equals(kON);
liyuqiane5a6cd92016-05-27 08:52:52 -07002528 } else {
2529 SkDebugf("Unknown stateName: %s", stateName.c_str());
2530 }
2531}
Brian Osman79086b92017-02-10 13:36:16 -05002532
Hal Canaryb1f411a2019-08-29 10:39:22 -04002533bool Viewer::onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers) {
Brian Osmand67e5182017-12-08 16:46:09 -05002534 return fCommands.onKey(key, state, modifiers);
Brian Osman79086b92017-02-10 13:36:16 -05002535}
2536
Hal Canaryb1f411a2019-08-29 10:39:22 -04002537bool Viewer::onChar(SkUnichar c, skui::ModifierKey modifiers) {
Brian Osmand67e5182017-12-08 16:46:09 -05002538 if (fSlides[fCurrentSlide]->onChar(c)) {
Jim Van Verth6f449692017-02-14 15:16:46 -05002539 fWindow->inval();
2540 return true;
Brian Osman80fc07e2017-12-08 16:45:43 -05002541 } else {
2542 return fCommands.onChar(c, modifiers);
Jim Van Verth6f449692017-02-14 15:16:46 -05002543 }
Brian Osman79086b92017-02-10 13:36:16 -05002544}